aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKrzysztof Kosiński <krzysio@google.com>2023-09-21 01:42:07 +0000
committerKrzysztof Kosiński <krzysio@google.com>2023-09-21 01:43:57 +0000
commitbed1e6c7125423e8a736d8cc8a8e761e030d6c60 (patch)
tree44770570169d8ae1b87f31c4f394952c853abf16
parent66f13c500763b7070176106b40a3484063e1126b (diff)
parent6b496379b475f223f3107a17050aa43a1b3f5182 (diff)
downloadregex-re2-bed1e6c7125423e8a736d8cc8a8e761e030d6c60.tar.gz
Manually merge AOSP changes into internal main.
For some reason, the AOSP changes in this project got merged into udc-qpr-dev-plus-aosp, but did not merge into main. Bug: 279548314 Test: N/A, project not yet in the manifest. Change-Id: I51fca7db96972139693a6bddc2bbc91249f752b6
-rw-r--r--.gitignore5
-rw-r--r--.travis.yml170
-rw-r--r--AUTHORS1
-rw-r--r--Android.bp89
-rw-r--r--Android.mk61
-rw-r--r--BUILD239
-rw-r--r--CMakeLists.txt150
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--CONTRIBUTORS6
-rw-r--r--Makefile191
-rw-r--r--README27
-rw-r--r--README.android24
-rw-r--r--README.version5
-rw-r--r--WORKSPACE6
-rwxr-xr-xbenchlog/benchplot.py98
-rwxr-xr-xdoc/mksyntaxgo2
-rw-r--r--doc/syntax.html65
-rw-r--r--doc/syntax.txt275
-rwxr-xr-xkokoro/bazel.sh26
-rwxr-xr-xkokoro/cmake.sh25
-rw-r--r--kokoro/macos-bazel.cfg1
-rwxr-xr-xkokoro/macos-bazel.sh4
-rw-r--r--kokoro/macos-cmake.cfg1
-rwxr-xr-xkokoro/macos-cmake.sh4
-rw-r--r--kokoro/ubuntu-bazel.cfg1
-rwxr-xr-xkokoro/ubuntu-bazel.sh4
-rwxr-xr-xkokoro/windows-bazel.bat2
-rw-r--r--kokoro/windows-bazel.cfg1
-rwxr-xr-xkokoro/windows-cmake.bat2
-rw-r--r--kokoro/windows-cmake.cfg1
-rw-r--r--lib/codereview/codereview.cfg1
-rw-r--r--lib/codereview/codereview.py3562
-rwxr-xr-xlib/git/commit-msg.hook104
-rw-r--r--libre2.symbols5
-rw-r--r--libre2.symbols.darwin5
-rw-r--r--re2.pc10
-rw-r--r--re2/Makefile1
-rw-r--r--re2/bitmap256.h113
-rw-r--r--re2/bitstate.cc195
-rw-r--r--re2/compile.cc503
-rw-r--r--re2/dfa.cc993
-rw-r--r--re2/filtered_re2.cc59
-rw-r--r--re2/filtered_re2.h39
-rw-r--r--re2/fuzzing/re2_fuzzer.cc169
-rwxr-xr-xre2/make_perl_groups.pl34
-rwxr-xr-xre2/make_unicode_casefold.py7
-rwxr-xr-xre2/make_unicode_groups.py8
-rw-r--r--re2/mimics_pcre.cc4
-rw-r--r--re2/nfa.cc535
-rw-r--r--re2/onepass.cc295
-rw-r--r--re2/parse.cc715
-rw-r--r--re2/perl_groups.cc42
-rw-r--r--re2/prefilter.cc115
-rw-r--r--re2/prefilter.h25
-rw-r--r--re2/prefilter_tree.cc332
-rw-r--r--re2/prefilter_tree.h59
-rw-r--r--re2/prog.cc632
-rw-r--r--re2/prog.h276
-rw-r--r--re2/re2.cc712
-rw-r--r--re2/re2.h506
-rw-r--r--re2/regexp.cc185
-rw-r--r--re2/regexp.h203
-rw-r--r--re2/set.cc110
-rw-r--r--re2/set.h67
-rw-r--r--re2/simplify.cc341
-rw-r--r--re2/stringpiece.cc65
-rw-r--r--re2/stringpiece.h218
-rw-r--r--re2/testing/backtrack.cc52
-rw-r--r--re2/testing/charclass_test.cc3
-rw-r--r--re2/testing/compile_test.cc396
-rw-r--r--re2/testing/dfa_test.cc325
-rw-r--r--re2/testing/dump.cc7
-rw-r--r--re2/testing/exhaustive1_test.cc14
-rw-r--r--re2/testing/exhaustive2_test.cc15
-rw-r--r--re2/testing/exhaustive3_test.cc26
-rw-r--r--re2/testing/exhaustive_test.cc2
-rw-r--r--re2/testing/exhaustive_tester.cc32
-rw-r--r--re2/testing/exhaustive_tester.h44
-rw-r--r--re2/testing/filtered_re2_test.cc85
-rw-r--r--re2/testing/mimics_pcre_test.cc5
-rw-r--r--re2/testing/null_walker.cc4
-rw-r--r--re2/testing/parse_test.cc104
-rw-r--r--re2/testing/possible_match_test.cc45
-rw-r--r--re2/testing/random_test.cc18
-rw-r--r--re2/testing/re2_arg_test.cc58
-rw-r--r--re2/testing/re2_test.cc1161
-rw-r--r--re2/testing/regexp_benchmark.cc218
-rw-r--r--re2/testing/regexp_generator.cc74
-rw-r--r--re2/testing/regexp_generator.h47
-rw-r--r--re2/testing/regexp_test.cc27
-rw-r--r--re2/testing/required_prefix_test.cc23
-rw-r--r--re2/testing/search_test.cc34
-rw-r--r--re2/testing/set_test.cc238
-rw-r--r--re2/testing/simplify_test.cc116
-rw-r--r--re2/testing/string_generator.cc41
-rw-r--r--re2/testing/string_generator.h31
-rw-r--r--re2/testing/string_generator_test.cc15
-rw-r--r--re2/testing/tester.cc105
-rw-r--r--re2/testing/tester.h22
-rwxr-xr-xre2/testing/unicode_test.py207
-rw-r--r--re2/tostring.cc28
-rw-r--r--re2/unicode.py4
-rw-r--r--re2/unicode_casefold.cc132
-rw-r--r--re2/unicode_casefold.h29
-rw-r--r--re2/unicode_groups.cc2346
-rw-r--r--re2/unicode_groups.h33
-rw-r--r--re2/variadic_function.h346
-rw-r--r--re2/walker-inl.h18
-rw-r--r--re2_test.bzl12
-rwxr-xr-xruntests26
-rw-r--r--testinstall.cc8
-rw-r--r--util/arena.cc168
-rw-r--r--util/arena.h103
-rw-r--r--util/atomicops.h79
-rw-r--r--util/benchmark.cc42
-rw-r--r--util/benchmark.h12
-rw-r--r--util/flags.h14
-rw-r--r--util/fuzz.cc21
-rw-r--r--util/hash.cc231
-rw-r--r--util/logging.h61
-rw-r--r--util/mix.h41
-rw-r--r--util/mutex.h176
-rw-r--r--util/pcre.cc360
-rw-r--r--util/pcre.h117
-rw-r--r--util/pod_array.h55
-rw-r--r--util/random.cc34
-rw-r--r--util/random.h29
-rw-r--r--util/rune.cc12
-rw-r--r--util/sparse_array.h489
-rw-r--r--util/sparse_array_test.cc150
-rw-r--r--util/sparse_set.h317
-rw-r--r--util/stringpiece.cc87
-rw-r--r--util/stringprintf.cc78
-rw-r--r--util/strutil.cc133
-rw-r--r--util/strutil.h23
-rw-r--r--util/test.cc12
-rw-r--r--util/test.h43
-rw-r--r--util/thread.cc44
-rw-r--r--util/thread.h26
-rw-r--r--util/utf.h7
-rw-r--r--util/util.h142
-rw-r--r--util/valgrind.cc18
-rw-r--r--util/valgrind.h4517
143 files changed, 11409 insertions, 15541 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a671fe2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.pyc
+*.orig
+core
+obj/
+benchlog.*
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..0ddc4a9
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,170 @@
+language: cpp
+sudo: false
+dist: trusty
+script:
+ - make
+ - make test
+matrix:
+ include:
+
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-4.8
+ env:
+ - MATRIX_EVAL="CC=gcc-4.8 CXX=g++-4.8"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-4.9
+ env:
+ - MATRIX_EVAL="CC=gcc-4.9 CXX=g++-4.9"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-5
+ env:
+ - MATRIX_EVAL="CC=gcc-5 CXX=g++-5"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-6
+ env:
+ - MATRIX_EVAL="CC=gcc-6 CXX=g++-6"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-7
+ env:
+ - MATRIX_EVAL="CC=gcc-7 CXX=g++-7"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-8
+ env:
+ - MATRIX_EVAL="CC=gcc-8 CXX=g++-8"
+
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-precise-3.5
+ packages:
+ - clang-3.5
+ env:
+ - MATRIX_EVAL="CC=clang-3.5 CXX=clang++-3.5"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-precise-3.6
+ packages:
+ - clang-3.6
+ env:
+ - MATRIX_EVAL="CC=clang-3.6 CXX=clang++-3.6"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-precise-3.7
+ packages:
+ - clang-3.7
+ env:
+ - MATRIX_EVAL="CC=clang-3.7 CXX=clang++-3.7"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-precise-3.8
+ packages:
+ - clang-3.8
+ env:
+ - MATRIX_EVAL="CC=clang-3.8 CXX=clang++-3.8"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-precise-3.9
+ packages:
+ - clang-3.9
+ env:
+ - MATRIX_EVAL="CC=clang-3.9 CXX=clang++-3.9"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-trusty-4.0
+ packages:
+ - clang-4.0
+ env:
+ - MATRIX_EVAL="CC=clang-4.0 CXX=clang++-4.0"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-trusty-5.0
+ packages:
+ - clang-5.0
+ env:
+ - MATRIX_EVAL="CC=clang-5.0 CXX=clang++-5.0"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - sourceline: 'deb https://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main'
+ key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
+ packages:
+ - clang-6.0
+ env:
+ - MATRIX_EVAL="CC=clang-6.0 CXX=clang++-6.0"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - sourceline: 'deb https://apt.llvm.org/trusty/ llvm-toolchain-trusty-7 main'
+ key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
+ packages:
+ - clang-7
+ env:
+ - MATRIX_EVAL="CC=clang-7 CXX=clang++-7"
+ - os: linux
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - sourceline: 'deb https://apt.llvm.org/trusty/ llvm-toolchain-trusty-8 main'
+ key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
+ packages:
+ - clang-8
+ env:
+ - MATRIX_EVAL="CC=clang-8 CXX=clang++-8"
+
+before_install:
+ - eval "${MATRIX_EVAL}"
diff --git a/AUTHORS b/AUTHORS
index 3c0f928..0754006 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -9,4 +9,5 @@
# Please keep the list sorted.
Google Inc.
+Samsung Electronics
Stefano Rivera <stefano.rivera@gmail.com>
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..7a62d81
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,89 @@
+package {
+ default_applicable_licenses: ["external_regex_re2_license"],
+}
+
+license {
+ name: "external_regex_re2_license",
+ visibility: [":__subpackages__"],
+ license_kinds: ["SPDX-license-identifier-BSD-3-Clause"],
+ license_text: ["LICENSE"],
+}
+
+cc_library_static {
+ name: "libregex_re2",
+ host_supported: true,
+ vendor_available: true,
+ stl: "libc++",
+ apex_available: [
+ "//apex_available:platform",
+ ],
+ srcs: [
+ "util/rune.cc",
+ "util/strutil.cc",
+ "re2/bitstate.cc",
+ "re2/compile.cc",
+ "re2/dfa.cc",
+ "re2/filtered_re2.cc",
+ "re2/mimics_pcre.cc",
+ "re2/nfa.cc",
+ "re2/onepass.cc",
+ "re2/parse.cc",
+ "re2/perl_groups.cc",
+ "re2/prefilter.cc",
+ "re2/prefilter_tree.cc",
+ "re2/prog.cc",
+ "re2/re2.cc",
+ "re2/regexp.cc",
+ "re2/set.cc",
+ "re2/simplify.cc",
+ "re2/stringpiece.cc",
+ "re2/tostring.cc",
+ "re2/unicode_casefold.cc",
+ "re2/unicode_groups.cc",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ "-Wno-missing-field-initializers",
+ ],
+ export_include_dirs: ["."],
+ visibility: [
+ "//external/grpc-grpc:__subpackages__",
+ "//external/kythe:__subpackages__",
+ ],
+}
+
+cc_test {
+ name: "regex_re2_test",
+ host_supported: true,
+ srcs: [
+ "re2/testing/backtrack.cc",
+ "re2/testing/charclass_test.cc",
+ "re2/testing/compile_test.cc",
+ "re2/testing/dump.cc",
+ "re2/testing/filtered_re2_test.cc",
+ "re2/testing/mimics_pcre_test.cc",
+ "re2/testing/null_walker.cc",
+ "re2/testing/parse_test.cc",
+ "re2/testing/possible_match_test.cc",
+ "re2/testing/re2_arg_test.cc",
+ "re2/testing/re2_test.cc",
+ "re2/testing/regexp_generator.cc",
+ "re2/testing/regexp_test.cc",
+ "re2/testing/required_prefix_test.cc",
+ "re2/testing/search_test.cc",
+ "re2/testing/set_test.cc",
+ "re2/testing/simplify_test.cc",
+ "re2/testing/string_generator.cc",
+ "re2/testing/string_generator_test.cc",
+ "re2/testing/tester.cc",
+ "util/pcre.cc",
+ "util/test.cc",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ "-Wno-missing-field-initializers",
+ ],
+ static_libs: [
+ "libregex_re2",
+ ],
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index d9b04eb..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-#
-# Copyright 2012 Google Inc. All Rights Reserved.
-# Author: idh@google.com (Ian Hodson)
-#
-# Android makefile for the re2 regexp library.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-regexp_re2_files := \
- util/arena.cc \
- util/hash.cc \
- util/rune.cc \
- util/stringpiece.cc \
- util/stringprintf.cc \
- util/strutil.cc \
- util/valgrind.cc \
- re2/bitstate.cc \
- re2/compile.cc \
- re2/dfa.cc \
- re2/filtered_re2.cc \
- re2/mimics_pcre.cc \
- re2/nfa.cc \
- re2/onepass.cc \
- re2/parse.cc \
- re2/perl_groups.cc \
- re2/prefilter.cc \
- re2/prefilter_tree.cc \
- re2/prog.cc \
- re2/re2.cc \
- re2/regexp.cc \
- re2/set.cc \
- re2/simplify.cc \
- re2/tostring.cc \
- re2/unicode_casefold.cc \
- re2/unicode_groups.cc
-
-MY_RE2_WARNING_FLAGS := \
- -Wall -Werror \
- -Wno-missing-field-initializers \
- -Wno-sign-compare \
- -Wno-tautological-undefined-compare \
- -Wno-unused-local-typedef \
- -Wno-unused-parameter \
-
-# Stlport version
-# =======================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libregex-re2
-LOCAL_MODULE_TAGS := optional
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/re2 \
- external/stlport
-LOCAL_CFLAGS := $(MY_RE2_WARNING_FLAGS)
-
-LOCAL_SRC_FILES := $(regexp_re2_files)
-LOCAL_NDK_STL_VARIANT := stlport_static
-LOCAL_SDK_VERSION := 14
-include $(BUILD_STATIC_LIBRARY)
-
-MY_RE2_WARNING_FLAGS :=
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..30ce320
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,239 @@
+# Copyright 2009 The RE2 Authors. All Rights Reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Bazel (http://bazel.io/) BUILD file for RE2.
+
+licenses(["notice"])
+
+exports_files(["LICENSE"])
+
+config_setting(
+ name = "darwin",
+ values = {"cpu": "darwin"},
+)
+
+config_setting(
+ name = "windows",
+ values = {"cpu": "x64_windows"},
+)
+
+config_setting(
+ name = "windows_msvc",
+ values = {"cpu": "x64_windows_msvc"},
+)
+
+cc_library(
+ name = "re2",
+ srcs = [
+ "re2/bitmap256.h",
+ "re2/bitstate.cc",
+ "re2/compile.cc",
+ "re2/dfa.cc",
+ "re2/filtered_re2.cc",
+ "re2/mimics_pcre.cc",
+ "re2/nfa.cc",
+ "re2/onepass.cc",
+ "re2/parse.cc",
+ "re2/perl_groups.cc",
+ "re2/prefilter.cc",
+ "re2/prefilter.h",
+ "re2/prefilter_tree.cc",
+ "re2/prefilter_tree.h",
+ "re2/prog.cc",
+ "re2/prog.h",
+ "re2/re2.cc",
+ "re2/regexp.cc",
+ "re2/regexp.h",
+ "re2/set.cc",
+ "re2/simplify.cc",
+ "re2/stringpiece.cc",
+ "re2/tostring.cc",
+ "re2/unicode_casefold.cc",
+ "re2/unicode_casefold.h",
+ "re2/unicode_groups.cc",
+ "re2/unicode_groups.h",
+ "re2/walker-inl.h",
+ "util/flags.h",
+ "util/logging.h",
+ "util/mix.h",
+ "util/mutex.h",
+ "util/pod_array.h",
+ "util/rune.cc",
+ "util/sparse_array.h",
+ "util/sparse_set.h",
+ "util/strutil.cc",
+ "util/strutil.h",
+ "util/utf.h",
+ "util/util.h",
+ ],
+ hdrs = [
+ "re2/filtered_re2.h",
+ "re2/re2.h",
+ "re2/set.h",
+ "re2/stringpiece.h",
+ ],
+ copts = select({
+ ":windows": [],
+ ":windows_msvc": [],
+ "//conditions:default": ["-pthread"],
+ }),
+ linkopts = select({
+ # Darwin doesn't need `-pthread' when linking and it appears that
+ # older versions of Clang will warn about the unused command line
+ # argument, so just don't pass it.
+ ":darwin": [],
+ ":windows": [],
+ ":windows_msvc": [],
+ "//conditions:default": ["-pthread"],
+ }),
+ visibility = ["//visibility:public"],
+)
+
+cc_library(
+ name = "testing",
+ testonly = 1,
+ srcs = [
+ "re2/testing/backtrack.cc",
+ "re2/testing/dump.cc",
+ "re2/testing/exhaustive_tester.cc",
+ "re2/testing/null_walker.cc",
+ "re2/testing/regexp_generator.cc",
+ "re2/testing/string_generator.cc",
+ "re2/testing/tester.cc",
+ "util/pcre.cc",
+ ],
+ hdrs = [
+ "re2/testing/exhaustive_tester.h",
+ "re2/testing/regexp_generator.h",
+ "re2/testing/string_generator.h",
+ "re2/testing/tester.h",
+ "util/benchmark.h",
+ "util/pcre.h",
+ "util/test.h",
+ ],
+ deps = [":re2"],
+)
+
+cc_library(
+ name = "test",
+ testonly = 1,
+ srcs = ["util/test.cc"],
+ deps = [":testing"],
+)
+
+load(":re2_test.bzl", "re2_test")
+
+re2_test(
+ "charclass_test",
+ size = "small",
+)
+
+re2_test(
+ "compile_test",
+ size = "small",
+)
+
+re2_test(
+ "filtered_re2_test",
+ size = "small",
+)
+
+re2_test(
+ "mimics_pcre_test",
+ size = "small",
+)
+
+re2_test(
+ "parse_test",
+ size = "small",
+)
+
+re2_test(
+ "possible_match_test",
+ size = "small",
+)
+
+re2_test(
+ "re2_arg_test",
+ size = "small",
+)
+
+re2_test(
+ "re2_test",
+ size = "small",
+)
+
+re2_test(
+ "regexp_test",
+ size = "small",
+)
+
+re2_test(
+ "required_prefix_test",
+ size = "small",
+)
+
+re2_test(
+ "search_test",
+ size = "small",
+)
+
+re2_test(
+ "set_test",
+ size = "small",
+)
+
+re2_test(
+ "simplify_test",
+ size = "small",
+)
+
+re2_test(
+ "string_generator_test",
+ size = "small",
+)
+
+re2_test(
+ "dfa_test",
+ size = "large",
+)
+
+re2_test(
+ "exhaustive1_test",
+ size = "large",
+)
+
+re2_test(
+ "exhaustive2_test",
+ size = "large",
+)
+
+re2_test(
+ "exhaustive3_test",
+ size = "large",
+)
+
+re2_test(
+ "exhaustive_test",
+ size = "large",
+)
+
+re2_test(
+ "random_test",
+ size = "large",
+)
+
+cc_library(
+ name = "benchmark",
+ testonly = 1,
+ srcs = ["util/benchmark.cc"],
+ deps = [":testing"],
+)
+
+cc_binary(
+ name = "regexp_benchmark",
+ testonly = 1,
+ srcs = ["re2/testing/regexp_benchmark.cc"],
+ deps = [":benchmark"],
+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..f87c4ff
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,150 @@
+# Copyright 2015 The RE2 Authors. All Rights Reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Old enough to support Ubuntu Trusty.
+cmake_minimum_required(VERSION 2.8.12)
+
+if(POLICY CMP0048)
+ cmake_policy(SET CMP0048 NEW)
+endif()
+
+project(RE2 CXX)
+include(CTest)
+
+option(BUILD_SHARED_LIBS "build shared libraries" OFF)
+option(USEPCRE "use PCRE in tests and benchmarks" OFF)
+
+# CMake seems to have no way to enable/disable testing per subproject,
+# so we provide an option similar to BUILD_TESTING, but just for RE2.
+option(RE2_BUILD_TESTING "enable testing for RE2" ON)
+
+set(EXTRA_TARGET_LINK_LIBRARIES)
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
+ if(MSVC_VERSION LESS 1900)
+ message(FATAL_ERROR "you need Visual Studio 2015 or later")
+ endif()
+ if(BUILD_SHARED_LIBS)
+ # See http://www.kitware.com/blog/home/post/939 for details.
+ cmake_minimum_required(VERSION 3.4)
+ set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
+ endif()
+ # CMake defaults to /W3, but some users like /W4 (or /Wall) and /WX,
+ # so we disable various warnings that aren't particularly helpful.
+ add_compile_options(/wd4100 /wd4201 /wd4456 /wd4457 /wd4702 /wd4815)
+ # Without a byte order mark (BOM), Visual Studio assumes that the source
+ # file is encoded using the current user code page, so we specify UTF-8.
+ add_compile_options(/utf-8)
+elseif(CYGWIN OR MINGW)
+ # See https://stackoverflow.com/questions/38139631 for details.
+ add_compile_options(-std=gnu++11)
+elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
+ add_compile_options(-std=c++11)
+endif()
+
+if(WIN32)
+ add_definitions(-DUNICODE -D_UNICODE -DSTRICT -DNOMINMAX)
+ add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
+elseif(UNIX)
+ add_compile_options(-pthread)
+ list(APPEND EXTRA_TARGET_LINK_LIBRARIES -pthread)
+endif()
+
+if(USEPCRE)
+ add_definitions(-DUSEPCRE)
+ list(APPEND EXTRA_TARGET_LINK_LIBRARIES pcre)
+endif()
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+set(RE2_SOURCES
+ re2/bitstate.cc
+ re2/compile.cc
+ re2/dfa.cc
+ re2/filtered_re2.cc
+ re2/mimics_pcre.cc
+ re2/nfa.cc
+ re2/onepass.cc
+ re2/parse.cc
+ re2/perl_groups.cc
+ re2/prefilter.cc
+ re2/prefilter_tree.cc
+ re2/prog.cc
+ re2/re2.cc
+ re2/regexp.cc
+ re2/set.cc
+ re2/simplify.cc
+ re2/stringpiece.cc
+ re2/tostring.cc
+ re2/unicode_casefold.cc
+ re2/unicode_groups.cc
+ util/rune.cc
+ util/strutil.cc
+ )
+
+add_library(re2 ${RE2_SOURCES})
+
+if(RE2_BUILD_TESTING)
+ set(TESTING_SOURCES
+ re2/testing/backtrack.cc
+ re2/testing/dump.cc
+ re2/testing/exhaustive_tester.cc
+ re2/testing/null_walker.cc
+ re2/testing/regexp_generator.cc
+ re2/testing/string_generator.cc
+ re2/testing/tester.cc
+ util/pcre.cc
+ )
+
+ add_library(testing STATIC ${TESTING_SOURCES})
+
+ set(TEST_TARGETS
+ charclass_test
+ compile_test
+ filtered_re2_test
+ mimics_pcre_test
+ parse_test
+ possible_match_test
+ re2_test
+ re2_arg_test
+ regexp_test
+ required_prefix_test
+ search_test
+ set_test
+ simplify_test
+ string_generator_test
+
+ dfa_test
+ exhaustive1_test
+ exhaustive2_test
+ exhaustive3_test
+ exhaustive_test
+ random_test
+ )
+
+ set(BENCHMARK_TARGETS
+ regexp_benchmark
+ )
+
+ foreach(target ${TEST_TARGETS})
+ add_executable(${target} re2/testing/${target}.cc util/test.cc)
+ target_link_libraries(${target} testing re2 ${EXTRA_TARGET_LINK_LIBRARIES})
+ add_test(NAME ${target} COMMAND ${target})
+ endforeach(target)
+
+ foreach(target ${BENCHMARK_TARGETS})
+ add_executable(${target} re2/testing/${target}.cc util/benchmark.cc)
+ target_link_libraries(${target} testing re2 ${EXTRA_TARGET_LINK_LIBRARIES})
+ endforeach(target)
+endif()
+
+set(RE2_HEADERS
+ re2/filtered_re2.h
+ re2/re2.h
+ re2/set.h
+ re2/stringpiece.h
+ )
+
+install(FILES ${RE2_HEADERS} DESTINATION include/re2)
+install(TARGETS re2 ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..3af2b0a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,2 @@
+RE2 uses Gerrit instead of GitHub pull requests.
+See the [Contributing](https://github.com/google/re2/wiki/Contribute) wiki page.
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 7b44e04..1a1c848 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -27,9 +27,15 @@
# Please keep the list sorted.
Dominic Battré <battre@chromium.org>
+Doug Kwan <dougkwan@google.com>
+Dmitriy Vyukov <dvyukov@google.com>
John Millikin <jmillikin@gmail.com>
+Mike Nazarewicz <mpn@google.com>
+Nico Weber <thakis@chromium.org>
+Pawel Hajdan <phajdan.jr@gmail.com>
Rob Pike <r@google.com>
Russ Cox <rsc@swtch.com>
Sanjay Ghemawat <sanjay@google.com>
Stefano Rivera <stefano.rivera@gmail.com>
Srinivasan Venkatachary <vsri@google.com>
+Viatcheslav Ostapenko <sl.ostapenko@samsung.com>
diff --git a/Makefile b/Makefile
index 4ded8ec..f001f06 100644
--- a/Makefile
+++ b/Makefile
@@ -2,33 +2,46 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-all: obj/libre2.a obj/so/libre2.so
+# To build against ICU for full Unicode properties support,
+# uncomment the next two lines:
+# CCICU=$(shell pkg-config icu-uc --cflags) -DRE2_USE_ICU
+# LDICU=$(shell pkg-config icu-uc --libs)
-# to build against PCRE for testing or benchmarking,
-# uncomment the next two lines
+# To build against PCRE for testing or benchmarking,
+# uncomment the next two lines:
# CCPCRE=-I/usr/local/include -DUSEPCRE
# LDPCRE=-L/usr/local/lib -lpcre
-CXX=g++
-CXXFLAGS=-Wall -O3 -g -pthread # can override
-RE2_CXXFLAGS=-Wno-sign-compare -c -I. $(CCPCRE) # required
-LDFLAGS=-pthread
-AR=ar
-ARFLAGS=rsc
-NM=nm
-NMFLAGS=-p
+CXX?=g++
+# can override
+CXXFLAGS?=-O3 -g
+LDFLAGS?=
+# required
+RE2_CXXFLAGS?=-std=c++11 -pthread -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -I. $(CCICU) $(CCPCRE)
+RE2_LDFLAGS?=-pthread $(LDICU) $(LDPCRE)
+AR?=ar
+ARFLAGS?=rsc
+NM?=nm
+NMFLAGS?=-p
# Variables mandated by GNU, the arbiter of all good taste on the internet.
# http://www.gnu.org/prep/standards/standards.html
prefix=/usr/local
exec_prefix=$(prefix)
-bindir=$(exec_prefix)/bin
includedir=$(prefix)/include
libdir=$(exec_prefix)/lib
INSTALL=install
-INSTALL_PROGRAM=$(INSTALL)
INSTALL_DATA=$(INSTALL) -m 644
+# Work around the weirdness of sed(1) on Darwin. :/
+ifeq ($(shell uname),Darwin)
+SED_INPLACE=sed -i ''
+else ifeq ($(shell uname),SunOS)
+SED_INPLACE=sed -i
+else
+SED_INPLACE=sed -i
+endif
+
# ABI version
# http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
SONAME=0
@@ -37,34 +50,47 @@ SONAME=0
# access for Unicode data), uncomment the following line:
# REBUILD_TABLES=1
+# The SunOS linker does not support wildcards. :(
ifeq ($(shell uname),Darwin)
-MAKE_SHARED_LIBRARY=$(CXX) -dynamiclib $(LDFLAGS) -exported_symbols_list libre2.symbols.darwin
+SOEXT=dylib
+SOEXTVER=$(SONAME).$(SOEXT)
+SOEXTVER00=$(SONAME).0.0.$(SOEXT)
+MAKE_SHARED_LIBRARY=$(CXX) -dynamiclib -Wl,-install_name,$(libdir)/libre2.$(SOEXTVER),-exported_symbols_list,libre2.symbols.darwin $(RE2_LDFLAGS) $(LDFLAGS)
+else ifeq ($(shell uname),SunOS)
+SOEXT=so
+SOEXTVER=$(SOEXT).$(SONAME)
+SOEXTVER00=$(SOEXT).$(SONAME).0.0
+MAKE_SHARED_LIBRARY=$(CXX) -shared -Wl,-soname,libre2.$(SOEXTVER) $(RE2_LDFLAGS) $(LDFLAGS)
else
-MAKE_SHARED_LIBRARY=$(CXX) -shared -Wl,-soname,libre2.so.$(SONAME),--version-script=libre2.symbols $(LDFLAGS)
+SOEXT=so
+SOEXTVER=$(SOEXT).$(SONAME)
+SOEXTVER00=$(SOEXT).$(SONAME).0.0
+MAKE_SHARED_LIBRARY=$(CXX) -shared -Wl,-soname,libre2.$(SOEXTVER),--version-script,libre2.symbols $(RE2_LDFLAGS) $(LDFLAGS)
endif
+all: obj/libre2.a obj/so/libre2.$(SOEXT)
+
INSTALL_HFILES=\
re2/filtered_re2.h\
re2/re2.h\
re2/set.h\
re2/stringpiece.h\
- re2/variadic_function.h\
HFILES=\
- util/arena.h\
- util/atomicops.h\
util/benchmark.h\
util/flags.h\
util/logging.h\
+ util/mix.h\
util/mutex.h\
util/pcre.h\
- util/random.h\
+ util/pod_array.h\
util/sparse_array.h\
util/sparse_set.h\
+ util/strutil.h\
util/test.h\
util/utf.h\
util/util.h\
- util/valgrind.h\
+ re2/bitmap256.h\
re2/filtered_re2.h\
re2/prefilter.h\
re2/prefilter_tree.h\
@@ -79,17 +105,11 @@ HFILES=\
re2/testing/tester.h\
re2/unicode_casefold.h\
re2/unicode_groups.h\
- re2/variadic_function.h\
re2/walker-inl.h\
OFILES=\
- obj/util/arena.o\
- obj/util/hash.o\
obj/util/rune.o\
- obj/util/stringpiece.o\
- obj/util/stringprintf.o\
obj/util/strutil.o\
- obj/util/valgrind.o\
obj/re2/bitstate.o\
obj/re2/compile.o\
obj/re2/dfa.o\
@@ -106,14 +126,13 @@ OFILES=\
obj/re2/regexp.o\
obj/re2/set.o\
obj/re2/simplify.o\
+ obj/re2/stringpiece.o\
obj/re2/tostring.o\
obj/re2/unicode_casefold.o\
obj/re2/unicode_groups.o\
TESTOFILES=\
obj/util/pcre.o\
- obj/util/random.o\
- obj/util/thread.o\
obj/re2/testing/backtrack.o\
obj/re2/testing/dump.o\
obj/re2/testing/exhaustive_tester.o\
@@ -147,7 +166,7 @@ BIGTESTS=\
obj/test/random_test\
SOFILES=$(patsubst obj/%,obj/so/%,$(OFILES))
-STESTOFILES=$(patsubst obj/%,obj/so/%,$(TESTOFILES))
+# We use TESTOFILES for testing the shared lib, only it is built differently.
STESTS=$(patsubst obj/%,obj/so/%,$(TESTS))
SBIGTESTS=$(patsubst obj/%,obj/so/%,$(BIGTESTS))
@@ -158,15 +177,15 @@ DBIGTESTS=$(patsubst obj/%,obj/dbg/%,$(BIGTESTS))
obj/%.o: %.cc $(HFILES)
@mkdir -p $$(dirname $@)
- $(CXX) -o $@ $(CPPFLAGS) $(CXXFLAGS) $(RE2_CXXFLAGS) -DNDEBUG $*.cc
+ $(CXX) -c -o $@ $(CPPFLAGS) $(RE2_CXXFLAGS) $(CXXFLAGS) -DNDEBUG $*.cc
obj/dbg/%.o: %.cc $(HFILES)
@mkdir -p $$(dirname $@)
- $(CXX) -o $@ -fPIC $(CPPFLAGS) $(CXXFLAGS) $(RE2_CXXFLAGS) $*.cc
+ $(CXX) -c -o $@ $(CPPFLAGS) $(RE2_CXXFLAGS) $(CXXFLAGS) $*.cc
obj/so/%.o: %.cc $(HFILES)
@mkdir -p $$(dirname $@)
- $(CXX) -o $@ -fPIC $(CPPFLAGS) $(CXXFLAGS) $(RE2_CXXFLAGS) -DNDEBUG $*.cc
+ $(CXX) -c -o $@ -fPIC $(CPPFLAGS) $(RE2_CXXFLAGS) $(CXXFLAGS) -DNDEBUG $*.cc
obj/libre2.a: $(OFILES)
@mkdir -p obj
@@ -176,26 +195,35 @@ obj/dbg/libre2.a: $(DOFILES)
@mkdir -p obj/dbg
$(AR) $(ARFLAGS) obj/dbg/libre2.a $(DOFILES)
-obj/so/libre2.so: $(SOFILES)
+obj/so/libre2.$(SOEXT): $(SOFILES)
@mkdir -p obj/so
- $(MAKE_SHARED_LIBRARY) -o $@.$(SONAME) $(SOFILES)
- ln -sf libre2.so.$(SONAME) $@
-
-obj/test/%: obj/libre2.a obj/re2/testing/%.o $(TESTOFILES) obj/util/test.o
- @mkdir -p obj/test
- $(CXX) -o $@ obj/re2/testing/$*.o $(TESTOFILES) obj/util/test.o obj/libre2.a $(LDFLAGS) $(LDPCRE)
+ $(MAKE_SHARED_LIBRARY) -o obj/so/libre2.$(SOEXTVER) $(SOFILES)
+ ln -sf libre2.$(SOEXTVER) $@
obj/dbg/test/%: obj/dbg/libre2.a obj/dbg/re2/testing/%.o $(DTESTOFILES) obj/dbg/util/test.o
@mkdir -p obj/dbg/test
- $(CXX) -o $@ obj/dbg/re2/testing/$*.o $(DTESTOFILES) obj/dbg/util/test.o obj/dbg/libre2.a $(LDFLAGS) $(LDPCRE)
+ $(CXX) -o $@ obj/dbg/re2/testing/$*.o $(DTESTOFILES) obj/dbg/util/test.o obj/dbg/libre2.a $(RE2_LDFLAGS) $(LDFLAGS)
-obj/so/test/%: obj/so/libre2.so obj/libre2.a obj/so/re2/testing/%.o $(STESTOFILES) obj/so/util/test.o
+obj/test/%: obj/libre2.a obj/re2/testing/%.o $(TESTOFILES) obj/util/test.o
+ @mkdir -p obj/test
+ $(CXX) -o $@ obj/re2/testing/$*.o $(TESTOFILES) obj/util/test.o obj/libre2.a $(RE2_LDFLAGS) $(LDFLAGS)
+
+# Test the shared lib, falling back to the static lib for private symbols
+obj/so/test/%: obj/so/libre2.$(SOEXT) obj/libre2.a obj/re2/testing/%.o $(TESTOFILES) obj/util/test.o
@mkdir -p obj/so/test
- $(CXX) -o $@ obj/so/re2/testing/$*.o $(STESTOFILES) obj/so/util/test.o -Lobj/so -lre2 obj/libre2.a $(LDFLAGS) $(LDPCRE)
+ $(CXX) -o $@ obj/re2/testing/$*.o $(TESTOFILES) obj/util/test.o -Lobj/so -lre2 obj/libre2.a $(RE2_LDFLAGS) $(LDFLAGS)
obj/test/regexp_benchmark: obj/libre2.a obj/re2/testing/regexp_benchmark.o $(TESTOFILES) obj/util/benchmark.o
@mkdir -p obj/test
- $(CXX) -o $@ obj/re2/testing/regexp_benchmark.o $(TESTOFILES) obj/util/benchmark.o obj/libre2.a $(LDFLAGS) $(LDPCRE)
+ $(CXX) -o $@ obj/re2/testing/regexp_benchmark.o $(TESTOFILES) obj/util/benchmark.o obj/libre2.a $(RE2_LDFLAGS) $(LDFLAGS)
+
+# re2_fuzzer is a target for fuzzers like libFuzzer and AFL. This fake fuzzing
+# is simply a way to check that the target builds and then to run it against a
+# fixed set of inputs. To perform real fuzzing, refer to the documentation for
+# libFuzzer (llvm.org/docs/LibFuzzer.html) and AFL (lcamtuf.coredump.cx/afl/).
+obj/test/re2_fuzzer: obj/libre2.a obj/re2/fuzzing/re2_fuzzer.o obj/util/fuzz.o
+ @mkdir -p obj/test
+ $(CXX) -o $@ obj/re2/fuzzing/re2_fuzzer.o obj/util/fuzz.o obj/libre2.a $(RE2_LDFLAGS) $(LDFLAGS)
ifdef REBUILD_TABLES
re2/perl_groups.cc: re2/make_perl_groups.pl
@@ -203,6 +231,8 @@ re2/perl_groups.cc: re2/make_perl_groups.pl
re2/unicode_%.cc: re2/make_unicode_%.py
python $< > $@
+
+.PRECIOUS: re2/perl_groups.cc re2/unicode_casefold.cc re2/unicode_groups.cc
endif
distclean: clean
@@ -217,22 +247,13 @@ testofiles: $(TESTOFILES)
test: $(DTESTS) $(TESTS) $(STESTS) debug-test static-test shared-test
debug-test: $(DTESTS)
- @echo
- @echo Running debug binary tests.
- @echo
@./runtests $(DTESTS)
static-test: $(TESTS)
- @echo
- @echo Running static binary tests.
- @echo
@./runtests $(TESTS)
shared-test: $(STESTS)
- @echo
- @echo Running dynamic binary tests.
- @echo
- @LD_LIBRARY_PATH=obj/so:$(LD_LIBRARY_PATH) ./runtests $(STESTS)
+ @./runtests -shared-library-path obj/so $(STESTS)
debug-bigtest: $(DTESTS) $(DBIGTESTS)
@./runtests $(DTESTS) $(DBIGTESTS)
@@ -241,27 +262,59 @@ static-bigtest: $(TESTS) $(BIGTESTS)
@./runtests $(TESTS) $(BIGTESTS)
shared-bigtest: $(STESTS) $(SBIGTESTS)
- @LD_LIBRARY_PATH=obj/so:$(LD_LIBRARY_PATH) ./runtests $(STESTS) $(SBIGTESTS)
+ @./runtests -shared-library-path obj/so $(STESTS) $(SBIGTESTS)
benchmark: obj/test/regexp_benchmark
-install: obj/libre2.a obj/so/libre2.so
- mkdir -p $(DESTDIR)$(includedir)/re2 $(DESTDIR)$(libdir)
+fuzz: obj/test/re2_fuzzer
+
+install: obj/libre2.a obj/so/libre2.$(SOEXT)
+ mkdir -p $(DESTDIR)$(includedir)/re2 $(DESTDIR)$(libdir)/pkgconfig
$(INSTALL_DATA) $(INSTALL_HFILES) $(DESTDIR)$(includedir)/re2
$(INSTALL) obj/libre2.a $(DESTDIR)$(libdir)/libre2.a
- $(INSTALL) obj/so/libre2.so $(DESTDIR)$(libdir)/libre2.so.$(SONAME).0.0
- ln -sf libre2.so.$(SONAME).0.0 $(DESTDIR)$(libdir)/libre2.so.$(SONAME)
- ln -sf libre2.so.$(SONAME).0.0 $(DESTDIR)$(libdir)/libre2.so
+ $(INSTALL) obj/so/libre2.$(SOEXT) $(DESTDIR)$(libdir)/libre2.$(SOEXTVER00)
+ ln -sf libre2.$(SOEXTVER00) $(DESTDIR)$(libdir)/libre2.$(SOEXTVER)
+ ln -sf libre2.$(SOEXTVER00) $(DESTDIR)$(libdir)/libre2.$(SOEXT)
+ $(INSTALL_DATA) re2.pc $(DESTDIR)$(libdir)/pkgconfig/re2.pc
+ $(SED_INPLACE) -e "s#@prefix@#${prefix}#" $(DESTDIR)$(libdir)/pkgconfig/re2.pc
+ $(SED_INPLACE) -e "s#@exec_prefix@#${exec_prefix}#" $(DESTDIR)$(libdir)/pkgconfig/re2.pc
+ $(SED_INPLACE) -e "s#@includedir@#${includedir}#" $(DESTDIR)$(libdir)/pkgconfig/re2.pc
+ $(SED_INPLACE) -e "s#@libdir@#${libdir}#" $(DESTDIR)$(libdir)/pkgconfig/re2.pc
+
+testinstall: static-testinstall shared-testinstall
+ @echo
+ @echo Install tests passed.
+ @echo
+
+static-testinstall: CXXFLAGS:=-std=c++11 -pthread -I$(DESTDIR)$(includedir) $(CXXFLAGS)
+static-testinstall: LDFLAGS:=-pthread -L$(DESTDIR)$(libdir) -l:libre2.a $(LDICU) $(LDFLAGS)
+static-testinstall:
+ @mkdir -p obj
+ @cp testinstall.cc obj
+ifeq ($(shell uname),Darwin)
+ @echo Skipping test for libre2.a on Darwin.
+else ifeq ($(shell uname),SunOS)
+ @echo Skipping test for libre2.a on SunOS.
+else
+ (cd obj && $(CXX) testinstall.cc -o testinstall $(CXXFLAGS) $(LDFLAGS))
+ obj/testinstall
+endif
-testinstall:
+shared-testinstall: CXXFLAGS:=-std=c++11 -pthread -I$(DESTDIR)$(includedir) $(CXXFLAGS)
+shared-testinstall: LDFLAGS:=-pthread -L$(DESTDIR)$(libdir) -lre2 $(LDICU) $(LDFLAGS)
+shared-testinstall:
@mkdir -p obj
- cp testinstall.cc obj
- (cd obj && $(CXX) -I$(DESTDIR)$(includedir) -L$(DESTDIR)$(libdir) testinstall.cc -lre2 -pthread -o testinstall)
- LD_LIBRARY_PATH=$(DESTDIR)$(libdir) obj/testinstall
+ @cp testinstall.cc obj
+ (cd obj && $(CXX) testinstall.cc -o testinstall $(CXXFLAGS) $(LDFLAGS))
+ifeq ($(shell uname),Darwin)
+ DYLD_LIBRARY_PATH="$(DESTDIR)$(libdir):$(DYLD_LIBRARY_PATH)" obj/testinstall
+else
+ LD_LIBRARY_PATH="$(DESTDIR)$(libdir):$(LD_LIBRARY_PATH)" obj/testinstall
+endif
benchlog: obj/test/regexp_benchmark
(echo '==BENCHMARK==' `hostname` `date`; \
- (uname -a; $(CXX) --version; hg identify; file obj/test/regexp_benchmark) | sed 's/^/# /'; \
+ (uname -a; $(CXX) --version; git rev-parse --short HEAD; file obj/test/regexp_benchmark) | sed 's/^/# /'; \
echo; \
./obj/test/regexp_benchmark 'PCRE|RE2') | tee -a benchlog.$$(hostname | sed 's/\..*//')
@@ -273,8 +326,9 @@ benchlog: obj/test/regexp_benchmark
obj/test/% obj/so/test/% obj/dbg/test/%
log:
- make clean
- make CXXFLAGS="$(CXXFLAGS) -DLOGGING=1" obj/test/exhaustive{,1,2,3}_test
+ $(MAKE) clean
+ $(MAKE) CXXFLAGS="$(CXXFLAGS) -DLOGGING=1" \
+ $(filter obj/test/exhaustive%_test,$(BIGTESTS))
echo '#' RE2 exhaustive tests built by make log >re2-exhaustive.txt
echo '#' $$(date) >>re2-exhaustive.txt
obj/test/exhaustive_test |grep -v '^PASS$$' >>re2-exhaustive.txt
@@ -282,7 +336,10 @@ log:
obj/test/exhaustive2_test |grep -v '^PASS$$' >>re2-exhaustive.txt
obj/test/exhaustive3_test |grep -v '^PASS$$' >>re2-exhaustive.txt
- make CXXFLAGS="$(CXXFLAGS) -DLOGGING=1" obj/test/search_test
+ $(MAKE) CXXFLAGS="$(CXXFLAGS) -DLOGGING=1" obj/test/search_test
echo '#' RE2 basic search tests built by make $@ >re2-search.txt
echo '#' $$(date) >>re2-search.txt
obj/test/search_test |grep -v '^PASS$$' >>re2-search.txt
+
+x: x.cc obj/libre2.a
+ g++ -I. -o x x.cc obj/libre2.a
diff --git a/README b/README
index 57b3181..d1ef431 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
This is the source code repository for RE2, a regular expression library.
For documentation about how to install and use RE2,
-visit http://code.google.com/p/re2/.
+visit https://github.com/google/re2/.
The short version is:
@@ -10,10 +10,29 @@ make test
make install
make testinstall
+There is a fair amount of documentation (including code snippets) in
+the re2.h header file.
+
+More information can be found on the wiki:
+https://github.com/google/re2/wiki
+
+Issue tracker:
+https://github.com/google/re2/issues
+
+Mailing list:
+https://groups.google.com/group/re2-dev
+
Unless otherwise noted, the RE2 source files are distributed
under the BSD-style license found in the LICENSE file.
RE2's native language is C++.
-An Inferno wrapper is at http://code.google.com/p/inferno-re2/.
-A Python wrapper is at http://github.com/facebook/pyre2/.
-A Ruby wrapper is at http://github.com/axic/rre2/.
+
+A C wrapper is at https://github.com/marcomaggi/cre2/.
+An Erlang wrapper is at https://github.com/dukesoferl/re2/ and on Hex (hex.pm).
+An Inferno wrapper is at https://github.com/powerman/inferno-re2/.
+A Node.js wrapper is at https://github.com/uhop/node-re2/ and on NPM (npmjs.com).
+An OCaml wrapper is at https://github.com/janestreet/re2/ and on OPAM (opam.ocaml.org).
+A Perl wrapper is at https://github.com/dgl/re-engine-RE2/ and on CPAN (cpan.org).
+A Python wrapper is at https://github.com/facebook/pyre2/ and on PyPI (pypi.org).
+An R wrapper is at https://github.com/qinwf/re2r/ and on CRAN (cran.r-project.org).
+A Ruby wrapper is at https://github.com/mudge/re2/ and on RubyGems (rubygems.org).
diff --git a/README.android b/README.android
index 4911c3c..9d6b9ec 100644
--- a/README.android
+++ b/README.android
@@ -1,28 +1,10 @@
Code obtained from
------------------
-https://re2.googlecode.com/files/re2-20130115.tgz
+https://github.com/google/re2
Version
-------
-re2-20130115.tgz
-
-Changes required to build using stlport on Android as follows (full diff)
--------------------------------------------------------------------------
-util/util.h:
-
-44,53c44
-< #if defined(ANDROID)
-<
-< #if defined(_STLPORT_VERSION)
-< #include <unordered_set> // using stlport
-< #else
-< #include <tr1/unordered_set> // using gnustl
-< #endif
-< using std::tr1::unordered_set;
-<
-< #elif defined(__GNUC__) && !defined(USE_CXX0X)
----
-> #if defined(__GNUC__) && !defined(USE_CXX0X)
-
+Commit 79ef3b2d31f06493f687ef9e947d9632bad54b9b
+dated 2019-02-13
diff --git a/README.version b/README.version
index b48b8b7..6332133 100644
--- a/README.version
+++ b/README.version
@@ -1,3 +1,2 @@
-URL: https://re2.googlecode.com/files/re2-20130115.tgz
-Version: 20130115
-BugComponent: 14890
+URL: https://github.com/google/re2
+Version: 79ef3b2d31f06493f687ef9e947d9632bad54b9b
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..de481fe
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,6 @@
+# Copyright 2009 The RE2 Authors. All Rights Reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Bazel (http://bazel.io/) WORKSPACE file for RE2.
+workspace(name = "com_googlesource_code_re2")
diff --git a/benchlog/benchplot.py b/benchlog/benchplot.py
new file mode 100755
index 0000000..104abe8
--- /dev/null
+++ b/benchlog/benchplot.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+import argparse # for ArgumentParser
+import subprocess # for Popen
+import tempfile # for NamedTemporaryFile
+import os # for remove
+
+class gnuplot(object):
+
+ output = "result.png"
+
+ script = """
+ set terminal png size 1024, 768
+ set output "{}.png"
+ set title "re2 benchlog"
+ set datafile separator ";"
+ set grid x y
+ set ylabel "MB/s"
+ set autoscale
+ plot """
+
+ template = """'{}' using 1:5:xticlabels(2) with linespoints linewidth 3 title "{}",\\\n"""
+
+ benchdata = dict()
+ tempfiles = []
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ """
+ remove all temporary files
+ """
+
+ for filename in self.tempfiles:
+ os.remove(filename)
+
+ def parse_re2_benchlog(self, filename):
+ """
+ parse the input benchlog and return a dictionary contain bench data
+ """
+
+ benchdata = self.benchdata
+
+ with open(filename) as f:
+
+ for raw in f.readlines():
+
+ data = raw.split('\t')
+
+ if len(data) == 4:
+
+ data = data[0].split('/') + data[1:]
+ data = list(map(str.strip, data))
+
+ if not benchdata.get(data[0]):
+ benchdata[data[0]] = [ data[1:] ]
+ else:
+ benchdata[data[0]].append(data[1:])
+
+ def gen_csv(self):
+ """
+ generate temporary csv files
+ """
+
+ for name, data in self.benchdata.items():
+
+ with tempfile.NamedTemporaryFile(delete=False) as f:
+
+ for index, line in enumerate(data):
+ f.write('{};{}\n'.format(index, ';'.join(line)).encode())
+
+ self.tempfiles.append(f.name)
+ self.script = self.script + self.template.format(f.name, name)
+
+ def run(self):
+ self.gen_csv()
+ script = self.script[:-3].format(self.output)
+ command = subprocess.Popen(['gnuplot'], stdin=subprocess.PIPE)
+ command.communicate(script.encode())
+
+
+if __name__ == '__main__':
+
+ parser = argparse.ArgumentParser(description='generate plots for benchlog')
+ parser.add_argument('benchlog', type=str, help='benchlog generated by re2')
+ args = parser.parse_args()
+
+ try:
+ subprocess.Popen(['gnuplot'], stdin=subprocess.PIPE)
+ except FileNotFoundError:
+ print('you can install "gnuplot" to generate plots automatically')
+ exit(1)
+
+ with gnuplot() as plot:
+ plot.output = args.benchlog
+ plot.parse_re2_benchlog(args.benchlog)
+ plot.run()
diff --git a/doc/mksyntaxgo b/doc/mksyntaxgo
index 42e87d6..caad9b6 100755
--- a/doc/mksyntaxgo
+++ b/doc/mksyntaxgo
@@ -1,7 +1,7 @@
#!/bin/sh
set -e
-out=$GOROOT/src/pkg/regexp/syntax/doc.go
+out=$GOROOT/src/regexp/syntax/doc.go
cp syntax.txt $out
sam -d $out <<'!'
,x g/NOT SUPPORTED/d
diff --git a/doc/syntax.html b/doc/syntax.html
index 7f5e15a..aa08b11 100644
--- a/doc/syntax.html
+++ b/doc/syntax.html
@@ -11,16 +11,15 @@
<tr><td colspan=2>This page lists the regular expression syntax accepted by RE2.</td></tr>
<tr><td colspan=2>It also lists syntax accepted by PCRE, PERL, and VIM.</td></tr>
<tr><td colspan=2>Grayed out expressions are not supported by RE2.</td></tr>
-<tr><td colspan=2>See <a href="http://go/re2">http://go/re2</a> and <a href="http://go/re2quick">http://go/re2quick</a>.</td></tr>
<tr><td></td></tr>
<tr><td colspan=2><b>Single characters:</b></td></tr>
-<tr><td><code>.</code></td><td>any character, including newline (s=true)</td></tr>
+<tr><td><code>.</code></td><td>any character, possibly including newline (s=true)</td></tr>
<tr><td><code>[xyz]</code></td><td>character class</td></tr>
<tr><td><code>[^xyz]</code></td><td>negated character class</td></tr>
<tr><td><code>\d</code></td><td>Perl character class</td></tr>
<tr><td><code>\D</code></td><td>negated Perl character class</td></tr>
-<tr><td><code>[:alpha:]</code></td><td>ASCII character class</td></tr>
-<tr><td><code>[:^alpha:]</code></td><td>negated ASCII character class</td></tr>
+<tr><td><code>[[:alpha:]]</code></td><td>ASCII character class</td></tr>
+<tr><td><code>[[:^alpha:]]</code></td><td>negated ASCII character class</td></tr>
<tr><td><code>\pN</code></td><td>Unicode character class (one-letter name)</td></tr>
<tr><td><code>\p{Greek}</code></td><td>Unicode character class</td></tr>
<tr><td><code>\PN</code></td><td>negated Unicode character class (one-letter name)</td></tr>
@@ -62,7 +61,7 @@
<tr><td><code><font color=#808080>(?&lt;name&gt;re)</font></code></td><td>named &amp; numbered capturing group </td></tr>
<tr><td><code><font color=#808080>(?'name're)</font></code></td><td>named &amp; numbered capturing group </td></tr>
<tr><td><code>(?:re)</code></td><td>non-capturing group</td></tr>
-<tr><td><code>(?flags)</code></td><td>set flags until outer paren closes; non-capturing</td></tr>
+<tr><td><code>(?flags)</code></td><td>set flags within current group; non-capturing</td></tr>
<tr><td><code>(?flags:re)</code></td><td>set flags during re; non-capturing</td></tr>
<tr><td><code><font color=#808080>(?#text)</font></code></td><td>comment </td></tr>
<tr><td><code><font color=#808080>(?|x|y|z)</font></code></td><td>branch numbering reset </td></tr>
@@ -72,16 +71,16 @@
<tr><td></td></tr>
<tr><td colspan=2><b>Flags:</b></td></tr>
<tr><td><code>i</code></td><td>case-insensitive (default false)</td></tr>
-<tr><td><code>m</code></td><td>multi-line mode (default false)</td></tr>
+<tr><td><code>m</code></td><td>multi-line mode: <code>^</code> and <code>$</code> match begin/end line in addition to begin/end text (default false)</td></tr>
<tr><td><code>s</code></td><td>let <code>.</code> match <code>\n</code> (default false)</td></tr>
<tr><td><code>U</code></td><td>ungreedy: swap meaning of <code>x*</code> and <code>x*?</code>, <code>x+</code> and <code>x+?</code>, etc (default false)</td></tr>
<tr><td colspan=2>Flag syntax is <code>xyz</code> (set) or <code>-xyz</code> (clear) or <code>xy-z</code> (set <code>xy</code>, clear <code>z</code>).</td></tr>
<tr><td></td></tr>
<tr><td colspan=2><b>Empty strings:</b></td></tr>
<tr><td><code>^</code></td><td>at beginning of text or line (<code>m</code>=true)</td></tr>
-<tr><td><code>$</code></td><td>at end of text or line (<code>m</code>=true)</td></tr>
+<tr><td><code>$</code></td><td>at end of text (like <code>\z</code> not <code>\Z</code>) or line (<code>m</code>=true)</td></tr>
<tr><td><code>\A</code></td><td>at beginning of text</td></tr>
-<tr><td><code>\b</code></td><td>at word boundary (<code>\w</code> to left and <code>\W</code> to right or vice versa)</td></tr>
+<tr><td><code>\b</code></td><td>at word boundary (<code>\w</code> on one side and <code>\W</code>, <code>\A</code>, or <code>\z</code> on the other)</td></tr>
<tr><td><code>\B</code></td><td>not a word boundary</td></tr>
<tr><td><code><font color=#808080>\G</font></code></td><td>at beginning of subtext being searched <font size=-2>PCRE</font></td></tr>
<tr><td><code><font color=#808080>\G</font></code></td><td>at end of last match <font size=-2>PERL</font></td></tr>
@@ -181,20 +180,20 @@
<tr><td><code><font color=#808080>\V</font></code></td><td>not vertical space </td></tr>
<tr><td></td></tr>
<tr><td colspan=2><b>ASCII character classes:</b></td></tr>
-<tr><td><code>[:alnum:]</code></td><td>alphanumeric (≡ <code>[0-9A-Za-z]</code>)</td></tr>
-<tr><td><code>[:alpha:]</code></td><td>alphabetic (≡ <code>[A-Za-z]</code>)</td></tr>
-<tr><td><code>[:ascii:]</code></td><td>ASCII (≡ <code>[\x00-\x7F]</code>)</td></tr>
-<tr><td><code>[:blank:]</code></td><td>blank (≡ <code>[\t ]</code>)</td></tr>
-<tr><td><code>[:cntrl:]</code></td><td>control (≡ <code>[\x00-\x1F\x7F]</code>)</td></tr>
-<tr><td><code>[:digit:]</code></td><td>digits (≡ <code>[0-9]</code>)</td></tr>
-<tr><td><code>[:graph:]</code></td><td>graphical (≡ <code>[!-~] == [A-Za-z0-9!"#$%&amp;'()*+,\-./:;&lt;=&gt;?@[\\\]^_`{|}~]</code>)</td></tr>
-<tr><td><code>[:lower:]</code></td><td>lower case (≡ <code>[a-z]</code>)</td></tr>
-<tr><td><code>[:print:]</code></td><td>printable (≡ <code>[ -~] == [ [:graph:]]</code>)</td></tr>
-<tr><td><code>[:punct:]</code></td><td>punctuation (≡ <code>[!-/:-@[-`{-~]</code>)</td></tr>
-<tr><td><code>[:space:]</code></td><td>whitespace (≡ <code>[\t\n\v\f\r ]</code>)</td></tr>
-<tr><td><code>[:upper:]</code></td><td>upper case (≡ <code>[A-Z]</code>)</td></tr>
-<tr><td><code>[:word:]</code></td><td>word characters (≡ <code>[0-9A-Za-z_]</code>)</td></tr>
-<tr><td><code>[:xdigit:]</code></td><td>hex digit (≡ <code>[0-9A-Fa-f]</code>)</td></tr>
+<tr><td><code>[[:alnum:]]</code></td><td>alphanumeric (≡ <code>[0-9A-Za-z]</code>)</td></tr>
+<tr><td><code>[[:alpha:]]</code></td><td>alphabetic (≡ <code>[A-Za-z]</code>)</td></tr>
+<tr><td><code>[[:ascii:]]</code></td><td>ASCII (≡ <code>[\x00-\x7F]</code>)</td></tr>
+<tr><td><code>[[:blank:]]</code></td><td>blank (≡ <code>[\t ]</code>)</td></tr>
+<tr><td><code>[[:cntrl:]]</code></td><td>control (≡ <code>[\x00-\x1F\x7F]</code>)</td></tr>
+<tr><td><code>[[:digit:]]</code></td><td>digits (≡ <code>[0-9]</code>)</td></tr>
+<tr><td><code>[[:graph:]]</code></td><td>graphical (≡ <code>[!-~] == [A-Za-z0-9!"#$%&amp;'()*+,\-./:;&lt;=&gt;?@[\\\]^_`{|}~]</code>)</td></tr>
+<tr><td><code>[[:lower:]]</code></td><td>lower case (≡ <code>[a-z]</code>)</td></tr>
+<tr><td><code>[[:print:]]</code></td><td>printable (≡ <code>[ -~] == [ [:graph:]]</code>)</td></tr>
+<tr><td><code>[[:punct:]]</code></td><td>punctuation (≡ <code>[!-/:-@[-`{-~]</code>)</td></tr>
+<tr><td><code>[[:space:]]</code></td><td>whitespace (≡ <code>[\t\n\v\f\r ]</code>)</td></tr>
+<tr><td><code>[[:upper:]]</code></td><td>upper case (≡ <code>[A-Z]</code>)</td></tr>
+<tr><td><code>[[:word:]]</code></td><td>word characters (≡ <code>[0-9A-Za-z_]</code>)</td></tr>
+<tr><td><code>[[:xdigit:]]</code></td><td>hex digit (≡ <code>[0-9A-Fa-f]</code>)</td></tr>
<tr><td></td></tr>
<tr><td colspan=2><b>Unicode character class names--general category:</b></td></tr>
<tr><td><code>C</code></td><td>other</td></tr>
@@ -241,13 +240,17 @@
<tr><td><code>Arabic</code></td><td>Arabic</td></tr>
<tr><td><code>Armenian</code></td><td>Armenian</td></tr>
<tr><td><code>Balinese</code></td><td>Balinese</td></tr>
+<tr><td><code>Bamum</code></td><td>Bamum</td></tr>
+<tr><td><code>Batak</code></td><td>Batak</td></tr>
<tr><td><code>Bengali</code></td><td>Bengali</td></tr>
<tr><td><code>Bopomofo</code></td><td>Bopomofo</td></tr>
+<tr><td><code>Brahmi</code></td><td>Brahmi</td></tr>
<tr><td><code>Braille</code></td><td>Braille</td></tr>
<tr><td><code>Buginese</code></td><td>Buginese</td></tr>
<tr><td><code>Buhid</code></td><td>Buhid</td></tr>
<tr><td><code>Canadian_Aboriginal</code></td><td>Canadian Aboriginal</td></tr>
<tr><td><code>Carian</code></td><td>Carian</td></tr>
+<tr><td><code>Chakma</code></td><td>Chakma</td></tr>
<tr><td><code>Cham</code></td><td>Cham</td></tr>
<tr><td><code>Cherokee</code></td><td>Cherokee</td></tr>
<tr><td><code>Common</code></td><td>characters not specific to one script</td></tr>
@@ -257,6 +260,7 @@
<tr><td><code>Cyrillic</code></td><td>Cyrillic</td></tr>
<tr><td><code>Deseret</code></td><td>Deseret</td></tr>
<tr><td><code>Devanagari</code></td><td>Devanagari</td></tr>
+<tr><td><code>Egyptian_Hieroglyphs</code></td><td>Egyptian Hieroglyphs</td></tr>
<tr><td><code>Ethiopic</code></td><td>Ethiopic</td></tr>
<tr><td><code>Georgian</code></td><td>Georgian</td></tr>
<tr><td><code>Glagolitic</code></td><td>Glagolitic</td></tr>
@@ -269,7 +273,12 @@
<tr><td><code>Hanunoo</code></td><td>Hanunoo</td></tr>
<tr><td><code>Hebrew</code></td><td>Hebrew</td></tr>
<tr><td><code>Hiragana</code></td><td>Hiragana</td></tr>
+<tr><td><code>Imperial_Aramaic</code></td><td>Imperial Aramaic</td></tr>
<tr><td><code>Inherited</code></td><td>inherit script from previous character</td></tr>
+<tr><td><code>Inscriptional_Pahlavi</code></td><td>Inscriptional Pahlavi</td></tr>
+<tr><td><code>Inscriptional_Parthian</code></td><td>Inscriptional Parthian</td></tr>
+<tr><td><code>Javanese</code></td><td>Javanese</td></tr>
+<tr><td><code>Kaithi</code></td><td>Kaithi</td></tr>
<tr><td><code>Kannada</code></td><td>Kannada</td></tr>
<tr><td><code>Katakana</code></td><td>Katakana</td></tr>
<tr><td><code>Kayah_Li</code></td><td>Kayah Li</td></tr>
@@ -283,6 +292,11 @@
<tr><td><code>Lycian</code></td><td>Lycian</td></tr>
<tr><td><code>Lydian</code></td><td>Lydian</td></tr>
<tr><td><code>Malayalam</code></td><td>Malayalam</td></tr>
+<tr><td><code>Mandaic</code></td><td>Mandaic</td></tr>
+<tr><td><code>Meetei_Mayek</code></td><td>Meetei Mayek</td></tr>
+<tr><td><code>Meroitic_Cursive</code></td><td>Meroitic Cursive</td></tr>
+<tr><td><code>Meroitic_Hieroglyphs</code></td><td>Meroitic Hieroglyphs</td></tr>
+<tr><td><code>Miao</code></td><td>Miao</td></tr>
<tr><td><code>Mongolian</code></td><td>Mongolian</td></tr>
<tr><td><code>Myanmar</code></td><td>Myanmar</td></tr>
<tr><td><code>New_Tai_Lue</code></td><td>New Tai Lue (aka Simplified Tai Lue)</td></tr>
@@ -291,6 +305,8 @@
<tr><td><code>Ol_Chiki</code></td><td>Ol Chiki</td></tr>
<tr><td><code>Old_Italic</code></td><td>Old Italic</td></tr>
<tr><td><code>Old_Persian</code></td><td>Old Persian</td></tr>
+<tr><td><code>Old_South_Arabian</code></td><td>Old South Arabian</td></tr>
+<tr><td><code>Old_Turkic</code></td><td>Old Turkic</td></tr>
<tr><td><code>Oriya</code></td><td>Oriya</td></tr>
<tr><td><code>Osmanya</code></td><td>Osmanya</td></tr>
<tr><td><code>Phags_Pa</code></td><td>'Phags Pa</td></tr>
@@ -298,14 +314,19 @@
<tr><td><code>Rejang</code></td><td>Rejang</td></tr>
<tr><td><code>Runic</code></td><td>Runic</td></tr>
<tr><td><code>Saurashtra</code></td><td>Saurashtra</td></tr>
+<tr><td><code>Sharada</code></td><td>Sharada</td></tr>
<tr><td><code>Shavian</code></td><td>Shavian</td></tr>
<tr><td><code>Sinhala</code></td><td>Sinhala</td></tr>
+<tr><td><code>Sora_Sompeng</code></td><td>Sora Sompeng</td></tr>
<tr><td><code>Sundanese</code></td><td>Sundanese</td></tr>
<tr><td><code>Syloti_Nagri</code></td><td>Syloti Nagri</td></tr>
<tr><td><code>Syriac</code></td><td>Syriac</td></tr>
<tr><td><code>Tagalog</code></td><td>Tagalog</td></tr>
<tr><td><code>Tagbanwa</code></td><td>Tagbanwa</td></tr>
<tr><td><code>Tai_Le</code></td><td>Tai Le</td></tr>
+<tr><td><code>Tai_Tham</code></td><td>Tai Tham</td></tr>
+<tr><td><code>Tai_Viet</code></td><td>Tai Viet</td></tr>
+<tr><td><code>Takri</code></td><td>Takri</td></tr>
<tr><td><code>Tamil</code></td><td>Tamil</td></tr>
<tr><td><code>Telugu</code></td><td>Telugu</td></tr>
<tr><td><code>Thaana</code></td><td>Thaana</td></tr>
diff --git a/doc/syntax.txt b/doc/syntax.txt
index f940750..c87494e 100644
--- a/doc/syntax.txt
+++ b/doc/syntax.txt
@@ -7,8 +7,8 @@ Single characters:
[^xyz] negated character class
\d Perl character class
\D negated Perl character class
-[:alpha:] ASCII character class
-[:^alpha:] negated ASCII character class
+[[:alpha:]] ASCII character class
+[[:^alpha:]] negated ASCII character class
\pN Unicode character class (one-letter name)
\p{Greek} Unicode character class
\PN negated Unicode character class (one-letter name)
@@ -36,6 +36,10 @@ x{-} (== x*?) NOT SUPPORTED vim
x{-n} (== x{n}?) NOT SUPPORTED vim
x= (== x?) NOT SUPPORTED vim
+Implementation restriction: The counting forms «x{n,m}», «x{n,}», and «x{n}»
+reject forms that create a minimum or maximum repetition count above 1000.
+Unlimited repetitions are not subject to this restriction.
+
Possessive repetitions:
x*+ zero or more «x», possessive NOT SUPPORTED
x++ one or more «x», possessive NOT SUPPORTED
@@ -45,10 +49,10 @@ x{n,}+ «n» or more «x», possessive NOT SUPPORTED
x{n}+ exactly «n» «x», possessive NOT SUPPORTED
Grouping:
-(re) numbered capturing group
-(?P<name>re) named & numbered capturing group
-(?<name>re) named & numbered capturing group NOT SUPPORTED
-(?'name're) named & numbered capturing group NOT SUPPORTED
+(re) numbered capturing group (submatch)
+(?P<name>re) named & numbered capturing group (submatch)
+(?<name>re) named & numbered capturing group (submatch) NOT SUPPORTED
+(?'name're) named & numbered capturing group (submatch) NOT SUPPORTED
(?:re) non-capturing group
(?flags) set flags within current group; non-capturing
(?flags:re) set flags during re; non-capturing
@@ -69,8 +73,8 @@ Empty strings:
^ at beginning of text or line («m»=true)
$ at end of text (like «\z» not «\Z») or line («m»=true)
\A at beginning of text
-\b at word boundary («\w» on one side and «\W», «\A», or «\z» on the other)
-\B not a word boundary
+\b at ASCII word boundary («\w» on one side and «\W», «\A», or «\z» on the other)
+\B not at ASCII word boundary
\G at beginning of subtext being searched NOT SUPPORTED pcre
\G at end of last match NOT SUPPORTED perl
\Z at end of text, or before newline at end of text NOT SUPPORTED
@@ -155,7 +159,7 @@ Named character classes as character class elements:
[\p{Name}] named Unicode property inside character class (== \p{Name})
[^\p{Name}] named Unicode property inside negated character class (== \P{Name})
-Perl character classes:
+Perl character classes (all ASCII-only):
\d digits (== [0-9])
\D not digits (== [^0-9])
\s whitespace (== [\t\n\f\r ])
@@ -169,20 +173,20 @@ Perl character classes:
\V not vertical space NOT SUPPORTED
ASCII character classes:
-[:alnum:] alphanumeric (== [0-9A-Za-z])
-[:alpha:] alphabetic (== [A-Za-z])
-[:ascii:] ASCII (== [\x00-\x7F])
-[:blank:] blank (== [\t ])
-[:cntrl:] control (== [\x00-\x1F\x7F])
-[:digit:] digits (== [0-9])
-[:graph:] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])
-[:lower:] lower case (== [a-z])
-[:print:] printable (== [ -~] == [ [:graph:]])
-[:punct:] punctuation (== [!-/:-@[-`{-~])
-[:space:] whitespace (== [\t\n\v\f\r ])
-[:upper:] upper case (== [A-Z])
-[:word:] word characters (== [0-9A-Za-z_])
-[:xdigit:] hex digit (== [0-9A-Fa-f])
+[[:alnum:]] alphanumeric (== [0-9A-Za-z])
+[[:alpha:]] alphabetic (== [A-Za-z])
+[[:ascii:]] ASCII (== [\x00-\x7F])
+[[:blank:]] blank (== [\t ])
+[[:cntrl:]] control (== [\x00-\x1F\x7F])
+[[:digit:]] digits (== [0-9])
+[[:graph:]] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])
+[[:lower:]] lower case (== [a-z])
+[[:print:]] printable (== [ -~] == [ [:graph:]])
+[[:punct:]] punctuation (== [!-/:-@[-`{-~])
+[[:space:]] whitespace (== [\t\n\v\f\r ])
+[[:upper:]] upper case (== [A-Z])
+[[:word:]] word characters (== [0-9A-Za-z_])
+[[:xdigit:]] hex digit (== [0-9A-Fa-f])
Unicode character class names--general category:
C other
@@ -226,83 +230,154 @@ Zp paragraph separator
Zs space separator
Unicode character class names--scripts:
-Arabic Arabic
-Armenian Armenian
-Balinese Balinese
-Bengali Bengali
-Bopomofo Bopomofo
-Braille Braille
-Buginese Buginese
-Buhid Buhid
-Canadian_Aboriginal Canadian Aboriginal
-Carian Carian
-Cham Cham
-Cherokee Cherokee
-Common characters not specific to one script
-Coptic Coptic
-Cuneiform Cuneiform
-Cypriot Cypriot
-Cyrillic Cyrillic
-Deseret Deseret
-Devanagari Devanagari
-Ethiopic Ethiopic
-Georgian Georgian
-Glagolitic Glagolitic
-Gothic Gothic
-Greek Greek
-Gujarati Gujarati
-Gurmukhi Gurmukhi
-Han Han
-Hangul Hangul
-Hanunoo Hanunoo
-Hebrew Hebrew
-Hiragana Hiragana
-Inherited inherit script from previous character
-Kannada Kannada
-Katakana Katakana
-Kayah_Li Kayah Li
-Kharoshthi Kharoshthi
-Khmer Khmer
-Lao Lao
-Latin Latin
-Lepcha Lepcha
-Limbu Limbu
-Linear_B Linear B
-Lycian Lycian
-Lydian Lydian
-Malayalam Malayalam
-Mongolian Mongolian
-Myanmar Myanmar
-New_Tai_Lue New Tai Lue (aka Simplified Tai Lue)
-Nko Nko
-Ogham Ogham
-Ol_Chiki Ol Chiki
-Old_Italic Old Italic
-Old_Persian Old Persian
-Oriya Oriya
-Osmanya Osmanya
-Phags_Pa 'Phags Pa
-Phoenician Phoenician
-Rejang Rejang
-Runic Runic
-Saurashtra Saurashtra
-Shavian Shavian
-Sinhala Sinhala
-Sundanese Sundanese
-Syloti_Nagri Syloti Nagri
-Syriac Syriac
-Tagalog Tagalog
-Tagbanwa Tagbanwa
-Tai_Le Tai Le
-Tamil Tamil
-Telugu Telugu
-Thaana Thaana
-Thai Thai
-Tibetan Tibetan
-Tifinagh Tifinagh
-Ugaritic Ugaritic
-Vai Vai
-Yi Yi
+Adlam
+Ahom
+Anatolian_Hieroglyphs
+Arabic
+Armenian
+Avestan
+Balinese
+Bamum
+Bassa_Vah
+Batak
+Bengali
+Bhaiksuki
+Bopomofo
+Brahmi
+Braille
+Buginese
+Buhid
+Canadian_Aboriginal
+Carian
+Caucasian_Albanian
+Chakma
+Cham
+Cherokee
+Common
+Coptic
+Cuneiform
+Cypriot
+Cyrillic
+Deseret
+Devanagari
+Dogra
+Duployan
+Egyptian_Hieroglyphs
+Elbasan
+Ethiopic
+Georgian
+Glagolitic
+Gothic
+Grantha
+Greek
+Gujarati
+Gunjala_Gondi
+Gurmukhi
+Han
+Hangul
+Hanifi_Rohingya
+Hanunoo
+Hatran
+Hebrew
+Hiragana
+Imperial_Aramaic
+Inherited
+Inscriptional_Pahlavi
+Inscriptional_Parthian
+Javanese
+Kaithi
+Kannada
+Katakana
+Kayah_Li
+Kharoshthi
+Khmer
+Khojki
+Khudawadi
+Lao
+Latin
+Lepcha
+Limbu
+Linear_A
+Linear_B
+Lisu
+Lycian
+Lydian
+Mahajani
+Makasar
+Malayalam
+Mandaic
+Manichaean
+Marchen
+Masaram_Gondi
+Medefaidrin
+Meetei_Mayek
+Mende_Kikakui
+Meroitic_Cursive
+Meroitic_Hieroglyphs
+Miao
+Modi
+Mongolian
+Mro
+Multani
+Myanmar
+Nabataean
+New_Tai_Lue
+Newa
+Nko
+Nushu
+Ogham
+Ol_Chiki
+Old_Hungarian
+Old_Italic
+Old_North_Arabian
+Old_Permic
+Old_Persian
+Old_Sogdian
+Old_South_Arabian
+Old_Turkic
+Oriya
+Osage
+Osmanya
+Pahawh_Hmong
+Palmyrene
+Pau_Cin_Hau
+Phags_Pa
+Phoenician
+Psalter_Pahlavi
+Rejang
+Runic
+Samaritan
+Saurashtra
+Sharada
+Shavian
+Siddham
+SignWriting
+Sinhala
+Sogdian
+Sora_Sompeng
+Soyombo
+Sundanese
+Syloti_Nagri
+Syriac
+Tagalog
+Tagbanwa
+Tai_Le
+Tai_Tham
+Tai_Viet
+Takri
+Tamil
+Tangut
+Telugu
+Thaana
+Thai
+Tibetan
+Tifinagh
+Tirhuta
+Ugaritic
+Vai
+Warang_Citi
+Yi
+Zanabazar_Square
Vim character classes:
\i identifier character NOT SUPPORTED vim
diff --git a/kokoro/bazel.sh b/kokoro/bazel.sh
new file mode 100755
index 0000000..6f25982
--- /dev/null
+++ b/kokoro/bazel.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+set -eux
+
+cd git/re2
+
+bazel clean
+bazel build --compilation_mode=dbg -- //...
+bazel test --compilation_mode=dbg --test_output=errors -- //... \
+ -//:dfa_test \
+ -//:exhaustive1_test \
+ -//:exhaustive2_test \
+ -//:exhaustive3_test \
+ -//:exhaustive_test \
+ -//:random_test
+
+bazel clean
+bazel build --compilation_mode=opt -- //...
+bazel test --compilation_mode=opt --test_output=errors -- //... \
+ -//:dfa_test \
+ -//:exhaustive1_test \
+ -//:exhaustive2_test \
+ -//:exhaustive3_test \
+ -//:exhaustive_test \
+ -//:random_test
+
+exit 0
diff --git a/kokoro/cmake.sh b/kokoro/cmake.sh
new file mode 100755
index 0000000..999fbfe
--- /dev/null
+++ b/kokoro/cmake.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+set -eux
+
+cd git/re2
+
+case "${KOKORO_JOB_NAME}" in
+ */windows-*)
+ CMAKE_G_A_FLAGS=('-G' 'Visual Studio 14 2015' '-A' 'x64')
+ ;;
+ *)
+ CMAKE_G_A_FLAGS=()
+ # Work around a bug in older versions of bash. :/
+ set +u
+ ;;
+esac
+
+cmake -D CMAKE_BUILD_TYPE=Debug "${CMAKE_G_A_FLAGS[@]}" .
+cmake --build . --config Debug --clean-first
+ctest -C Debug --output-on-failure -E 'dfa|exhaustive|random'
+
+cmake -D CMAKE_BUILD_TYPE=Release "${CMAKE_G_A_FLAGS[@]}" .
+cmake --build . --config Release --clean-first
+ctest -C Release --output-on-failure -E 'dfa|exhaustive|random'
+
+exit 0
diff --git a/kokoro/macos-bazel.cfg b/kokoro/macos-bazel.cfg
new file mode 100644
index 0000000..7901981
--- /dev/null
+++ b/kokoro/macos-bazel.cfg
@@ -0,0 +1 @@
+build_file: "re2/kokoro/macos-bazel.sh"
diff --git a/kokoro/macos-bazel.sh b/kokoro/macos-bazel.sh
new file mode 100755
index 0000000..e43c852
--- /dev/null
+++ b/kokoro/macos-bazel.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eux
+bash git/re2/kokoro/bazel.sh
+exit $?
diff --git a/kokoro/macos-cmake.cfg b/kokoro/macos-cmake.cfg
new file mode 100644
index 0000000..5c459e7
--- /dev/null
+++ b/kokoro/macos-cmake.cfg
@@ -0,0 +1 @@
+build_file: "re2/kokoro/macos-cmake.sh"
diff --git a/kokoro/macos-cmake.sh b/kokoro/macos-cmake.sh
new file mode 100755
index 0000000..ef4b7dc
--- /dev/null
+++ b/kokoro/macos-cmake.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eux
+bash git/re2/kokoro/cmake.sh
+exit $?
diff --git a/kokoro/ubuntu-bazel.cfg b/kokoro/ubuntu-bazel.cfg
new file mode 100644
index 0000000..884d14f
--- /dev/null
+++ b/kokoro/ubuntu-bazel.cfg
@@ -0,0 +1 @@
+build_file: "re2/kokoro/ubuntu-bazel.sh"
diff --git a/kokoro/ubuntu-bazel.sh b/kokoro/ubuntu-bazel.sh
new file mode 100755
index 0000000..e43c852
--- /dev/null
+++ b/kokoro/ubuntu-bazel.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -eux
+bash git/re2/kokoro/bazel.sh
+exit $?
diff --git a/kokoro/windows-bazel.bat b/kokoro/windows-bazel.bat
new file mode 100755
index 0000000..283f8d2
--- /dev/null
+++ b/kokoro/windows-bazel.bat
@@ -0,0 +1,2 @@
+bash git/re2/kokoro/bazel.sh
+EXIT /B %ERRORLEVEL%
diff --git a/kokoro/windows-bazel.cfg b/kokoro/windows-bazel.cfg
new file mode 100644
index 0000000..18b1ed7
--- /dev/null
+++ b/kokoro/windows-bazel.cfg
@@ -0,0 +1 @@
+build_file: "re2/kokoro/windows-bazel.bat"
diff --git a/kokoro/windows-cmake.bat b/kokoro/windows-cmake.bat
new file mode 100755
index 0000000..77a4db9
--- /dev/null
+++ b/kokoro/windows-cmake.bat
@@ -0,0 +1,2 @@
+bash git/re2/kokoro/cmake.sh
+EXIT /B %ERRORLEVEL%
diff --git a/kokoro/windows-cmake.cfg b/kokoro/windows-cmake.cfg
new file mode 100644
index 0000000..4453eb6
--- /dev/null
+++ b/kokoro/windows-cmake.cfg
@@ -0,0 +1 @@
+build_file: "re2/kokoro/windows-cmake.bat"
diff --git a/lib/codereview/codereview.cfg b/lib/codereview/codereview.cfg
deleted file mode 100644
index 9581920..0000000
--- a/lib/codereview/codereview.cfg
+++ /dev/null
@@ -1 +0,0 @@
-defaultcc: re2-dev@googlegroups.com
diff --git a/lib/codereview/codereview.py b/lib/codereview/codereview.py
deleted file mode 100644
index d26df2a..0000000
--- a/lib/codereview/codereview.py
+++ /dev/null
@@ -1,3562 +0,0 @@
-# coding=utf-8
-# (The line above is necessary so that I can use 世界 in the
-# *comment* below without Python getting all bent out of shape.)
-
-# Copyright 2007-2009 Google Inc.
-#
-# 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.
-
-'''Mercurial interface to codereview.appspot.com.
-
-To configure, set the following options in
-your repository's .hg/hgrc file.
-
- [extensions]
- codereview = /path/to/codereview.py
-
- [codereview]
- server = codereview.appspot.com
-
-The server should be running Rietveld; see http://code.google.com/p/rietveld/.
-
-In addition to the new commands, this extension introduces
-the file pattern syntax @nnnnnn, where nnnnnn is a change list
-number, to mean the files included in that change list, which
-must be associated with the current client.
-
-For example, if change 123456 contains the files x.go and y.go,
-"hg diff @123456" is equivalent to"hg diff x.go y.go".
-'''
-
-import sys
-
-if __name__ == "__main__":
- print >>sys.stderr, "This is a Mercurial extension and should not be invoked directly."
- sys.exit(2)
-
-# We require Python 2.6 for the json package.
-if sys.version < '2.6':
- print >>sys.stderr, "The codereview extension requires Python 2.6 or newer."
- print >>sys.stderr, "You are running Python " + sys.version
- sys.exit(2)
-
-import json
-import os
-import re
-import stat
-import subprocess
-import threading
-import time
-
-from mercurial import commands as hg_commands
-from mercurial import util as hg_util
-
-defaultcc = None
-codereview_disabled = None
-real_rollback = None
-releaseBranch = None
-server = "codereview.appspot.com"
-server_url_base = None
-
-#######################################################################
-# Normally I would split this into multiple files, but it simplifies
-# import path headaches to keep it all in one file. Sorry.
-# The different parts of the file are separated by banners like this one.
-
-#######################################################################
-# Helpers
-
-def RelativePath(path, cwd):
- n = len(cwd)
- if path.startswith(cwd) and path[n] == '/':
- return path[n+1:]
- return path
-
-def Sub(l1, l2):
- return [l for l in l1 if l not in l2]
-
-def Add(l1, l2):
- l = l1 + Sub(l2, l1)
- l.sort()
- return l
-
-def Intersect(l1, l2):
- return [l for l in l1 if l in l2]
-
-#######################################################################
-# RE: UNICODE STRING HANDLING
-#
-# Python distinguishes between the str (string of bytes)
-# and unicode (string of code points) types. Most operations
-# work on either one just fine, but some (like regexp matching)
-# require unicode, and others (like write) require str.
-#
-# As befits the language, Python hides the distinction between
-# unicode and str by converting between them silently, but
-# *only* if all the bytes/code points involved are 7-bit ASCII.
-# This means that if you're not careful, your program works
-# fine on "hello, world" and fails on "hello, 世界". And of course,
-# the obvious way to be careful - use static types - is unavailable.
-# So the only way is trial and error to find where to put explicit
-# conversions.
-#
-# Because more functions do implicit conversion to str (string of bytes)
-# than do implicit conversion to unicode (string of code points),
-# the convention in this module is to represent all text as str,
-# converting to unicode only when calling a unicode-only function
-# and then converting back to str as soon as possible.
-
-def typecheck(s, t):
- if type(s) != t:
- raise hg_util.Abort("type check failed: %s has type %s != %s" % (repr(s), type(s), t))
-
-# If we have to pass unicode instead of str, ustr does that conversion clearly.
-def ustr(s):
- typecheck(s, str)
- return s.decode("utf-8")
-
-# Even with those, Mercurial still sometimes turns unicode into str
-# and then tries to use it as ascii. Change Mercurial's default.
-def set_mercurial_encoding_to_utf8():
- from mercurial import encoding
- encoding.encoding = 'utf-8'
-
-set_mercurial_encoding_to_utf8()
-
-# Even with those we still run into problems.
-# I tried to do things by the book but could not convince
-# Mercurial to let me check in a change with UTF-8 in the
-# CL description or author field, no matter how many conversions
-# between str and unicode I inserted and despite changing the
-# default encoding. I'm tired of this game, so set the default
-# encoding for all of Python to 'utf-8', not 'ascii'.
-def default_to_utf8():
- import sys
- stdout, __stdout__ = sys.stdout, sys.__stdout__
- reload(sys) # site.py deleted setdefaultencoding; get it back
- sys.stdout, sys.__stdout__ = stdout, __stdout__
- sys.setdefaultencoding('utf-8')
-
-default_to_utf8()
-
-#######################################################################
-# Status printer for long-running commands
-
-global_status = None
-
-def set_status(s):
- # print >>sys.stderr, "\t", time.asctime(), s
- global global_status
- global_status = s
-
-class StatusThread(threading.Thread):
- def __init__(self):
- threading.Thread.__init__(self)
- def run(self):
- # pause a reasonable amount of time before
- # starting to display status messages, so that
- # most hg commands won't ever see them.
- time.sleep(30)
-
- # now show status every 15 seconds
- while True:
- time.sleep(15 - time.time() % 15)
- s = global_status
- if s is None:
- continue
- if s == "":
- s = "(unknown status)"
- print >>sys.stderr, time.asctime(), s
-
-def start_status_thread():
- t = StatusThread()
- t.setDaemon(True) # allowed to exit if t is still running
- t.start()
-
-#######################################################################
-# Change list parsing.
-#
-# Change lists are stored in .hg/codereview/cl.nnnnnn
-# where nnnnnn is the number assigned by the code review server.
-# Most data about a change list is stored on the code review server
-# too: the description, reviewer, and cc list are all stored there.
-# The only thing in the cl.nnnnnn file is the list of relevant files.
-# Also, the existence of the cl.nnnnnn file marks this repository
-# as the one where the change list lives.
-
-emptydiff = """Index: ~rietveld~placeholder~
-===================================================================
-diff --git a/~rietveld~placeholder~ b/~rietveld~placeholder~
-new file mode 100644
-"""
-
-class CL(object):
- def __init__(self, name):
- typecheck(name, str)
- self.name = name
- self.desc = ''
- self.files = []
- self.reviewer = []
- self.cc = []
- self.url = ''
- self.local = False
- self.web = False
- self.copied_from = None # None means current user
- self.mailed = False
- self.private = False
- self.lgtm = []
-
- def DiskText(self):
- cl = self
- s = ""
- if cl.copied_from:
- s += "Author: " + cl.copied_from + "\n\n"
- if cl.private:
- s += "Private: " + str(self.private) + "\n"
- s += "Mailed: " + str(self.mailed) + "\n"
- s += "Description:\n"
- s += Indent(cl.desc, "\t")
- s += "Files:\n"
- for f in cl.files:
- s += "\t" + f + "\n"
- typecheck(s, str)
- return s
-
- def EditorText(self):
- cl = self
- s = _change_prolog
- s += "\n"
- if cl.copied_from:
- s += "Author: " + cl.copied_from + "\n"
- if cl.url != '':
- s += 'URL: ' + cl.url + ' # cannot edit\n\n'
- if cl.private:
- s += "Private: True\n"
- s += "Reviewer: " + JoinComma(cl.reviewer) + "\n"
- s += "CC: " + JoinComma(cl.cc) + "\n"
- s += "\n"
- s += "Description:\n"
- if cl.desc == '':
- s += "\t<enter description here>\n"
- else:
- s += Indent(cl.desc, "\t")
- s += "\n"
- if cl.local or cl.name == "new":
- s += "Files:\n"
- for f in cl.files:
- s += "\t" + f + "\n"
- s += "\n"
- typecheck(s, str)
- return s
-
- def PendingText(self, quick=False):
- cl = self
- s = cl.name + ":" + "\n"
- s += Indent(cl.desc, "\t")
- s += "\n"
- if cl.copied_from:
- s += "\tAuthor: " + cl.copied_from + "\n"
- if not quick:
- s += "\tReviewer: " + JoinComma(cl.reviewer) + "\n"
- for (who, line) in cl.lgtm:
- s += "\t\t" + who + ": " + line + "\n"
- s += "\tCC: " + JoinComma(cl.cc) + "\n"
- s += "\tFiles:\n"
- for f in cl.files:
- s += "\t\t" + f + "\n"
- typecheck(s, str)
- return s
-
- def Flush(self, ui, repo):
- if self.name == "new":
- self.Upload(ui, repo, gofmt_just_warn=True, creating=True)
- dir = CodeReviewDir(ui, repo)
- path = dir + '/cl.' + self.name
- f = open(path+'!', "w")
- f.write(self.DiskText())
- f.close()
- if sys.platform == "win32" and os.path.isfile(path):
- os.remove(path)
- os.rename(path+'!', path)
- if self.web and not self.copied_from:
- EditDesc(self.name, desc=self.desc,
- reviewers=JoinComma(self.reviewer), cc=JoinComma(self.cc),
- private=self.private)
-
- def Delete(self, ui, repo):
- dir = CodeReviewDir(ui, repo)
- os.unlink(dir + "/cl." + self.name)
-
- def Subject(self):
- s = line1(self.desc)
- if len(s) > 60:
- s = s[0:55] + "..."
- if self.name != "new":
- s = "code review %s: %s" % (self.name, s)
- typecheck(s, str)
- return s
-
- def Upload(self, ui, repo, send_mail=False, gofmt=True, gofmt_just_warn=False, creating=False, quiet=False):
- if not self.files and not creating:
- ui.warn("no files in change list\n")
- if ui.configbool("codereview", "force_gofmt", True) and gofmt:
- CheckFormat(ui, repo, self.files, just_warn=gofmt_just_warn)
- set_status("uploading CL metadata + diffs")
- os.chdir(repo.root)
- form_fields = [
- ("content_upload", "1"),
- ("reviewers", JoinComma(self.reviewer)),
- ("cc", JoinComma(self.cc)),
- ("description", self.desc),
- ("base_hashes", ""),
- ]
-
- if self.name != "new":
- form_fields.append(("issue", self.name))
- vcs = None
- # We do not include files when creating the issue,
- # because we want the patch sets to record the repository
- # and base revision they are diffs against. We use the patch
- # set message for that purpose, but there is no message with
- # the first patch set. Instead the message gets used as the
- # new CL's overall subject. So omit the diffs when creating
- # and then we'll run an immediate upload.
- # This has the effect that every CL begins with an empty "Patch set 1".
- if self.files and not creating:
- vcs = MercurialVCS(upload_options, ui, repo)
- data = vcs.GenerateDiff(self.files)
- files = vcs.GetBaseFiles(data)
- if len(data) > MAX_UPLOAD_SIZE:
- uploaded_diff_file = []
- form_fields.append(("separate_patches", "1"))
- else:
- uploaded_diff_file = [("data", "data.diff", data)]
- else:
- uploaded_diff_file = [("data", "data.diff", emptydiff)]
-
- if vcs and self.name != "new":
- form_fields.append(("subject", "diff -r " + vcs.base_rev + " " + ui.expandpath("default")))
- else:
- # First upload sets the subject for the CL itself.
- form_fields.append(("subject", self.Subject()))
- ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff_file)
- response_body = MySend("/upload", body, content_type=ctype)
- patchset = None
- msg = response_body
- lines = msg.splitlines()
- if len(lines) >= 2:
- msg = lines[0]
- patchset = lines[1].strip()
- patches = [x.split(" ", 1) for x in lines[2:]]
- if response_body.startswith("Issue updated.") and quiet:
- pass
- else:
- ui.status(msg + "\n")
- set_status("uploaded CL metadata + diffs")
- if not response_body.startswith("Issue created.") and not response_body.startswith("Issue updated."):
- raise hg_util.Abort("failed to update issue: " + response_body)
- issue = msg[msg.rfind("/")+1:]
- self.name = issue
- if not self.url:
- self.url = server_url_base + self.name
- if not uploaded_diff_file:
- set_status("uploading patches")
- patches = UploadSeparatePatches(issue, rpc, patchset, data, upload_options)
- if vcs:
- set_status("uploading base files")
- vcs.UploadBaseFiles(issue, rpc, patches, patchset, upload_options, files)
- if send_mail:
- set_status("sending mail")
- MySend("/" + issue + "/mail", payload="")
- self.web = True
- set_status("flushing changes to disk")
- self.Flush(ui, repo)
- return
-
- def Mail(self, ui, repo):
- pmsg = "Hello " + JoinComma(self.reviewer)
- if self.cc:
- pmsg += " (cc: %s)" % (', '.join(self.cc),)
- pmsg += ",\n"
- pmsg += "\n"
- repourl = ui.expandpath("default")
- if not self.mailed:
- pmsg += "I'd like you to review this change to\n" + repourl + "\n"
- else:
- pmsg += "Please take another look.\n"
- typecheck(pmsg, str)
- PostMessage(ui, self.name, pmsg, subject=self.Subject())
- self.mailed = True
- self.Flush(ui, repo)
-
-def GoodCLName(name):
- typecheck(name, str)
- return re.match("^[0-9]+$", name)
-
-def ParseCL(text, name):
- typecheck(text, str)
- typecheck(name, str)
- sname = None
- lineno = 0
- sections = {
- 'Author': '',
- 'Description': '',
- 'Files': '',
- 'URL': '',
- 'Reviewer': '',
- 'CC': '',
- 'Mailed': '',
- 'Private': '',
- }
- for line in text.split('\n'):
- lineno += 1
- line = line.rstrip()
- if line != '' and line[0] == '#':
- continue
- if line == '' or line[0] == ' ' or line[0] == '\t':
- if sname == None and line != '':
- return None, lineno, 'text outside section'
- if sname != None:
- sections[sname] += line + '\n'
- continue
- p = line.find(':')
- if p >= 0:
- s, val = line[:p].strip(), line[p+1:].strip()
- if s in sections:
- sname = s
- if val != '':
- sections[sname] += val + '\n'
- continue
- return None, lineno, 'malformed section header'
-
- for k in sections:
- sections[k] = StripCommon(sections[k]).rstrip()
-
- cl = CL(name)
- if sections['Author']:
- cl.copied_from = sections['Author']
- cl.desc = sections['Description']
- for line in sections['Files'].split('\n'):
- i = line.find('#')
- if i >= 0:
- line = line[0:i].rstrip()
- line = line.strip()
- if line == '':
- continue
- cl.files.append(line)
- cl.reviewer = SplitCommaSpace(sections['Reviewer'])
- cl.cc = SplitCommaSpace(sections['CC'])
- cl.url = sections['URL']
- if sections['Mailed'] != 'False':
- # Odd default, but avoids spurious mailings when
- # reading old CLs that do not have a Mailed: line.
- # CLs created with this update will always have
- # Mailed: False on disk.
- cl.mailed = True
- if sections['Private'] in ('True', 'true', 'Yes', 'yes'):
- cl.private = True
- if cl.desc == '<enter description here>':
- cl.desc = ''
- return cl, 0, ''
-
-def SplitCommaSpace(s):
- typecheck(s, str)
- s = s.strip()
- if s == "":
- return []
- return re.split(", *", s)
-
-def CutDomain(s):
- typecheck(s, str)
- i = s.find('@')
- if i >= 0:
- s = s[0:i]
- return s
-
-def JoinComma(l):
- for s in l:
- typecheck(s, str)
- return ", ".join(l)
-
-def ExceptionDetail():
- s = str(sys.exc_info()[0])
- if s.startswith("<type '") and s.endswith("'>"):
- s = s[7:-2]
- elif s.startswith("<class '") and s.endswith("'>"):
- s = s[8:-2]
- arg = str(sys.exc_info()[1])
- if len(arg) > 0:
- s += ": " + arg
- return s
-
-def IsLocalCL(ui, repo, name):
- return GoodCLName(name) and os.access(CodeReviewDir(ui, repo) + "/cl." + name, 0)
-
-# Load CL from disk and/or the web.
-def LoadCL(ui, repo, name, web=True):
- typecheck(name, str)
- set_status("loading CL " + name)
- if not GoodCLName(name):
- return None, "invalid CL name"
- dir = CodeReviewDir(ui, repo)
- path = dir + "cl." + name
- if os.access(path, 0):
- ff = open(path)
- text = ff.read()
- ff.close()
- cl, lineno, err = ParseCL(text, name)
- if err != "":
- return None, "malformed CL data: "+err
- cl.local = True
- else:
- cl = CL(name)
- if web:
- set_status("getting issue metadata from web")
- d = JSONGet(ui, "/api/" + name + "?messages=true")
- set_status(None)
- if d is None:
- return None, "cannot load CL %s from server" % (name,)
- if 'owner_email' not in d or 'issue' not in d or str(d['issue']) != name:
- return None, "malformed response loading CL data from code review server"
- cl.dict = d
- cl.reviewer = d.get('reviewers', [])
- cl.cc = d.get('cc', [])
- if cl.local and cl.copied_from and cl.desc:
- # local copy of CL written by someone else
- # and we saved a description. use that one,
- # so that committers can edit the description
- # before doing hg submit.
- pass
- else:
- cl.desc = d.get('description', "")
- cl.url = server_url_base + name
- cl.web = True
- cl.private = d.get('private', False) != False
- cl.lgtm = []
- for m in d.get('messages', []):
- if m.get('approval', False) == True:
- who = re.sub('@.*', '', m.get('sender', ''))
- text = re.sub("\n(.|\n)*", '', m.get('text', ''))
- cl.lgtm.append((who, text))
-
- set_status("loaded CL " + name)
- return cl, ''
-
-class LoadCLThread(threading.Thread):
- def __init__(self, ui, repo, dir, f, web):
- threading.Thread.__init__(self)
- self.ui = ui
- self.repo = repo
- self.dir = dir
- self.f = f
- self.web = web
- self.cl = None
- def run(self):
- cl, err = LoadCL(self.ui, self.repo, self.f[3:], web=self.web)
- if err != '':
- self.ui.warn("loading "+self.dir+self.f+": " + err + "\n")
- return
- self.cl = cl
-
-# Load all the CLs from this repository.
-def LoadAllCL(ui, repo, web=True):
- dir = CodeReviewDir(ui, repo)
- m = {}
- files = [f for f in os.listdir(dir) if f.startswith('cl.')]
- if not files:
- return m
- active = []
- first = True
- for f in files:
- t = LoadCLThread(ui, repo, dir, f, web)
- t.start()
- if web and first:
- # first request: wait in case it needs to authenticate
- # otherwise we get lots of user/password prompts
- # running in parallel.
- t.join()
- if t.cl:
- m[t.cl.name] = t.cl
- first = False
- else:
- active.append(t)
- for t in active:
- t.join()
- if t.cl:
- m[t.cl.name] = t.cl
- return m
-
-# Find repository root. On error, ui.warn and return None
-def RepoDir(ui, repo):
- url = repo.url();
- if not url.startswith('file:'):
- ui.warn("repository %s is not in local file system\n" % (url,))
- return None
- url = url[5:]
- if url.endswith('/'):
- url = url[:-1]
- typecheck(url, str)
- return url
-
-# Find (or make) code review directory. On error, ui.warn and return None
-def CodeReviewDir(ui, repo):
- dir = RepoDir(ui, repo)
- if dir == None:
- return None
- dir += '/.hg/codereview/'
- if not os.path.isdir(dir):
- try:
- os.mkdir(dir, 0700)
- except:
- ui.warn('cannot mkdir %s: %s\n' % (dir, ExceptionDetail()))
- return None
- typecheck(dir, str)
- return dir
-
-# Turn leading tabs into spaces, so that the common white space
-# prefix doesn't get confused when people's editors write out
-# some lines with spaces, some with tabs. Only a heuristic
-# (some editors don't use 8 spaces either) but a useful one.
-def TabsToSpaces(line):
- i = 0
- while i < len(line) and line[i] == '\t':
- i += 1
- return ' '*(8*i) + line[i:]
-
-# Strip maximal common leading white space prefix from text
-def StripCommon(text):
- typecheck(text, str)
- ws = None
- for line in text.split('\n'):
- line = line.rstrip()
- if line == '':
- continue
- line = TabsToSpaces(line)
- white = line[:len(line)-len(line.lstrip())]
- if ws == None:
- ws = white
- else:
- common = ''
- for i in range(min(len(white), len(ws))+1):
- if white[0:i] == ws[0:i]:
- common = white[0:i]
- ws = common
- if ws == '':
- break
- if ws == None:
- return text
- t = ''
- for line in text.split('\n'):
- line = line.rstrip()
- line = TabsToSpaces(line)
- if line.startswith(ws):
- line = line[len(ws):]
- if line == '' and t == '':
- continue
- t += line + '\n'
- while len(t) >= 2 and t[-2:] == '\n\n':
- t = t[:-1]
- typecheck(t, str)
- return t
-
-# Indent text with indent.
-def Indent(text, indent):
- typecheck(text, str)
- typecheck(indent, str)
- t = ''
- for line in text.split('\n'):
- t += indent + line + '\n'
- typecheck(t, str)
- return t
-
-# Return the first line of l
-def line1(text):
- typecheck(text, str)
- return text.split('\n')[0]
-
-_change_prolog = """# Change list.
-# Lines beginning with # are ignored.
-# Multi-line values should be indented.
-"""
-
-desc_re = '^(.+: |(tag )?(release|weekly)\.|fix build|undo CL)'
-
-desc_msg = '''Your CL description appears not to use the standard form.
-
-The first line of your change description is conventionally a
-one-line summary of the change, prefixed by the primary affected package,
-and is used as the subject for code review mail; the rest of the description
-elaborates.
-
-Examples:
-
- encoding/rot13: new package
-
- math: add IsInf, IsNaN
-
- net: fix cname in LookupHost
-
- unicode: update to Unicode 5.0.2
-
-'''
-
-def promptyesno(ui, msg):
- return ui.promptchoice(msg, ["&yes", "&no"], 0) == 0
-
-def promptremove(ui, repo, f):
- if promptyesno(ui, "hg remove %s (y/n)?" % (f,)):
- if hg_commands.remove(ui, repo, 'path:'+f) != 0:
- ui.warn("error removing %s" % (f,))
-
-def promptadd(ui, repo, f):
- if promptyesno(ui, "hg add %s (y/n)?" % (f,)):
- if hg_commands.add(ui, repo, 'path:'+f) != 0:
- ui.warn("error adding %s" % (f,))
-
-def EditCL(ui, repo, cl):
- set_status(None) # do not show status
- s = cl.EditorText()
- while True:
- s = ui.edit(s, ui.username())
-
- # We can't trust Mercurial + Python not to die before making the change,
- # so, by popular demand, just scribble the most recent CL edit into
- # $(hg root)/last-change so that if Mercurial does die, people
- # can look there for their work.
- try:
- f = open(repo.root+"/last-change", "w")
- f.write(s)
- f.close()
- except:
- pass
-
- clx, line, err = ParseCL(s, cl.name)
- if err != '':
- if not promptyesno(ui, "error parsing change list: line %d: %s\nre-edit (y/n)?" % (line, err)):
- return "change list not modified"
- continue
-
- # Check description.
- if clx.desc == '':
- if promptyesno(ui, "change list should have a description\nre-edit (y/n)?"):
- continue
- elif re.search('<enter reason for undo>', clx.desc):
- if promptyesno(ui, "change list description omits reason for undo\nre-edit (y/n)?"):
- continue
- elif not re.match(desc_re, clx.desc.split('\n')[0]):
- if promptyesno(ui, desc_msg + "re-edit (y/n)?"):
- continue
-
- # Check file list for files that need to be hg added or hg removed
- # or simply aren't understood.
- pats = ['path:'+f for f in clx.files]
- changed = hg_matchPattern(ui, repo, *pats, modified=True, added=True, removed=True)
- deleted = hg_matchPattern(ui, repo, *pats, deleted=True)
- unknown = hg_matchPattern(ui, repo, *pats, unknown=True)
- ignored = hg_matchPattern(ui, repo, *pats, ignored=True)
- clean = hg_matchPattern(ui, repo, *pats, clean=True)
- files = []
- for f in clx.files:
- if f in changed:
- files.append(f)
- continue
- if f in deleted:
- promptremove(ui, repo, f)
- files.append(f)
- continue
- if f in unknown:
- promptadd(ui, repo, f)
- files.append(f)
- continue
- if f in ignored:
- ui.warn("error: %s is excluded by .hgignore; omitting\n" % (f,))
- continue
- if f in clean:
- ui.warn("warning: %s is listed in the CL but unchanged\n" % (f,))
- files.append(f)
- continue
- p = repo.root + '/' + f
- if os.path.isfile(p):
- ui.warn("warning: %s is a file but not known to hg\n" % (f,))
- files.append(f)
- continue
- if os.path.isdir(p):
- ui.warn("error: %s is a directory, not a file; omitting\n" % (f,))
- continue
- ui.warn("error: %s does not exist; omitting\n" % (f,))
- clx.files = files
-
- cl.desc = clx.desc
- cl.reviewer = clx.reviewer
- cl.cc = clx.cc
- cl.files = clx.files
- cl.private = clx.private
- break
- return ""
-
-# For use by submit, etc. (NOT by change)
-# Get change list number or list of files from command line.
-# If files are given, make a new change list.
-def CommandLineCL(ui, repo, pats, opts, defaultcc=None):
- if len(pats) > 0 and GoodCLName(pats[0]):
- if len(pats) != 1:
- return None, "cannot specify change number and file names"
- if opts.get('message'):
- return None, "cannot use -m with existing CL"
- cl, err = LoadCL(ui, repo, pats[0], web=True)
- if err != "":
- return None, err
- else:
- cl = CL("new")
- cl.local = True
- cl.files = ChangedFiles(ui, repo, pats, taken=Taken(ui, repo))
- if not cl.files:
- return None, "no files changed"
- if opts.get('reviewer'):
- cl.reviewer = Add(cl.reviewer, SplitCommaSpace(opts.get('reviewer')))
- if opts.get('cc'):
- cl.cc = Add(cl.cc, SplitCommaSpace(opts.get('cc')))
- if defaultcc:
- cl.cc = Add(cl.cc, defaultcc)
- if cl.name == "new":
- if opts.get('message'):
- cl.desc = opts.get('message')
- else:
- err = EditCL(ui, repo, cl)
- if err != '':
- return None, err
- return cl, ""
-
-#######################################################################
-# Change list file management
-
-# Return list of changed files in repository that match pats.
-# The patterns came from the command line, so we warn
-# if they have no effect or cannot be understood.
-def ChangedFiles(ui, repo, pats, taken=None):
- taken = taken or {}
- # Run each pattern separately so that we can warn about
- # patterns that didn't do anything useful.
- for p in pats:
- for f in hg_matchPattern(ui, repo, p, unknown=True):
- promptadd(ui, repo, f)
- for f in hg_matchPattern(ui, repo, p, removed=True):
- promptremove(ui, repo, f)
- files = hg_matchPattern(ui, repo, p, modified=True, added=True, removed=True)
- for f in files:
- if f in taken:
- ui.warn("warning: %s already in CL %s\n" % (f, taken[f].name))
- if not files:
- ui.warn("warning: %s did not match any modified files\n" % (p,))
-
- # Again, all at once (eliminates duplicates)
- l = hg_matchPattern(ui, repo, *pats, modified=True, added=True, removed=True)
- l.sort()
- if taken:
- l = Sub(l, taken.keys())
- return l
-
-# Return list of changed files in repository that match pats and still exist.
-def ChangedExistingFiles(ui, repo, pats, opts):
- l = hg_matchPattern(ui, repo, *pats, modified=True, added=True)
- l.sort()
- return l
-
-# Return list of files claimed by existing CLs
-def Taken(ui, repo):
- all = LoadAllCL(ui, repo, web=False)
- taken = {}
- for _, cl in all.items():
- for f in cl.files:
- taken[f] = cl
- return taken
-
-# Return list of changed files that are not claimed by other CLs
-def DefaultFiles(ui, repo, pats):
- return ChangedFiles(ui, repo, pats, taken=Taken(ui, repo))
-
-#######################################################################
-# File format checking.
-
-def CheckFormat(ui, repo, files, just_warn=False):
- set_status("running gofmt")
- CheckGofmt(ui, repo, files, just_warn)
- CheckTabfmt(ui, repo, files, just_warn)
-
-# Check that gofmt run on the list of files does not change them
-def CheckGofmt(ui, repo, files, just_warn):
- files = gofmt_required(files)
- if not files:
- return
- cwd = os.getcwd()
- files = [RelativePath(repo.root + '/' + f, cwd) for f in files]
- files = [f for f in files if os.access(f, 0)]
- if not files:
- return
- try:
- cmd = subprocess.Popen(["gofmt", "-l"] + files, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=sys.platform != "win32")
- cmd.stdin.close()
- except:
- raise hg_util.Abort("gofmt: " + ExceptionDetail())
- data = cmd.stdout.read()
- errors = cmd.stderr.read()
- cmd.wait()
- set_status("done with gofmt")
- if len(errors) > 0:
- ui.warn("gofmt errors:\n" + errors.rstrip() + "\n")
- return
- if len(data) > 0:
- msg = "gofmt needs to format these files (run hg gofmt):\n" + Indent(data, "\t").rstrip()
- if just_warn:
- ui.warn("warning: " + msg + "\n")
- else:
- raise hg_util.Abort(msg)
- return
-
-# Check that *.[chys] files indent using tabs.
-def CheckTabfmt(ui, repo, files, just_warn):
- files = [f for f in files if f.startswith('src/') and re.search(r"\.[chys]$", f) and not re.search(r"\.tab\.[ch]$", f)]
- if not files:
- return
- cwd = os.getcwd()
- files = [RelativePath(repo.root + '/' + f, cwd) for f in files]
- files = [f for f in files if os.access(f, 0)]
- badfiles = []
- for f in files:
- try:
- for line in open(f, 'r'):
- # Four leading spaces is enough to complain about,
- # except that some Plan 9 code uses four spaces as the label indent,
- # so allow that.
- if line.startswith(' ') and not re.match(' [A-Za-z0-9_]+:', line):
- badfiles.append(f)
- break
- except:
- # ignore cannot open file, etc.
- pass
- if len(badfiles) > 0:
- msg = "these files use spaces for indentation (use tabs instead):\n\t" + "\n\t".join(badfiles)
- if just_warn:
- ui.warn("warning: " + msg + "\n")
- else:
- raise hg_util.Abort(msg)
- return
-
-#######################################################################
-# CONTRIBUTORS file parsing
-
-contributorsCache = None
-contributorsURL = None
-
-def ReadContributors(ui, repo):
- global contributorsCache
- if contributorsCache is not None:
- return contributorsCache
-
- try:
- if contributorsURL is not None:
- opening = contributorsURL
- f = urllib2.urlopen(contributorsURL)
- else:
- opening = repo.root + '/CONTRIBUTORS'
- f = open(repo.root + '/CONTRIBUTORS', 'r')
- except:
- ui.write("warning: cannot open %s: %s\n" % (opening, ExceptionDetail()))
- return
-
- contributors = {}
- for line in f:
- # CONTRIBUTORS is a list of lines like:
- # Person <email>
- # Person <email> <alt-email>
- # The first email address is the one used in commit logs.
- if line.startswith('#'):
- continue
- m = re.match(r"([^<>]+\S)\s+(<[^<>\s]+>)((\s+<[^<>\s]+>)*)\s*$", line)
- if m:
- name = m.group(1)
- email = m.group(2)[1:-1]
- contributors[email.lower()] = (name, email)
- for extra in m.group(3).split():
- contributors[extra[1:-1].lower()] = (name, email)
-
- contributorsCache = contributors
- return contributors
-
-def CheckContributor(ui, repo, user=None):
- set_status("checking CONTRIBUTORS file")
- user, userline = FindContributor(ui, repo, user, warn=False)
- if not userline:
- raise hg_util.Abort("cannot find %s in CONTRIBUTORS" % (user,))
- return userline
-
-def FindContributor(ui, repo, user=None, warn=True):
- if not user:
- user = ui.config("ui", "username")
- if not user:
- raise hg_util.Abort("[ui] username is not configured in .hgrc")
- user = user.lower()
- m = re.match(r".*<(.*)>", user)
- if m:
- user = m.group(1)
-
- contributors = ReadContributors(ui, repo)
- if user not in contributors:
- if warn:
- ui.warn("warning: cannot find %s in CONTRIBUTORS\n" % (user,))
- return user, None
-
- user, email = contributors[user]
- return email, "%s <%s>" % (user, email)
-
-#######################################################################
-# Mercurial helper functions.
-# Read http://mercurial.selenic.com/wiki/MercurialApi before writing any of these.
-# We use the ui.pushbuffer/ui.popbuffer + hg_commands.xxx tricks for all interaction
-# with Mercurial. It has proved the most stable as they make changes.
-
-hgversion = hg_util.version()
-
-# We require Mercurial 1.9 and suggest Mercurial 2.0.
-# The details of the scmutil package changed then,
-# so allowing earlier versions would require extra band-aids below.
-# Ubuntu 11.10 ships with Mercurial 1.9.1 as the default version.
-hg_required = "1.9"
-hg_suggested = "2.0"
-
-old_message = """
-
-The code review extension requires Mercurial """+hg_required+""" or newer.
-You are using Mercurial """+hgversion+""".
-
-To install a new Mercurial, use
-
- sudo easy_install mercurial=="""+hg_suggested+"""
-
-or visit http://mercurial.selenic.com/downloads/.
-"""
-
-linux_message = """
-You may need to clear your current Mercurial installation by running:
-
- sudo apt-get remove mercurial mercurial-common
- sudo rm -rf /etc/mercurial
-"""
-
-if hgversion < hg_required:
- msg = old_message
- if os.access("/etc/mercurial", 0):
- msg += linux_message
- raise hg_util.Abort(msg)
-
-from mercurial.hg import clean as hg_clean
-from mercurial import cmdutil as hg_cmdutil
-from mercurial import error as hg_error
-from mercurial import match as hg_match
-from mercurial import node as hg_node
-
-class uiwrap(object):
- def __init__(self, ui):
- self.ui = ui
- ui.pushbuffer()
- self.oldQuiet = ui.quiet
- ui.quiet = True
- self.oldVerbose = ui.verbose
- ui.verbose = False
- def output(self):
- ui = self.ui
- ui.quiet = self.oldQuiet
- ui.verbose = self.oldVerbose
- return ui.popbuffer()
-
-def to_slash(path):
- if sys.platform == "win32":
- return path.replace('\\', '/')
- return path
-
-def hg_matchPattern(ui, repo, *pats, **opts):
- w = uiwrap(ui)
- hg_commands.status(ui, repo, *pats, **opts)
- text = w.output()
- ret = []
- prefix = to_slash(os.path.realpath(repo.root))+'/'
- for line in text.split('\n'):
- f = line.split()
- if len(f) > 1:
- if len(pats) > 0:
- # Given patterns, Mercurial shows relative to cwd
- p = to_slash(os.path.realpath(f[1]))
- if not p.startswith(prefix):
- print >>sys.stderr, "File %s not in repo root %s.\n" % (p, prefix)
- else:
- ret.append(p[len(prefix):])
- else:
- # Without patterns, Mercurial shows relative to root (what we want)
- ret.append(to_slash(f[1]))
- return ret
-
-def hg_heads(ui, repo):
- w = uiwrap(ui)
- hg_commands.heads(ui, repo)
- return w.output()
-
-noise = [
- "",
- "resolving manifests",
- "searching for changes",
- "couldn't find merge tool hgmerge",
- "adding changesets",
- "adding manifests",
- "adding file changes",
- "all local heads known remotely",
-]
-
-def isNoise(line):
- line = str(line)
- for x in noise:
- if line == x:
- return True
- return False
-
-def hg_incoming(ui, repo):
- w = uiwrap(ui)
- ret = hg_commands.incoming(ui, repo, force=False, bundle="")
- if ret and ret != 1:
- raise hg_util.Abort(ret)
- return w.output()
-
-def hg_log(ui, repo, **opts):
- for k in ['date', 'keyword', 'rev', 'user']:
- if not opts.has_key(k):
- opts[k] = ""
- w = uiwrap(ui)
- ret = hg_commands.log(ui, repo, **opts)
- if ret:
- raise hg_util.Abort(ret)
- return w.output()
-
-def hg_outgoing(ui, repo, **opts):
- w = uiwrap(ui)
- ret = hg_commands.outgoing(ui, repo, **opts)
- if ret and ret != 1:
- raise hg_util.Abort(ret)
- return w.output()
-
-def hg_pull(ui, repo, **opts):
- w = uiwrap(ui)
- ui.quiet = False
- ui.verbose = True # for file list
- err = hg_commands.pull(ui, repo, **opts)
- for line in w.output().split('\n'):
- if isNoise(line):
- continue
- if line.startswith('moving '):
- line = 'mv ' + line[len('moving '):]
- if line.startswith('getting ') and line.find(' to ') >= 0:
- line = 'mv ' + line[len('getting '):]
- if line.startswith('getting '):
- line = '+ ' + line[len('getting '):]
- if line.startswith('removing '):
- line = '- ' + line[len('removing '):]
- ui.write(line + '\n')
- return err
-
-def hg_push(ui, repo, **opts):
- w = uiwrap(ui)
- ui.quiet = False
- ui.verbose = True
- err = hg_commands.push(ui, repo, **opts)
- for line in w.output().split('\n'):
- if not isNoise(line):
- ui.write(line + '\n')
- return err
-
-def hg_commit(ui, repo, *pats, **opts):
- return hg_commands.commit(ui, repo, *pats, **opts)
-
-#######################################################################
-# Mercurial precommit hook to disable commit except through this interface.
-
-commit_okay = False
-
-def precommithook(ui, repo, **opts):
- if commit_okay:
- return False # False means okay.
- ui.write("\ncodereview extension enabled; use mail, upload, or submit instead of commit\n\n")
- return True
-
-#######################################################################
-# @clnumber file pattern support
-
-# We replace scmutil.match with the MatchAt wrapper to add the @clnumber pattern.
-
-match_repo = None
-match_ui = None
-match_orig = None
-
-def InstallMatch(ui, repo):
- global match_repo
- global match_ui
- global match_orig
-
- match_ui = ui
- match_repo = repo
-
- from mercurial import scmutil
- match_orig = scmutil.match
- scmutil.match = MatchAt
-
-def MatchAt(ctx, pats=None, opts=None, globbed=False, default='relpath'):
- taken = []
- files = []
- pats = pats or []
- opts = opts or {}
-
- for p in pats:
- if p.startswith('@'):
- taken.append(p)
- clname = p[1:]
- if clname == "default":
- files = DefaultFiles(match_ui, match_repo, [])
- else:
- if not GoodCLName(clname):
- raise hg_util.Abort("invalid CL name " + clname)
- cl, err = LoadCL(match_repo.ui, match_repo, clname, web=False)
- if err != '':
- raise hg_util.Abort("loading CL " + clname + ": " + err)
- if not cl.files:
- raise hg_util.Abort("no files in CL " + clname)
- files = Add(files, cl.files)
- pats = Sub(pats, taken) + ['path:'+f for f in files]
-
- # work-around for http://selenic.com/hg/rev/785bbc8634f8
- if not hasattr(ctx, 'match'):
- ctx = ctx[None]
- return match_orig(ctx, pats=pats, opts=opts, globbed=globbed, default=default)
-
-#######################################################################
-# Commands added by code review extension.
-
-# As of Mercurial 2.1 the commands are all required to return integer
-# exit codes, whereas earlier versions allowed returning arbitrary strings
-# to be printed as errors. We wrap the old functions to make sure we
-# always return integer exit codes now. Otherwise Mercurial dies
-# with a TypeError traceback (unsupported operand type(s) for &: 'str' and 'int').
-# Introduce a Python decorator to convert old functions to the new
-# stricter convention.
-
-def hgcommand(f):
- def wrapped(ui, repo, *pats, **opts):
- err = f(ui, repo, *pats, **opts)
- if type(err) is int:
- return err
- if not err:
- return 0
- raise hg_util.Abort(err)
- wrapped.__doc__ = f.__doc__
- return wrapped
-
-#######################################################################
-# hg change
-
-@hgcommand
-def change(ui, repo, *pats, **opts):
- """create, edit or delete a change list
-
- Create, edit or delete a change list.
- A change list is a group of files to be reviewed and submitted together,
- plus a textual description of the change.
- Change lists are referred to by simple alphanumeric names.
-
- Changes must be reviewed before they can be submitted.
-
- In the absence of options, the change command opens the
- change list for editing in the default editor.
-
- Deleting a change with the -d or -D flag does not affect
- the contents of the files listed in that change. To revert
- the files listed in a change, use
-
- hg revert @123456
-
- before running hg change -d 123456.
- """
-
- if codereview_disabled:
- return codereview_disabled
-
- dirty = {}
- if len(pats) > 0 and GoodCLName(pats[0]):
- name = pats[0]
- if len(pats) != 1:
- return "cannot specify CL name and file patterns"
- pats = pats[1:]
- cl, err = LoadCL(ui, repo, name, web=True)
- if err != '':
- return err
- if not cl.local and (opts["stdin"] or not opts["stdout"]):
- return "cannot change non-local CL " + name
- else:
- name = "new"
- cl = CL("new")
- if repo[None].branch() != "default":
- return "cannot create CL outside default branch; switch with 'hg update default'"
- dirty[cl] = True
- files = ChangedFiles(ui, repo, pats, taken=Taken(ui, repo))
-
- if opts["delete"] or opts["deletelocal"]:
- if opts["delete"] and opts["deletelocal"]:
- return "cannot use -d and -D together"
- flag = "-d"
- if opts["deletelocal"]:
- flag = "-D"
- if name == "new":
- return "cannot use "+flag+" with file patterns"
- if opts["stdin"] or opts["stdout"]:
- return "cannot use "+flag+" with -i or -o"
- if not cl.local:
- return "cannot change non-local CL " + name
- if opts["delete"]:
- if cl.copied_from:
- return "original author must delete CL; hg change -D will remove locally"
- PostMessage(ui, cl.name, "*** Abandoned ***", send_mail=cl.mailed)
- EditDesc(cl.name, closed=True, private=cl.private)
- cl.Delete(ui, repo)
- return
-
- if opts["stdin"]:
- s = sys.stdin.read()
- clx, line, err = ParseCL(s, name)
- if err != '':
- return "error parsing change list: line %d: %s" % (line, err)
- if clx.desc is not None:
- cl.desc = clx.desc;
- dirty[cl] = True
- if clx.reviewer is not None:
- cl.reviewer = clx.reviewer
- dirty[cl] = True
- if clx.cc is not None:
- cl.cc = clx.cc
- dirty[cl] = True
- if clx.files is not None:
- cl.files = clx.files
- dirty[cl] = True
- if clx.private != cl.private:
- cl.private = clx.private
- dirty[cl] = True
-
- if not opts["stdin"] and not opts["stdout"]:
- if name == "new":
- cl.files = files
- err = EditCL(ui, repo, cl)
- if err != "":
- return err
- dirty[cl] = True
-
- for d, _ in dirty.items():
- name = d.name
- d.Flush(ui, repo)
- if name == "new":
- d.Upload(ui, repo, quiet=True)
-
- if opts["stdout"]:
- ui.write(cl.EditorText())
- elif opts["pending"]:
- ui.write(cl.PendingText())
- elif name == "new":
- if ui.quiet:
- ui.write(cl.name)
- else:
- ui.write("CL created: " + cl.url + "\n")
- return
-
-#######################################################################
-# hg code-login (broken?)
-
-@hgcommand
-def code_login(ui, repo, **opts):
- """log in to code review server
-
- Logs in to the code review server, saving a cookie in
- a file in your home directory.
- """
- if codereview_disabled:
- return codereview_disabled
-
- MySend(None)
-
-#######################################################################
-# hg clpatch / undo / release-apply / download
-# All concerned with applying or unapplying patches to the repository.
-
-@hgcommand
-def clpatch(ui, repo, clname, **opts):
- """import a patch from the code review server
-
- Imports a patch from the code review server into the local client.
- If the local client has already modified any of the files that the
- patch modifies, this command will refuse to apply the patch.
-
- Submitting an imported patch will keep the original author's
- name as the Author: line but add your own name to a Committer: line.
- """
- if repo[None].branch() != "default":
- return "cannot run hg clpatch outside default branch"
- return clpatch_or_undo(ui, repo, clname, opts, mode="clpatch")
-
-@hgcommand
-def undo(ui, repo, clname, **opts):
- """undo the effect of a CL
-
- Creates a new CL that undoes an earlier CL.
- After creating the CL, opens the CL text for editing so that
- you can add the reason for the undo to the description.
- """
- if repo[None].branch() != "default":
- return "cannot run hg undo outside default branch"
- return clpatch_or_undo(ui, repo, clname, opts, mode="undo")
-
-@hgcommand
-def release_apply(ui, repo, clname, **opts):
- """apply a CL to the release branch
-
- Creates a new CL copying a previously committed change
- from the main branch to the release branch.
- The current client must either be clean or already be in
- the release branch.
-
- The release branch must be created by starting with a
- clean client, disabling the code review plugin, and running:
-
- hg update weekly.YYYY-MM-DD
- hg branch release-branch.rNN
- hg commit -m 'create release-branch.rNN'
- hg push --new-branch
-
- Then re-enable the code review plugin.
-
- People can test the release branch by running
-
- hg update release-branch.rNN
-
- in a clean client. To return to the normal tree,
-
- hg update default
-
- Move changes since the weekly into the release branch
- using hg release-apply followed by the usual code review
- process and hg submit.
-
- When it comes time to tag the release, record the
- final long-form tag of the release-branch.rNN
- in the *default* branch's .hgtags file. That is, run
-
- hg update default
-
- and then edit .hgtags as you would for a weekly.
-
- """
- c = repo[None]
- if not releaseBranch:
- return "no active release branches"
- if c.branch() != releaseBranch:
- if c.modified() or c.added() or c.removed():
- raise hg_util.Abort("uncommitted local changes - cannot switch branches")
- err = hg_clean(repo, releaseBranch)
- if err:
- return err
- try:
- err = clpatch_or_undo(ui, repo, clname, opts, mode="backport")
- if err:
- raise hg_util.Abort(err)
- except Exception, e:
- hg_clean(repo, "default")
- raise e
- return None
-
-def rev2clname(rev):
- # Extract CL name from revision description.
- # The last line in the description that is a codereview URL is the real one.
- # Earlier lines might be part of the user-written description.
- all = re.findall('(?m)^http://codereview.appspot.com/([0-9]+)$', rev.description())
- if len(all) > 0:
- return all[-1]
- return ""
-
-undoHeader = """undo CL %s / %s
-
-<enter reason for undo>
-
-««« original CL description
-"""
-
-undoFooter = """
-»»»
-"""
-
-backportHeader = """[%s] %s
-
-««« CL %s / %s
-"""
-
-backportFooter = """
-»»»
-"""
-
-# Implementation of clpatch/undo.
-def clpatch_or_undo(ui, repo, clname, opts, mode):
- if codereview_disabled:
- return codereview_disabled
-
- if mode == "undo" or mode == "backport":
- # Find revision in Mercurial repository.
- # Assume CL number is 7+ decimal digits.
- # Otherwise is either change log sequence number (fewer decimal digits),
- # hexadecimal hash, or tag name.
- # Mercurial will fall over long before the change log
- # sequence numbers get to be 7 digits long.
- if re.match('^[0-9]{7,}$', clname):
- found = False
- for r in hg_log(ui, repo, keyword="codereview.appspot.com/"+clname, limit=100, template="{node}\n").split():
- rev = repo[r]
- # Last line with a code review URL is the actual review URL.
- # Earlier ones might be part of the CL description.
- n = rev2clname(rev)
- if n == clname:
- found = True
- break
- if not found:
- return "cannot find CL %s in local repository" % clname
- else:
- rev = repo[clname]
- if not rev:
- return "unknown revision %s" % clname
- clname = rev2clname(rev)
- if clname == "":
- return "cannot find CL name in revision description"
-
- # Create fresh CL and start with patch that would reverse the change.
- vers = hg_node.short(rev.node())
- cl = CL("new")
- desc = str(rev.description())
- if mode == "undo":
- cl.desc = (undoHeader % (clname, vers)) + desc + undoFooter
- else:
- cl.desc = (backportHeader % (releaseBranch, line1(desc), clname, vers)) + desc + undoFooter
- v1 = vers
- v0 = hg_node.short(rev.parents()[0].node())
- if mode == "undo":
- arg = v1 + ":" + v0
- else:
- vers = v0
- arg = v0 + ":" + v1
- patch = RunShell(["hg", "diff", "--git", "-r", arg])
-
- else: # clpatch
- cl, vers, patch, err = DownloadCL(ui, repo, clname)
- if err != "":
- return err
- if patch == emptydiff:
- return "codereview issue %s has no diff" % clname
-
- # find current hg version (hg identify)
- ctx = repo[None]
- parents = ctx.parents()
- id = '+'.join([hg_node.short(p.node()) for p in parents])
-
- # if version does not match the patch version,
- # try to update the patch line numbers.
- if vers != "" and id != vers:
- # "vers in repo" gives the wrong answer
- # on some versions of Mercurial. Instead, do the actual
- # lookup and catch the exception.
- try:
- repo[vers].description()
- except:
- return "local repository is out of date; sync to get %s" % (vers)
- patch1, err = portPatch(repo, patch, vers, id)
- if err != "":
- if not opts["ignore_hgpatch_failure"]:
- return "codereview issue %s is out of date: %s (%s->%s)" % (clname, err, vers, id)
- else:
- patch = patch1
- argv = ["hgpatch"]
- if opts["no_incoming"] or mode == "backport":
- argv += ["--checksync=false"]
- try:
- cmd = subprocess.Popen(argv, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None, close_fds=sys.platform != "win32")
- except:
- return "hgpatch: " + ExceptionDetail() + "\nInstall hgpatch with:\n$ go get code.google.com/p/go.codereview/cmd/hgpatch\n"
-
- out, err = cmd.communicate(patch)
- if cmd.returncode != 0 and not opts["ignore_hgpatch_failure"]:
- return "hgpatch failed"
- cl.local = True
- cl.files = out.strip().split()
- if not cl.files and not opts["ignore_hgpatch_failure"]:
- return "codereview issue %s has no changed files" % clname
- files = ChangedFiles(ui, repo, [])
- extra = Sub(cl.files, files)
- if extra:
- ui.warn("warning: these files were listed in the patch but not changed:\n\t" + "\n\t".join(extra) + "\n")
- cl.Flush(ui, repo)
- if mode == "undo":
- err = EditCL(ui, repo, cl)
- if err != "":
- return "CL created, but error editing: " + err
- cl.Flush(ui, repo)
- else:
- ui.write(cl.PendingText() + "\n")
-
-# portPatch rewrites patch from being a patch against
-# oldver to being a patch against newver.
-def portPatch(repo, patch, oldver, newver):
- lines = patch.splitlines(True) # True = keep \n
- delta = None
- for i in range(len(lines)):
- line = lines[i]
- if line.startswith('--- a/'):
- file = line[6:-1]
- delta = fileDeltas(repo, file, oldver, newver)
- if not delta or not line.startswith('@@ '):
- continue
- # @@ -x,y +z,w @@ means the patch chunk replaces
- # the original file's line numbers x up to x+y with the
- # line numbers z up to z+w in the new file.
- # Find the delta from x in the original to the same
- # line in the current version and add that delta to both
- # x and z.
- m = re.match('@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@', line)
- if not m:
- return None, "error parsing patch line numbers"
- n1, len1, n2, len2 = int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4))
- d, err = lineDelta(delta, n1, len1)
- if err != "":
- return "", err
- n1 += d
- n2 += d
- lines[i] = "@@ -%d,%d +%d,%d @@\n" % (n1, len1, n2, len2)
-
- newpatch = ''.join(lines)
- return newpatch, ""
-
-# fileDelta returns the line number deltas for the given file's
-# changes from oldver to newver.
-# The deltas are a list of (n, len, newdelta) triples that say
-# lines [n, n+len) were modified, and after that range the
-# line numbers are +newdelta from what they were before.
-def fileDeltas(repo, file, oldver, newver):
- cmd = ["hg", "diff", "--git", "-r", oldver + ":" + newver, "path:" + file]
- data = RunShell(cmd, silent_ok=True)
- deltas = []
- for line in data.splitlines():
- m = re.match('@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@', line)
- if not m:
- continue
- n1, len1, n2, len2 = int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4))
- deltas.append((n1, len1, n2+len2-(n1+len1)))
- return deltas
-
-# lineDelta finds the appropriate line number delta to apply to the lines [n, n+len).
-# It returns an error if those lines were rewritten by the patch.
-def lineDelta(deltas, n, len):
- d = 0
- for (old, oldlen, newdelta) in deltas:
- if old >= n+len:
- break
- if old+len > n:
- return 0, "patch and recent changes conflict"
- d = newdelta
- return d, ""
-
-@hgcommand
-def download(ui, repo, clname, **opts):
- """download a change from the code review server
-
- Download prints a description of the given change list
- followed by its diff, downloaded from the code review server.
- """
- if codereview_disabled:
- return codereview_disabled
-
- cl, vers, patch, err = DownloadCL(ui, repo, clname)
- if err != "":
- return err
- ui.write(cl.EditorText() + "\n")
- ui.write(patch + "\n")
- return
-
-#######################################################################
-# hg file
-
-@hgcommand
-def file(ui, repo, clname, pat, *pats, **opts):
- """assign files to or remove files from a change list
-
- Assign files to or (with -d) remove files from a change list.
-
- The -d option only removes files from the change list.
- It does not edit them or remove them from the repository.
- """
- if codereview_disabled:
- return codereview_disabled
-
- pats = tuple([pat] + list(pats))
- if not GoodCLName(clname):
- return "invalid CL name " + clname
-
- dirty = {}
- cl, err = LoadCL(ui, repo, clname, web=False)
- if err != '':
- return err
- if not cl.local:
- return "cannot change non-local CL " + clname
-
- files = ChangedFiles(ui, repo, pats)
-
- if opts["delete"]:
- oldfiles = Intersect(files, cl.files)
- if oldfiles:
- if not ui.quiet:
- ui.status("# Removing files from CL. To undo:\n")
- ui.status("# cd %s\n" % (repo.root))
- for f in oldfiles:
- ui.status("# hg file %s %s\n" % (cl.name, f))
- cl.files = Sub(cl.files, oldfiles)
- cl.Flush(ui, repo)
- else:
- ui.status("no such files in CL")
- return
-
- if not files:
- return "no such modified files"
-
- files = Sub(files, cl.files)
- taken = Taken(ui, repo)
- warned = False
- for f in files:
- if f in taken:
- if not warned and not ui.quiet:
- ui.status("# Taking files from other CLs. To undo:\n")
- ui.status("# cd %s\n" % (repo.root))
- warned = True
- ocl = taken[f]
- if not ui.quiet:
- ui.status("# hg file %s %s\n" % (ocl.name, f))
- if ocl not in dirty:
- ocl.files = Sub(ocl.files, files)
- dirty[ocl] = True
- cl.files = Add(cl.files, files)
- dirty[cl] = True
- for d, _ in dirty.items():
- d.Flush(ui, repo)
- return
-
-#######################################################################
-# hg gofmt
-
-@hgcommand
-def gofmt(ui, repo, *pats, **opts):
- """apply gofmt to modified files
-
- Applies gofmt to the modified files in the repository that match
- the given patterns.
- """
- if codereview_disabled:
- return codereview_disabled
-
- files = ChangedExistingFiles(ui, repo, pats, opts)
- files = gofmt_required(files)
- if not files:
- return "no modified go files"
- cwd = os.getcwd()
- files = [RelativePath(repo.root + '/' + f, cwd) for f in files]
- try:
- cmd = ["gofmt", "-l"]
- if not opts["list"]:
- cmd += ["-w"]
- if os.spawnvp(os.P_WAIT, "gofmt", cmd + files) != 0:
- raise hg_util.Abort("gofmt did not exit cleanly")
- except hg_error.Abort, e:
- raise
- except:
- raise hg_util.Abort("gofmt: " + ExceptionDetail())
- return
-
-def gofmt_required(files):
- return [f for f in files if (not f.startswith('test/') or f.startswith('test/bench/')) and f.endswith('.go')]
-
-#######################################################################
-# hg mail
-
-@hgcommand
-def mail(ui, repo, *pats, **opts):
- """mail a change for review
-
- Uploads a patch to the code review server and then sends mail
- to the reviewer and CC list asking for a review.
- """
- if codereview_disabled:
- return codereview_disabled
-
- cl, err = CommandLineCL(ui, repo, pats, opts, defaultcc=defaultcc)
- if err != "":
- return err
- cl.Upload(ui, repo, gofmt_just_warn=True)
- if not cl.reviewer:
- # If no reviewer is listed, assign the review to defaultcc.
- # This makes sure that it appears in the
- # codereview.appspot.com/user/defaultcc
- # page, so that it doesn't get dropped on the floor.
- if not defaultcc:
- return "no reviewers listed in CL"
- cl.cc = Sub(cl.cc, defaultcc)
- cl.reviewer = defaultcc
- cl.Flush(ui, repo)
-
- if cl.files == []:
- return "no changed files, not sending mail"
-
- cl.Mail(ui, repo)
-
-#######################################################################
-# hg p / hg pq / hg ps / hg pending
-
-@hgcommand
-def ps(ui, repo, *pats, **opts):
- """alias for hg p --short
- """
- opts['short'] = True
- return pending(ui, repo, *pats, **opts)
-
-@hgcommand
-def pq(ui, repo, *pats, **opts):
- """alias for hg p --quick
- """
- opts['quick'] = True
- return pending(ui, repo, *pats, **opts)
-
-@hgcommand
-def pending(ui, repo, *pats, **opts):
- """show pending changes
-
- Lists pending changes followed by a list of unassigned but modified files.
- """
- if codereview_disabled:
- return codereview_disabled
-
- quick = opts.get('quick', False)
- short = opts.get('short', False)
- m = LoadAllCL(ui, repo, web=not quick and not short)
- names = m.keys()
- names.sort()
- for name in names:
- cl = m[name]
- if short:
- ui.write(name + "\t" + line1(cl.desc) + "\n")
- else:
- ui.write(cl.PendingText(quick=quick) + "\n")
-
- if short:
- return
- files = DefaultFiles(ui, repo, [])
- if len(files) > 0:
- s = "Changed files not in any CL:\n"
- for f in files:
- s += "\t" + f + "\n"
- ui.write(s)
-
-#######################################################################
-# hg submit
-
-def need_sync():
- raise hg_util.Abort("local repository out of date; must sync before submit")
-
-@hgcommand
-def submit(ui, repo, *pats, **opts):
- """submit change to remote repository
-
- Submits change to remote repository.
- Bails out if the local repository is not in sync with the remote one.
- """
- if codereview_disabled:
- return codereview_disabled
-
- # We already called this on startup but sometimes Mercurial forgets.
- set_mercurial_encoding_to_utf8()
-
- if not opts["no_incoming"] and hg_incoming(ui, repo):
- need_sync()
-
- cl, err = CommandLineCL(ui, repo, pats, opts, defaultcc=defaultcc)
- if err != "":
- return err
-
- user = None
- if cl.copied_from:
- user = cl.copied_from
- userline = CheckContributor(ui, repo, user)
- typecheck(userline, str)
-
- about = ""
- if cl.reviewer:
- about += "R=" + JoinComma([CutDomain(s) for s in cl.reviewer]) + "\n"
- if opts.get('tbr'):
- tbr = SplitCommaSpace(opts.get('tbr'))
- cl.reviewer = Add(cl.reviewer, tbr)
- about += "TBR=" + JoinComma([CutDomain(s) for s in tbr]) + "\n"
- if cl.cc:
- about += "CC=" + JoinComma([CutDomain(s) for s in cl.cc]) + "\n"
-
- if not cl.reviewer:
- return "no reviewers listed in CL"
-
- if not cl.local:
- return "cannot submit non-local CL"
-
- # upload, to sync current patch and also get change number if CL is new.
- if not cl.copied_from:
- cl.Upload(ui, repo, gofmt_just_warn=True)
-
- # check gofmt for real; allowed upload to warn in order to save CL.
- cl.Flush(ui, repo)
- CheckFormat(ui, repo, cl.files)
-
- about += "%s%s\n" % (server_url_base, cl.name)
-
- if cl.copied_from:
- about += "\nCommitter: " + CheckContributor(ui, repo, None) + "\n"
- typecheck(about, str)
-
- if not cl.mailed and not cl.copied_from: # in case this is TBR
- cl.Mail(ui, repo)
-
- # submit changes locally
- message = cl.desc.rstrip() + "\n\n" + about
- typecheck(message, str)
-
- set_status("pushing " + cl.name + " to remote server")
-
- if hg_outgoing(ui, repo):
- raise hg_util.Abort("local repository corrupt or out-of-phase with remote: found outgoing changes")
-
- old_heads = len(hg_heads(ui, repo).split())
-
- global commit_okay
- commit_okay = True
- ret = hg_commit(ui, repo, *['path:'+f for f in cl.files], message=message, user=userline)
- commit_okay = False
- if ret:
- return "nothing changed"
- node = repo["-1"].node()
- # push to remote; if it fails for any reason, roll back
- try:
- new_heads = len(hg_heads(ui, repo).split())
- if old_heads != new_heads and not (old_heads == 0 and new_heads == 1):
- # Created new head, so we weren't up to date.
- need_sync()
-
- # Push changes to remote. If it works, we're committed. If not, roll back.
- try:
- hg_push(ui, repo)
- except hg_error.Abort, e:
- if e.message.find("push creates new heads") >= 0:
- # Remote repository had changes we missed.
- need_sync()
- raise
- except:
- real_rollback()
- raise
-
- # We're committed. Upload final patch, close review, add commit message.
- changeURL = hg_node.short(node)
- url = ui.expandpath("default")
- m = re.match("(^https?://([^@/]+@)?([^.]+)\.googlecode\.com/hg/?)" + "|" +
- "(^https?://([^@/]+@)?code\.google\.com/p/([^/.]+)(\.[^./]+)?/?)", url)
- if m:
- if m.group(1): # prj.googlecode.com/hg/ case
- changeURL = "http://code.google.com/p/%s/source/detail?r=%s" % (m.group(3), changeURL)
- elif m.group(4) and m.group(7): # code.google.com/p/prj.subrepo/ case
- changeURL = "http://code.google.com/p/%s/source/detail?r=%s&repo=%s" % (m.group(6), changeURL, m.group(7)[1:])
- elif m.group(4): # code.google.com/p/prj/ case
- changeURL = "http://code.google.com/p/%s/source/detail?r=%s" % (m.group(6), changeURL)
- else:
- print >>sys.stderr, "URL: ", url
- else:
- print >>sys.stderr, "URL: ", url
- pmsg = "*** Submitted as " + changeURL + " ***\n\n" + message
-
- # When posting, move reviewers to CC line,
- # so that the issue stops showing up in their "My Issues" page.
- PostMessage(ui, cl.name, pmsg, reviewers="", cc=JoinComma(cl.reviewer+cl.cc))
-
- if not cl.copied_from:
- EditDesc(cl.name, closed=True, private=cl.private)
- cl.Delete(ui, repo)
-
- c = repo[None]
- if c.branch() == releaseBranch and not c.modified() and not c.added() and not c.removed():
- ui.write("switching from %s to default branch.\n" % releaseBranch)
- err = hg_clean(repo, "default")
- if err:
- return err
- return None
-
-#######################################################################
-# hg sync
-
-@hgcommand
-def sync(ui, repo, **opts):
- """synchronize with remote repository
-
- Incorporates recent changes from the remote repository
- into the local repository.
- """
- if codereview_disabled:
- return codereview_disabled
-
- if not opts["local"]:
- err = hg_pull(ui, repo, update=True)
- if err:
- return err
- sync_changes(ui, repo)
-
-def sync_changes(ui, repo):
- # Look through recent change log descriptions to find
- # potential references to http://.*/our-CL-number.
- # Double-check them by looking at the Rietveld log.
- for rev in hg_log(ui, repo, limit=100, template="{node}\n").split():
- desc = repo[rev].description().strip()
- for clname in re.findall('(?m)^http://(?:[^\n]+)/([0-9]+)$', desc):
- if IsLocalCL(ui, repo, clname) and IsRietveldSubmitted(ui, clname, repo[rev].hex()):
- ui.warn("CL %s submitted as %s; closing\n" % (clname, repo[rev]))
- cl, err = LoadCL(ui, repo, clname, web=False)
- if err != "":
- ui.warn("loading CL %s: %s\n" % (clname, err))
- continue
- if not cl.copied_from:
- EditDesc(cl.name, closed=True, private=cl.private)
- cl.Delete(ui, repo)
-
- # Remove files that are not modified from the CLs in which they appear.
- all = LoadAllCL(ui, repo, web=False)
- changed = ChangedFiles(ui, repo, [])
- for cl in all.values():
- extra = Sub(cl.files, changed)
- if extra:
- ui.warn("Removing unmodified files from CL %s:\n" % (cl.name,))
- for f in extra:
- ui.warn("\t%s\n" % (f,))
- cl.files = Sub(cl.files, extra)
- cl.Flush(ui, repo)
- if not cl.files:
- if not cl.copied_from:
- ui.warn("CL %s has no files; delete (abandon) with hg change -d %s\n" % (cl.name, cl.name))
- else:
- ui.warn("CL %s has no files; delete locally with hg change -D %s\n" % (cl.name, cl.name))
- return
-
-#######################################################################
-# hg upload
-
-@hgcommand
-def upload(ui, repo, name, **opts):
- """upload diffs to the code review server
-
- Uploads the current modifications for a given change to the server.
- """
- if codereview_disabled:
- return codereview_disabled
-
- repo.ui.quiet = True
- cl, err = LoadCL(ui, repo, name, web=True)
- if err != "":
- return err
- if not cl.local:
- return "cannot upload non-local change"
- cl.Upload(ui, repo)
- print "%s%s\n" % (server_url_base, cl.name)
- return
-
-#######################################################################
-# Table of commands, supplied to Mercurial for installation.
-
-review_opts = [
- ('r', 'reviewer', '', 'add reviewer'),
- ('', 'cc', '', 'add cc'),
- ('', 'tbr', '', 'add future reviewer'),
- ('m', 'message', '', 'change description (for new change)'),
-]
-
-cmdtable = {
- # The ^ means to show this command in the help text that
- # is printed when running hg with no arguments.
- "^change": (
- change,
- [
- ('d', 'delete', None, 'delete existing change list'),
- ('D', 'deletelocal', None, 'delete locally, but do not change CL on server'),
- ('i', 'stdin', None, 'read change list from standard input'),
- ('o', 'stdout', None, 'print change list to standard output'),
- ('p', 'pending', None, 'print pending summary to standard output'),
- ],
- "[-d | -D] [-i] [-o] change# or FILE ..."
- ),
- "^clpatch": (
- clpatch,
- [
- ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'),
- ('', 'no_incoming', None, 'disable check for incoming changes'),
- ],
- "change#"
- ),
- # Would prefer to call this codereview-login, but then
- # hg help codereview prints the help for this command
- # instead of the help for the extension.
- "code-login": (
- code_login,
- [],
- "",
- ),
- "^download": (
- download,
- [],
- "change#"
- ),
- "^file": (
- file,
- [
- ('d', 'delete', None, 'delete files from change list (but not repository)'),
- ],
- "[-d] change# FILE ..."
- ),
- "^gofmt": (
- gofmt,
- [
- ('l', 'list', None, 'list files that would change, but do not edit them'),
- ],
- "FILE ..."
- ),
- "^pending|p": (
- pending,
- [
- ('s', 'short', False, 'show short result form'),
- ('', 'quick', False, 'do not consult codereview server'),
- ],
- "[FILE ...]"
- ),
- "^ps": (
- ps,
- [],
- "[FILE ...]"
- ),
- "^pq": (
- pq,
- [],
- "[FILE ...]"
- ),
- "^mail": (
- mail,
- review_opts + [
- ] + hg_commands.walkopts,
- "[-r reviewer] [--cc cc] [change# | file ...]"
- ),
- "^release-apply": (
- release_apply,
- [
- ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'),
- ('', 'no_incoming', None, 'disable check for incoming changes'),
- ],
- "change#"
- ),
- # TODO: release-start, release-tag, weekly-tag
- "^submit": (
- submit,
- review_opts + [
- ('', 'no_incoming', None, 'disable initial incoming check (for testing)'),
- ] + hg_commands.walkopts + hg_commands.commitopts + hg_commands.commitopts2,
- "[-r reviewer] [--cc cc] [change# | file ...]"
- ),
- "^sync": (
- sync,
- [
- ('', 'local', None, 'do not pull changes from remote repository')
- ],
- "[--local]",
- ),
- "^undo": (
- undo,
- [
- ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'),
- ('', 'no_incoming', None, 'disable check for incoming changes'),
- ],
- "change#"
- ),
- "^upload": (
- upload,
- [],
- "change#"
- ),
-}
-
-#######################################################################
-# Mercurial extension initialization
-
-def norollback(*pats, **opts):
- """(disabled when using this extension)"""
- raise hg_util.Abort("codereview extension enabled; use undo instead of rollback")
-
-codereview_init = False
-
-def reposetup(ui, repo):
- global codereview_disabled
- global defaultcc
-
- # reposetup gets called both for the local repository
- # and also for any repository we are pulling or pushing to.
- # Only initialize the first time.
- global codereview_init
- if codereview_init:
- return
- codereview_init = True
-
- # Read repository-specific options from lib/codereview/codereview.cfg or codereview.cfg.
- root = ''
- try:
- root = repo.root
- except:
- # Yes, repo might not have root; see issue 959.
- codereview_disabled = 'codereview disabled: repository has no root'
- return
-
- repo_config_path = ''
- p1 = root + '/lib/codereview/codereview.cfg'
- p2 = root + '/codereview.cfg'
- if os.access(p1, os.F_OK):
- repo_config_path = p1
- else:
- repo_config_path = p2
- try:
- f = open(repo_config_path)
- for line in f:
- if line.startswith('defaultcc:'):
- defaultcc = SplitCommaSpace(line[len('defaultcc:'):])
- if line.startswith('contributors:'):
- global contributorsURL
- contributorsURL = line[len('contributors:'):].strip()
- except:
- codereview_disabled = 'codereview disabled: cannot open ' + repo_config_path
- return
-
- remote = ui.config("paths", "default", "")
- if remote.find("://") < 0:
- raise hg_util.Abort("codereview: default path '%s' is not a URL" % (remote,))
-
- InstallMatch(ui, repo)
- RietveldSetup(ui, repo)
-
- # Disable the Mercurial commands that might change the repository.
- # Only commands in this extension are supposed to do that.
- ui.setconfig("hooks", "precommit.codereview", precommithook)
-
- # Rollback removes an existing commit. Don't do that either.
- global real_rollback
- real_rollback = repo.rollback
- repo.rollback = norollback
-
-
-#######################################################################
-# Wrappers around upload.py for interacting with Rietveld
-
-from HTMLParser import HTMLParser
-
-# HTML form parser
-class FormParser(HTMLParser):
- def __init__(self):
- self.map = {}
- self.curtag = None
- self.curdata = None
- HTMLParser.__init__(self)
- def handle_starttag(self, tag, attrs):
- if tag == "input":
- key = None
- value = ''
- for a in attrs:
- if a[0] == 'name':
- key = a[1]
- if a[0] == 'value':
- value = a[1]
- if key is not None:
- self.map[key] = value
- if tag == "textarea":
- key = None
- for a in attrs:
- if a[0] == 'name':
- key = a[1]
- if key is not None:
- self.curtag = key
- self.curdata = ''
- def handle_endtag(self, tag):
- if tag == "textarea" and self.curtag is not None:
- self.map[self.curtag] = self.curdata
- self.curtag = None
- self.curdata = None
- def handle_charref(self, name):
- self.handle_data(unichr(int(name)))
- def handle_entityref(self, name):
- import htmlentitydefs
- if name in htmlentitydefs.entitydefs:
- self.handle_data(htmlentitydefs.entitydefs[name])
- else:
- self.handle_data("&" + name + ";")
- def handle_data(self, data):
- if self.curdata is not None:
- self.curdata += data
-
-def JSONGet(ui, path):
- try:
- data = MySend(path, force_auth=False)
- typecheck(data, str)
- d = fix_json(json.loads(data))
- except:
- ui.warn("JSONGet %s: %s\n" % (path, ExceptionDetail()))
- return None
- return d
-
-# Clean up json parser output to match our expectations:
-# * all strings are UTF-8-encoded str, not unicode.
-# * missing fields are missing, not None,
-# so that d.get("foo", defaultvalue) works.
-def fix_json(x):
- if type(x) in [str, int, float, bool, type(None)]:
- pass
- elif type(x) is unicode:
- x = x.encode("utf-8")
- elif type(x) is list:
- for i in range(len(x)):
- x[i] = fix_json(x[i])
- elif type(x) is dict:
- todel = []
- for k in x:
- if x[k] is None:
- todel.append(k)
- else:
- x[k] = fix_json(x[k])
- for k in todel:
- del x[k]
- else:
- raise hg_util.Abort("unknown type " + str(type(x)) + " in fix_json")
- if type(x) is str:
- x = x.replace('\r\n', '\n')
- return x
-
-def IsRietveldSubmitted(ui, clname, hex):
- dict = JSONGet(ui, "/api/" + clname + "?messages=true")
- if dict is None:
- return False
- for msg in dict.get("messages", []):
- text = msg.get("text", "")
- m = re.match('\*\*\* Submitted as [^*]*?([0-9a-f]+) \*\*\*', text)
- if m is not None and len(m.group(1)) >= 8 and hex.startswith(m.group(1)):
- return True
- return False
-
-def IsRietveldMailed(cl):
- for msg in cl.dict.get("messages", []):
- if msg.get("text", "").find("I'd like you to review this change") >= 0:
- return True
- return False
-
-def DownloadCL(ui, repo, clname):
- set_status("downloading CL " + clname)
- cl, err = LoadCL(ui, repo, clname, web=True)
- if err != "":
- return None, None, None, "error loading CL %s: %s" % (clname, err)
-
- # Find most recent diff
- diffs = cl.dict.get("patchsets", [])
- if not diffs:
- return None, None, None, "CL has no patch sets"
- patchid = diffs[-1]
-
- patchset = JSONGet(ui, "/api/" + clname + "/" + str(patchid))
- if patchset is None:
- return None, None, None, "error loading CL patchset %s/%d" % (clname, patchid)
- if patchset.get("patchset", 0) != patchid:
- return None, None, None, "malformed patchset information"
-
- vers = ""
- msg = patchset.get("message", "").split()
- if len(msg) >= 3 and msg[0] == "diff" and msg[1] == "-r":
- vers = msg[2]
- diff = "/download/issue" + clname + "_" + str(patchid) + ".diff"
-
- diffdata = MySend(diff, force_auth=False)
-
- # Print warning if email is not in CONTRIBUTORS file.
- email = cl.dict.get("owner_email", "")
- if not email:
- return None, None, None, "cannot find owner for %s" % (clname)
- him = FindContributor(ui, repo, email)
- me = FindContributor(ui, repo, None)
- if him == me:
- cl.mailed = IsRietveldMailed(cl)
- else:
- cl.copied_from = email
-
- return cl, vers, diffdata, ""
-
-def MySend(request_path, payload=None,
- content_type="application/octet-stream",
- timeout=None, force_auth=True,
- **kwargs):
- """Run MySend1 maybe twice, because Rietveld is unreliable."""
- try:
- return MySend1(request_path, payload, content_type, timeout, force_auth, **kwargs)
- except Exception, e:
- if type(e) != urllib2.HTTPError or e.code != 500: # only retry on HTTP 500 error
- raise
- print >>sys.stderr, "Loading "+request_path+": "+ExceptionDetail()+"; trying again in 2 seconds."
- time.sleep(2)
- return MySend1(request_path, payload, content_type, timeout, force_auth, **kwargs)
-
-# Like upload.py Send but only authenticates when the
-# redirect is to www.google.com/accounts. This keeps
-# unnecessary redirects from happening during testing.
-def MySend1(request_path, payload=None,
- content_type="application/octet-stream",
- timeout=None, force_auth=True,
- **kwargs):
- """Sends an RPC and returns the response.
-
- Args:
- request_path: The path to send the request to, eg /api/appversion/create.
- payload: The body of the request, or None to send an empty request.
- content_type: The Content-Type header to use.
- timeout: timeout in seconds; default None i.e. no timeout.
- (Note: for large requests on OS X, the timeout doesn't work right.)
- kwargs: Any keyword arguments are converted into query string parameters.
-
- Returns:
- The response body, as a string.
- """
- # TODO: Don't require authentication. Let the server say
- # whether it is necessary.
- global rpc
- if rpc == None:
- rpc = GetRpcServer(upload_options)
- self = rpc
- if not self.authenticated and force_auth:
- self._Authenticate()
- if request_path is None:
- return
-
- old_timeout = socket.getdefaulttimeout()
- socket.setdefaulttimeout(timeout)
- try:
- tries = 0
- while True:
- tries += 1
- args = dict(kwargs)
- url = "http://%s%s" % (self.host, request_path)
- if args:
- url += "?" + urllib.urlencode(args)
- req = self._CreateRequest(url=url, data=payload)
- req.add_header("Content-Type", content_type)
- try:
- f = self.opener.open(req)
- response = f.read()
- f.close()
- # Translate \r\n into \n, because Rietveld doesn't.
- response = response.replace('\r\n', '\n')
- # who knows what urllib will give us
- if type(response) == unicode:
- response = response.encode("utf-8")
- typecheck(response, str)
- return response
- except urllib2.HTTPError, e:
- if tries > 3:
- raise
- elif e.code == 401:
- self._Authenticate()
- elif e.code == 302:
- loc = e.info()["location"]
- if not loc.startswith('https://www.google.com/a') or loc.find('/ServiceLogin') < 0:
- return ''
- self._Authenticate()
- else:
- raise
- finally:
- socket.setdefaulttimeout(old_timeout)
-
-def GetForm(url):
- f = FormParser()
- f.feed(ustr(MySend(url))) # f.feed wants unicode
- f.close()
- # convert back to utf-8 to restore sanity
- m = {}
- for k,v in f.map.items():
- m[k.encode("utf-8")] = v.replace("\r\n", "\n").encode("utf-8")
- return m
-
-def EditDesc(issue, subject=None, desc=None, reviewers=None, cc=None, closed=False, private=False):
- set_status("uploading change to description")
- form_fields = GetForm("/" + issue + "/edit")
- if subject is not None:
- form_fields['subject'] = subject
- if desc is not None:
- form_fields['description'] = desc
- if reviewers is not None:
- form_fields['reviewers'] = reviewers
- if cc is not None:
- form_fields['cc'] = cc
- if closed:
- form_fields['closed'] = "checked"
- if private:
- form_fields['private'] = "checked"
- ctype, body = EncodeMultipartFormData(form_fields.items(), [])
- response = MySend("/" + issue + "/edit", body, content_type=ctype)
- if response != "":
- print >>sys.stderr, "Error editing description:\n" + "Sent form: \n", form_fields, "\n", response
- sys.exit(2)
-
-def PostMessage(ui, issue, message, reviewers=None, cc=None, send_mail=True, subject=None):
- set_status("uploading message")
- form_fields = GetForm("/" + issue + "/publish")
- if reviewers is not None:
- form_fields['reviewers'] = reviewers
- if cc is not None:
- form_fields['cc'] = cc
- if send_mail:
- form_fields['send_mail'] = "checked"
- else:
- del form_fields['send_mail']
- if subject is not None:
- form_fields['subject'] = subject
- form_fields['message'] = message
-
- form_fields['message_only'] = '1' # Don't include draft comments
- if reviewers is not None or cc is not None:
- form_fields['message_only'] = '' # Must set '' in order to override cc/reviewer
- ctype = "applications/x-www-form-urlencoded"
- body = urllib.urlencode(form_fields)
- response = MySend("/" + issue + "/publish", body, content_type=ctype)
- if response != "":
- print response
- sys.exit(2)
-
-class opt(object):
- pass
-
-def RietveldSetup(ui, repo):
- global force_google_account
- global rpc
- global server
- global server_url_base
- global upload_options
- global verbosity
-
- if not ui.verbose:
- verbosity = 0
-
- # Config options.
- x = ui.config("codereview", "server")
- if x is not None:
- server = x
-
- # TODO(rsc): Take from ui.username?
- email = None
- x = ui.config("codereview", "email")
- if x is not None:
- email = x
-
- server_url_base = "http://" + server + "/"
-
- testing = ui.config("codereview", "testing")
- force_google_account = ui.configbool("codereview", "force_google_account", False)
-
- upload_options = opt()
- upload_options.email = email
- upload_options.host = None
- upload_options.verbose = 0
- upload_options.description = None
- upload_options.description_file = None
- upload_options.reviewers = None
- upload_options.cc = None
- upload_options.message = None
- upload_options.issue = None
- upload_options.download_base = False
- upload_options.revision = None
- upload_options.send_mail = False
- upload_options.vcs = None
- upload_options.server = server
- upload_options.save_cookies = True
-
- if testing:
- upload_options.save_cookies = False
- upload_options.email = "test@example.com"
-
- rpc = None
-
- global releaseBranch
- tags = repo.branchtags().keys()
- if 'release-branch.go10' in tags:
- # NOTE(rsc): This tags.sort is going to get the wrong
- # answer when comparing release-branch.go9 with
- # release-branch.go10. It will be a while before we care.
- raise hg_util.Abort('tags.sort needs to be fixed for release-branch.go10')
- tags.sort()
- for t in tags:
- if t.startswith('release-branch.go'):
- releaseBranch = t
-
-#######################################################################
-# http://codereview.appspot.com/static/upload.py, heavily edited.
-
-#!/usr/bin/env python
-#
-# Copyright 2007 Google Inc.
-#
-# 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.
-
-"""Tool for uploading diffs from a version control system to the codereview app.
-
-Usage summary: upload.py [options] [-- diff_options]
-
-Diff options are passed to the diff command of the underlying system.
-
-Supported version control systems:
- Git
- Mercurial
- Subversion
-
-It is important for Git/Mercurial users to specify a tree/node/branch to diff
-against by using the '--rev' option.
-"""
-# This code is derived from appcfg.py in the App Engine SDK (open source),
-# and from ASPN recipe #146306.
-
-import cookielib
-import getpass
-import logging
-import mimetypes
-import optparse
-import os
-import re
-import socket
-import subprocess
-import sys
-import urllib
-import urllib2
-import urlparse
-
-# The md5 module was deprecated in Python 2.5.
-try:
- from hashlib import md5
-except ImportError:
- from md5 import md5
-
-try:
- import readline
-except ImportError:
- pass
-
-# The logging verbosity:
-# 0: Errors only.
-# 1: Status messages.
-# 2: Info logs.
-# 3: Debug logs.
-verbosity = 1
-
-# Max size of patch or base file.
-MAX_UPLOAD_SIZE = 900 * 1024
-
-# whitelist for non-binary filetypes which do not start with "text/"
-# .mm (Objective-C) shows up as application/x-freemind on my Linux box.
-TEXT_MIMETYPES = [
- 'application/javascript',
- 'application/x-javascript',
- 'application/x-freemind'
-]
-
-def GetEmail(prompt):
- """Prompts the user for their email address and returns it.
-
- The last used email address is saved to a file and offered up as a suggestion
- to the user. If the user presses enter without typing in anything the last
- used email address is used. If the user enters a new address, it is saved
- for next time we prompt.
-
- """
- last_email_file_name = os.path.expanduser("~/.last_codereview_email_address")
- last_email = ""
- if os.path.exists(last_email_file_name):
- try:
- last_email_file = open(last_email_file_name, "r")
- last_email = last_email_file.readline().strip("\n")
- last_email_file.close()
- prompt += " [%s]" % last_email
- except IOError, e:
- pass
- email = raw_input(prompt + ": ").strip()
- if email:
- try:
- last_email_file = open(last_email_file_name, "w")
- last_email_file.write(email)
- last_email_file.close()
- except IOError, e:
- pass
- else:
- email = last_email
- return email
-
-
-def StatusUpdate(msg):
- """Print a status message to stdout.
-
- If 'verbosity' is greater than 0, print the message.
-
- Args:
- msg: The string to print.
- """
- if verbosity > 0:
- print msg
-
-
-def ErrorExit(msg):
- """Print an error message to stderr and exit."""
- print >>sys.stderr, msg
- sys.exit(1)
-
-
-class ClientLoginError(urllib2.HTTPError):
- """Raised to indicate there was an error authenticating with ClientLogin."""
-
- def __init__(self, url, code, msg, headers, args):
- urllib2.HTTPError.__init__(self, url, code, msg, headers, None)
- self.args = args
- self.reason = args["Error"]
-
-
-class AbstractRpcServer(object):
- """Provides a common interface for a simple RPC server."""
-
- def __init__(self, host, auth_function, host_override=None, extra_headers={}, save_cookies=False):
- """Creates a new HttpRpcServer.
-
- Args:
- host: The host to send requests to.
- auth_function: A function that takes no arguments and returns an
- (email, password) tuple when called. Will be called if authentication
- is required.
- host_override: The host header to send to the server (defaults to host).
- extra_headers: A dict of extra headers to append to every request.
- save_cookies: If True, save the authentication cookies to local disk.
- If False, use an in-memory cookiejar instead. Subclasses must
- implement this functionality. Defaults to False.
- """
- self.host = host
- self.host_override = host_override
- self.auth_function = auth_function
- self.authenticated = False
- self.extra_headers = extra_headers
- self.save_cookies = save_cookies
- self.opener = self._GetOpener()
- if self.host_override:
- logging.info("Server: %s; Host: %s", self.host, self.host_override)
- else:
- logging.info("Server: %s", self.host)
-
- def _GetOpener(self):
- """Returns an OpenerDirector for making HTTP requests.
-
- Returns:
- A urllib2.OpenerDirector object.
- """
- raise NotImplementedError()
-
- def _CreateRequest(self, url, data=None):
- """Creates a new urllib request."""
- logging.debug("Creating request for: '%s' with payload:\n%s", url, data)
- req = urllib2.Request(url, data=data)
- if self.host_override:
- req.add_header("Host", self.host_override)
- for key, value in self.extra_headers.iteritems():
- req.add_header(key, value)
- return req
-
- def _GetAuthToken(self, email, password):
- """Uses ClientLogin to authenticate the user, returning an auth token.
-
- Args:
- email: The user's email address
- password: The user's password
-
- Raises:
- ClientLoginError: If there was an error authenticating with ClientLogin.
- HTTPError: If there was some other form of HTTP error.
-
- Returns:
- The authentication token returned by ClientLogin.
- """
- account_type = "GOOGLE"
- if self.host.endswith(".google.com") and not force_google_account:
- # Needed for use inside Google.
- account_type = "HOSTED"
- req = self._CreateRequest(
- url="https://www.google.com/accounts/ClientLogin",
- data=urllib.urlencode({
- "Email": email,
- "Passwd": password,
- "service": "ah",
- "source": "rietveld-codereview-upload",
- "accountType": account_type,
- }),
- )
- try:
- response = self.opener.open(req)
- response_body = response.read()
- response_dict = dict(x.split("=") for x in response_body.split("\n") if x)
- return response_dict["Auth"]
- except urllib2.HTTPError, e:
- if e.code == 403:
- body = e.read()
- response_dict = dict(x.split("=", 1) for x in body.split("\n") if x)
- raise ClientLoginError(req.get_full_url(), e.code, e.msg, e.headers, response_dict)
- else:
- raise
-
- def _GetAuthCookie(self, auth_token):
- """Fetches authentication cookies for an authentication token.
-
- Args:
- auth_token: The authentication token returned by ClientLogin.
-
- Raises:
- HTTPError: If there was an error fetching the authentication cookies.
- """
- # This is a dummy value to allow us to identify when we're successful.
- continue_location = "http://localhost/"
- args = {"continue": continue_location, "auth": auth_token}
- req = self._CreateRequest("http://%s/_ah/login?%s" % (self.host, urllib.urlencode(args)))
- try:
- response = self.opener.open(req)
- except urllib2.HTTPError, e:
- response = e
- if (response.code != 302 or
- response.info()["location"] != continue_location):
- raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg, response.headers, response.fp)
- self.authenticated = True
-
- def _Authenticate(self):
- """Authenticates the user.
-
- The authentication process works as follows:
- 1) We get a username and password from the user
- 2) We use ClientLogin to obtain an AUTH token for the user
- (see http://code.google.com/apis/accounts/AuthForInstalledApps.html).
- 3) We pass the auth token to /_ah/login on the server to obtain an
- authentication cookie. If login was successful, it tries to redirect
- us to the URL we provided.
-
- If we attempt to access the upload API without first obtaining an
- authentication cookie, it returns a 401 response (or a 302) and
- directs us to authenticate ourselves with ClientLogin.
- """
- for i in range(3):
- credentials = self.auth_function()
- try:
- auth_token = self._GetAuthToken(credentials[0], credentials[1])
- except ClientLoginError, e:
- if e.reason == "BadAuthentication":
- print >>sys.stderr, "Invalid username or password."
- continue
- if e.reason == "CaptchaRequired":
- print >>sys.stderr, (
- "Please go to\n"
- "https://www.google.com/accounts/DisplayUnlockCaptcha\n"
- "and verify you are a human. Then try again.")
- break
- if e.reason == "NotVerified":
- print >>sys.stderr, "Account not verified."
- break
- if e.reason == "TermsNotAgreed":
- print >>sys.stderr, "User has not agreed to TOS."
- break
- if e.reason == "AccountDeleted":
- print >>sys.stderr, "The user account has been deleted."
- break
- if e.reason == "AccountDisabled":
- print >>sys.stderr, "The user account has been disabled."
- break
- if e.reason == "ServiceDisabled":
- print >>sys.stderr, "The user's access to the service has been disabled."
- break
- if e.reason == "ServiceUnavailable":
- print >>sys.stderr, "The service is not available; try again later."
- break
- raise
- self._GetAuthCookie(auth_token)
- return
-
- def Send(self, request_path, payload=None,
- content_type="application/octet-stream",
- timeout=None,
- **kwargs):
- """Sends an RPC and returns the response.
-
- Args:
- request_path: The path to send the request to, eg /api/appversion/create.
- payload: The body of the request, or None to send an empty request.
- content_type: The Content-Type header to use.
- timeout: timeout in seconds; default None i.e. no timeout.
- (Note: for large requests on OS X, the timeout doesn't work right.)
- kwargs: Any keyword arguments are converted into query string parameters.
-
- Returns:
- The response body, as a string.
- """
- # TODO: Don't require authentication. Let the server say
- # whether it is necessary.
- if not self.authenticated:
- self._Authenticate()
-
- old_timeout = socket.getdefaulttimeout()
- socket.setdefaulttimeout(timeout)
- try:
- tries = 0
- while True:
- tries += 1
- args = dict(kwargs)
- url = "http://%s%s" % (self.host, request_path)
- if args:
- url += "?" + urllib.urlencode(args)
- req = self._CreateRequest(url=url, data=payload)
- req.add_header("Content-Type", content_type)
- try:
- f = self.opener.open(req)
- response = f.read()
- f.close()
- return response
- except urllib2.HTTPError, e:
- if tries > 3:
- raise
- elif e.code == 401 or e.code == 302:
- self._Authenticate()
- else:
- raise
- finally:
- socket.setdefaulttimeout(old_timeout)
-
-
-class HttpRpcServer(AbstractRpcServer):
- """Provides a simplified RPC-style interface for HTTP requests."""
-
- def _Authenticate(self):
- """Save the cookie jar after authentication."""
- super(HttpRpcServer, self)._Authenticate()
- if self.save_cookies:
- StatusUpdate("Saving authentication cookies to %s" % self.cookie_file)
- self.cookie_jar.save()
-
- def _GetOpener(self):
- """Returns an OpenerDirector that supports cookies and ignores redirects.
-
- Returns:
- A urllib2.OpenerDirector object.
- """
- opener = urllib2.OpenerDirector()
- opener.add_handler(urllib2.ProxyHandler())
- opener.add_handler(urllib2.UnknownHandler())
- opener.add_handler(urllib2.HTTPHandler())
- opener.add_handler(urllib2.HTTPDefaultErrorHandler())
- opener.add_handler(urllib2.HTTPSHandler())
- opener.add_handler(urllib2.HTTPErrorProcessor())
- if self.save_cookies:
- self.cookie_file = os.path.expanduser("~/.codereview_upload_cookies_" + server)
- self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file)
- if os.path.exists(self.cookie_file):
- try:
- self.cookie_jar.load()
- self.authenticated = True
- StatusUpdate("Loaded authentication cookies from %s" % self.cookie_file)
- except (cookielib.LoadError, IOError):
- # Failed to load cookies - just ignore them.
- pass
- else:
- # Create an empty cookie file with mode 600
- fd = os.open(self.cookie_file, os.O_CREAT, 0600)
- os.close(fd)
- # Always chmod the cookie file
- os.chmod(self.cookie_file, 0600)
- else:
- # Don't save cookies across runs of update.py.
- self.cookie_jar = cookielib.CookieJar()
- opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar))
- return opener
-
-
-def GetRpcServer(options):
- """Returns an instance of an AbstractRpcServer.
-
- Returns:
- A new AbstractRpcServer, on which RPC calls can be made.
- """
-
- rpc_server_class = HttpRpcServer
-
- def GetUserCredentials():
- """Prompts the user for a username and password."""
- # Disable status prints so they don't obscure the password prompt.
- global global_status
- st = global_status
- global_status = None
-
- email = options.email
- if email is None:
- email = GetEmail("Email (login for uploading to %s)" % options.server)
- password = getpass.getpass("Password for %s: " % email)
-
- # Put status back.
- global_status = st
- return (email, password)
-
- # If this is the dev_appserver, use fake authentication.
- host = (options.host or options.server).lower()
- if host == "localhost" or host.startswith("localhost:"):
- email = options.email
- if email is None:
- email = "test@example.com"
- logging.info("Using debug user %s. Override with --email" % email)
- server = rpc_server_class(
- options.server,
- lambda: (email, "password"),
- host_override=options.host,
- extra_headers={"Cookie": 'dev_appserver_login="%s:False"' % email},
- save_cookies=options.save_cookies)
- # Don't try to talk to ClientLogin.
- server.authenticated = True
- return server
-
- return rpc_server_class(options.server, GetUserCredentials,
- host_override=options.host, save_cookies=options.save_cookies)
-
-
-def EncodeMultipartFormData(fields, files):
- """Encode form fields for multipart/form-data.
-
- Args:
- fields: A sequence of (name, value) elements for regular form fields.
- files: A sequence of (name, filename, value) elements for data to be
- uploaded as files.
- Returns:
- (content_type, body) ready for httplib.HTTP instance.
-
- Source:
- http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
- """
- BOUNDARY = '-M-A-G-I-C---B-O-U-N-D-A-R-Y-'
- CRLF = '\r\n'
- lines = []
- for (key, value) in fields:
- typecheck(key, str)
- typecheck(value, str)
- lines.append('--' + BOUNDARY)
- lines.append('Content-Disposition: form-data; name="%s"' % key)
- lines.append('')
- lines.append(value)
- for (key, filename, value) in files:
- typecheck(key, str)
- typecheck(filename, str)
- typecheck(value, str)
- lines.append('--' + BOUNDARY)
- lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
- lines.append('Content-Type: %s' % GetContentType(filename))
- lines.append('')
- lines.append(value)
- lines.append('--' + BOUNDARY + '--')
- lines.append('')
- body = CRLF.join(lines)
- content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
- return content_type, body
-
-
-def GetContentType(filename):
- """Helper to guess the content-type from the filename."""
- return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
-
-
-# Use a shell for subcommands on Windows to get a PATH search.
-use_shell = sys.platform.startswith("win")
-
-def RunShellWithReturnCode(command, print_output=False,
- universal_newlines=True, env=os.environ):
- """Executes a command and returns the output from stdout and the return code.
-
- Args:
- command: Command to execute.
- print_output: If True, the output is printed to stdout.
- If False, both stdout and stderr are ignored.
- universal_newlines: Use universal_newlines flag (default: True).
-
- Returns:
- Tuple (output, return code)
- """
- logging.info("Running %s", command)
- p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- shell=use_shell, universal_newlines=universal_newlines, env=env)
- if print_output:
- output_array = []
- while True:
- line = p.stdout.readline()
- if not line:
- break
- print line.strip("\n")
- output_array.append(line)
- output = "".join(output_array)
- else:
- output = p.stdout.read()
- p.wait()
- errout = p.stderr.read()
- if print_output and errout:
- print >>sys.stderr, errout
- p.stdout.close()
- p.stderr.close()
- return output, p.returncode
-
-
-def RunShell(command, silent_ok=False, universal_newlines=True,
- print_output=False, env=os.environ):
- data, retcode = RunShellWithReturnCode(command, print_output, universal_newlines, env)
- if retcode:
- ErrorExit("Got error status from %s:\n%s" % (command, data))
- if not silent_ok and not data:
- ErrorExit("No output from %s" % command)
- return data
-
-
-class VersionControlSystem(object):
- """Abstract base class providing an interface to the VCS."""
-
- def __init__(self, options):
- """Constructor.
-
- Args:
- options: Command line options.
- """
- self.options = options
-
- def GenerateDiff(self, args):
- """Return the current diff as a string.
-
- Args:
- args: Extra arguments to pass to the diff command.
- """
- raise NotImplementedError(
- "abstract method -- subclass %s must override" % self.__class__)
-
- def GetUnknownFiles(self):
- """Return a list of files unknown to the VCS."""
- raise NotImplementedError(
- "abstract method -- subclass %s must override" % self.__class__)
-
- def CheckForUnknownFiles(self):
- """Show an "are you sure?" prompt if there are unknown files."""
- unknown_files = self.GetUnknownFiles()
- if unknown_files:
- print "The following files are not added to version control:"
- for line in unknown_files:
- print line
- prompt = "Are you sure to continue?(y/N) "
- answer = raw_input(prompt).strip()
- if answer != "y":
- ErrorExit("User aborted")
-
- def GetBaseFile(self, filename):
- """Get the content of the upstream version of a file.
-
- Returns:
- A tuple (base_content, new_content, is_binary, status)
- base_content: The contents of the base file.
- new_content: For text files, this is empty. For binary files, this is
- the contents of the new file, since the diff output won't contain
- information to reconstruct the current file.
- is_binary: True iff the file is binary.
- status: The status of the file.
- """
-
- raise NotImplementedError(
- "abstract method -- subclass %s must override" % self.__class__)
-
-
- def GetBaseFiles(self, diff):
- """Helper that calls GetBase file for each file in the patch.
-
- Returns:
- A dictionary that maps from filename to GetBaseFile's tuple. Filenames
- are retrieved based on lines that start with "Index:" or
- "Property changes on:".
- """
- files = {}
- for line in diff.splitlines(True):
- if line.startswith('Index:') or line.startswith('Property changes on:'):
- unused, filename = line.split(':', 1)
- # On Windows if a file has property changes its filename uses '\'
- # instead of '/'.
- filename = to_slash(filename.strip())
- files[filename] = self.GetBaseFile(filename)
- return files
-
-
- def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options,
- files):
- """Uploads the base files (and if necessary, the current ones as well)."""
-
- def UploadFile(filename, file_id, content, is_binary, status, is_base):
- """Uploads a file to the server."""
- set_status("uploading " + filename)
- file_too_large = False
- if is_base:
- type = "base"
- else:
- type = "current"
- if len(content) > MAX_UPLOAD_SIZE:
- print ("Not uploading the %s file for %s because it's too large." %
- (type, filename))
- file_too_large = True
- content = ""
- checksum = md5(content).hexdigest()
- if options.verbose > 0 and not file_too_large:
- print "Uploading %s file for %s" % (type, filename)
- url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id)
- form_fields = [
- ("filename", filename),
- ("status", status),
- ("checksum", checksum),
- ("is_binary", str(is_binary)),
- ("is_current", str(not is_base)),
- ]
- if file_too_large:
- form_fields.append(("file_too_large", "1"))
- if options.email:
- form_fields.append(("user", options.email))
- ctype, body = EncodeMultipartFormData(form_fields, [("data", filename, content)])
- response_body = rpc_server.Send(url, body, content_type=ctype)
- if not response_body.startswith("OK"):
- StatusUpdate(" --> %s" % response_body)
- sys.exit(1)
-
- # Don't want to spawn too many threads, nor do we want to
- # hit Rietveld too hard, or it will start serving 500 errors.
- # When 8 works, it's no better than 4, and sometimes 8 is
- # too many for Rietveld to handle.
- MAX_PARALLEL_UPLOADS = 4
-
- sema = threading.BoundedSemaphore(MAX_PARALLEL_UPLOADS)
- upload_threads = []
- finished_upload_threads = []
-
- class UploadFileThread(threading.Thread):
- def __init__(self, args):
- threading.Thread.__init__(self)
- self.args = args
- def run(self):
- UploadFile(*self.args)
- finished_upload_threads.append(self)
- sema.release()
-
- def StartUploadFile(*args):
- sema.acquire()
- while len(finished_upload_threads) > 0:
- t = finished_upload_threads.pop()
- upload_threads.remove(t)
- t.join()
- t = UploadFileThread(args)
- upload_threads.append(t)
- t.start()
-
- def WaitForUploads():
- for t in upload_threads:
- t.join()
-
- patches = dict()
- [patches.setdefault(v, k) for k, v in patch_list]
- for filename in patches.keys():
- base_content, new_content, is_binary, status = files[filename]
- file_id_str = patches.get(filename)
- if file_id_str.find("nobase") != -1:
- base_content = None
- file_id_str = file_id_str[file_id_str.rfind("_") + 1:]
- file_id = int(file_id_str)
- if base_content != None:
- StartUploadFile(filename, file_id, base_content, is_binary, status, True)
- if new_content != None:
- StartUploadFile(filename, file_id, new_content, is_binary, status, False)
- WaitForUploads()
-
- def IsImage(self, filename):
- """Returns true if the filename has an image extension."""
- mimetype = mimetypes.guess_type(filename)[0]
- if not mimetype:
- return False
- return mimetype.startswith("image/")
-
- def IsBinary(self, filename):
- """Returns true if the guessed mimetyped isnt't in text group."""
- mimetype = mimetypes.guess_type(filename)[0]
- if not mimetype:
- return False # e.g. README, "real" binaries usually have an extension
- # special case for text files which don't start with text/
- if mimetype in TEXT_MIMETYPES:
- return False
- return not mimetype.startswith("text/")
-
-
-class FakeMercurialUI(object):
- def __init__(self):
- self.quiet = True
- self.output = ''
-
- def write(self, *args, **opts):
- self.output += ' '.join(args)
- def copy(self):
- return self
- def status(self, *args, **opts):
- pass
-
- def formatter(self, topic, opts):
- from mercurial.formatter import plainformatter
- return plainformatter(self, topic, opts)
-
- def readconfig(self, *args, **opts):
- pass
- def expandpath(self, *args, **opts):
- return global_ui.expandpath(*args, **opts)
- def configitems(self, *args, **opts):
- return global_ui.configitems(*args, **opts)
- def config(self, *args, **opts):
- return global_ui.config(*args, **opts)
-
-use_hg_shell = False # set to True to shell out to hg always; slower
-
-class MercurialVCS(VersionControlSystem):
- """Implementation of the VersionControlSystem interface for Mercurial."""
-
- def __init__(self, options, ui, repo):
- super(MercurialVCS, self).__init__(options)
- self.ui = ui
- self.repo = repo
- self.status = None
- # Absolute path to repository (we can be in a subdir)
- self.repo_dir = os.path.normpath(repo.root)
- # Compute the subdir
- cwd = os.path.normpath(os.getcwd())
- assert cwd.startswith(self.repo_dir)
- self.subdir = cwd[len(self.repo_dir):].lstrip(r"\/")
- if self.options.revision:
- self.base_rev = self.options.revision
- else:
- mqparent, err = RunShellWithReturnCode(['hg', 'log', '--rev', 'qparent', '--template={node}'])
- if not err and mqparent != "":
- self.base_rev = mqparent
- else:
- out = RunShell(["hg", "parents", "-q"], silent_ok=True).strip()
- if not out:
- # No revisions; use 0 to mean a repository with nothing.
- out = "0:0"
- self.base_rev = out.split(':')[1].strip()
- def _GetRelPath(self, filename):
- """Get relative path of a file according to the current directory,
- given its logical path in the repo."""
- assert filename.startswith(self.subdir), (filename, self.subdir)
- return filename[len(self.subdir):].lstrip(r"\/")
-
- def GenerateDiff(self, extra_args):
- # If no file specified, restrict to the current subdir
- extra_args = extra_args or ["."]
- cmd = ["hg", "diff", "--git", "-r", self.base_rev] + extra_args
- data = RunShell(cmd, silent_ok=True)
- svndiff = []
- filecount = 0
- for line in data.splitlines():
- m = re.match("diff --git a/(\S+) b/(\S+)", line)
- if m:
- # Modify line to make it look like as it comes from svn diff.
- # With this modification no changes on the server side are required
- # to make upload.py work with Mercurial repos.
- # NOTE: for proper handling of moved/copied files, we have to use
- # the second filename.
- filename = m.group(2)
- svndiff.append("Index: %s" % filename)
- svndiff.append("=" * 67)
- filecount += 1
- logging.info(line)
- else:
- svndiff.append(line)
- if not filecount:
- ErrorExit("No valid patches found in output from hg diff")
- return "\n".join(svndiff) + "\n"
-
- def GetUnknownFiles(self):
- """Return a list of files unknown to the VCS."""
- args = []
- status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."],
- silent_ok=True)
- unknown_files = []
- for line in status.splitlines():
- st, fn = line.split(" ", 1)
- if st == "?":
- unknown_files.append(fn)
- return unknown_files
-
- def get_hg_status(self, rev, path):
- # We'd like to use 'hg status -C path', but that is buggy
- # (see http://mercurial.selenic.com/bts/issue3023).
- # Instead, run 'hg status -C' without a path
- # and skim the output for the path we want.
- if self.status is None:
- if use_hg_shell:
- out = RunShell(["hg", "status", "-C", "--rev", rev])
- else:
- fui = FakeMercurialUI()
- ret = hg_commands.status(fui, self.repo, *[], **{'rev': [rev], 'copies': True})
- if ret:
- raise hg_util.Abort(ret)
- out = fui.output
- self.status = out.splitlines()
- for i in range(len(self.status)):
- # line is
- # A path
- # M path
- # etc
- line = to_slash(self.status[i])
- if line[2:] == path:
- if i+1 < len(self.status) and self.status[i+1][:2] == ' ':
- return self.status[i:i+2]
- return self.status[i:i+1]
- raise hg_util.Abort("no status for " + path)
-
- def GetBaseFile(self, filename):
- set_status("inspecting " + filename)
- # "hg status" and "hg cat" both take a path relative to the current subdir
- # rather than to the repo root, but "hg diff" has given us the full path
- # to the repo root.
- base_content = ""
- new_content = None
- is_binary = False
- oldrelpath = relpath = self._GetRelPath(filename)
- out = self.get_hg_status(self.base_rev, relpath)
- status, what = out[0].split(' ', 1)
- if len(out) > 1 and status == "A" and what == relpath:
- oldrelpath = out[1].strip()
- status = "M"
- if ":" in self.base_rev:
- base_rev = self.base_rev.split(":", 1)[0]
- else:
- base_rev = self.base_rev
- if status != "A":
- if use_hg_shell:
- base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], silent_ok=True)
- else:
- base_content = str(self.repo[base_rev][oldrelpath].data())
- is_binary = "\0" in base_content # Mercurial's heuristic
- if status != "R":
- new_content = open(relpath, "rb").read()
- is_binary = is_binary or "\0" in new_content
- if is_binary and base_content and use_hg_shell:
- # Fetch again without converting newlines
- base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath],
- silent_ok=True, universal_newlines=False)
- if not is_binary or not self.IsImage(relpath):
- new_content = None
- return base_content, new_content, is_binary, status
-
-
-# NOTE: The SplitPatch function is duplicated in engine.py, keep them in sync.
-def SplitPatch(data):
- """Splits a patch into separate pieces for each file.
-
- Args:
- data: A string containing the output of svn diff.
-
- Returns:
- A list of 2-tuple (filename, text) where text is the svn diff output
- pertaining to filename.
- """
- patches = []
- filename = None
- diff = []
- for line in data.splitlines(True):
- new_filename = None
- if line.startswith('Index:'):
- unused, new_filename = line.split(':', 1)
- new_filename = new_filename.strip()
- elif line.startswith('Property changes on:'):
- unused, temp_filename = line.split(':', 1)
- # When a file is modified, paths use '/' between directories, however
- # when a property is modified '\' is used on Windows. Make them the same
- # otherwise the file shows up twice.
- temp_filename = to_slash(temp_filename.strip())
- if temp_filename != filename:
- # File has property changes but no modifications, create a new diff.
- new_filename = temp_filename
- if new_filename:
- if filename and diff:
- patches.append((filename, ''.join(diff)))
- filename = new_filename
- diff = [line]
- continue
- if diff is not None:
- diff.append(line)
- if filename and diff:
- patches.append((filename, ''.join(diff)))
- return patches
-
-
-def UploadSeparatePatches(issue, rpc_server, patchset, data, options):
- """Uploads a separate patch for each file in the diff output.
-
- Returns a list of [patch_key, filename] for each file.
- """
- patches = SplitPatch(data)
- rv = []
- for patch in patches:
- set_status("uploading patch for " + patch[0])
- if len(patch[1]) > MAX_UPLOAD_SIZE:
- print ("Not uploading the patch for " + patch[0] +
- " because the file is too large.")
- continue
- form_fields = [("filename", patch[0])]
- if not options.download_base:
- form_fields.append(("content_upload", "1"))
- files = [("data", "data.diff", patch[1])]
- ctype, body = EncodeMultipartFormData(form_fields, files)
- url = "/%d/upload_patch/%d" % (int(issue), int(patchset))
- print "Uploading patch for " + patch[0]
- response_body = rpc_server.Send(url, body, content_type=ctype)
- lines = response_body.splitlines()
- if not lines or lines[0] != "OK":
- StatusUpdate(" --> %s" % response_body)
- sys.exit(1)
- rv.append([lines[1], patch[0]])
- return rv
diff --git a/lib/git/commit-msg.hook b/lib/git/commit-msg.hook
new file mode 100755
index 0000000..985016b
--- /dev/null
+++ b/lib/git/commit-msg.hook
@@ -0,0 +1,104 @@
+#!/bin/sh
+# From Gerrit Code Review 2.2.1
+#
+# Part of Gerrit Code Review (http://code.google.com/p/gerrit/)
+#
+# Copyright (C) 2009 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.
+#
+
+CHANGE_ID_AFTER="Bug|Issue"
+MSG="$1"
+
+# Check for, and add if missing, a unique Change-Id
+#
+add_ChangeId() {
+ clean_message=`sed -e '
+ /^diff --git a\/.*/{
+ s///
+ q
+ }
+ /^Signed-off-by:/d
+ /^#/d
+ ' "$MSG" | git stripspace`
+ if test -z "$clean_message"
+ then
+ return
+ fi
+
+ if grep -i '^Change-Id:' "$MSG" >/dev/null
+ then
+ return
+ fi
+
+ id=`_gen_ChangeId`
+ perl -e '
+ $MSG = shift;
+ $id = shift;
+ $CHANGE_ID_AFTER = shift;
+
+ undef $/;
+ open(I, $MSG); $_ = <I>; close I;
+ s|^diff --git a/.*||ms;
+ s|^#.*$||mg;
+ exit unless $_;
+
+ @message = split /\n/;
+ $haveFooter = 0;
+ $startFooter = @message;
+ for($line = @message - 1; $line >= 0; $line--) {
+ $_ = $message[$line];
+
+ if (/^[a-zA-Z0-9-]+:/ && !m,^[a-z0-9-]+://,) {
+ $haveFooter++;
+ next;
+ }
+ next if /^[ []/;
+ $startFooter = $line if ($haveFooter && /^\r?$/);
+ last;
+ }
+
+ @footer = @message[$startFooter+1..@message];
+ @message = @message[0..$startFooter];
+ push(@footer, "") unless @footer;
+
+ for ($line = 0; $line < @footer; $line++) {
+ $_ = $footer[$line];
+ next if /^($CHANGE_ID_AFTER):/i;
+ last;
+ }
+ splice(@footer, $line, 0, "Change-Id: I$id");
+
+ $_ = join("\n", @message, @footer);
+ open(O, ">$MSG"); print O; close O;
+ ' "$MSG" "$id" "$CHANGE_ID_AFTER"
+}
+_gen_ChangeIdInput() {
+ echo "tree `git write-tree`"
+ if parent=`git rev-parse HEAD^0 2>/dev/null`
+ then
+ echo "parent $parent"
+ fi
+ echo "author `git var GIT_AUTHOR_IDENT`"
+ echo "committer `git var GIT_COMMITTER_IDENT`"
+ echo
+ printf '%s' "$clean_message"
+}
+_gen_ChangeId() {
+ _gen_ChangeIdInput |
+ git hash-object -t commit --stdin
+}
+
+
+add_ChangeId
diff --git a/libre2.symbols b/libre2.symbols
index 1a9cae3..8308b64 100644
--- a/libre2.symbols
+++ b/libre2.symbols
@@ -6,10 +6,11 @@
# re2::StringPiece*
_ZN3re211StringPiece*;
_ZNK3re211StringPiece*;
- # operator<<(std::ostream&, re2::StringPiece const&)
- _ZlsRSoRKN3re211StringPieceE;
+ # re2::operator<<*
+ _ZN3re2ls*;
# re2::FilteredRE2*
_ZN3re211FilteredRE2*;
+ _ZNK3re211FilteredRE2*;
local:
*;
};
diff --git a/libre2.symbols.darwin b/libre2.symbols.darwin
index 93eab3e..31e8c52 100644
--- a/libre2.symbols.darwin
+++ b/libre2.symbols.darwin
@@ -5,7 +5,8 @@ __ZNK3re23RE2*
# re2::StringPiece*
__ZN3re211StringPiece*
__ZNK3re211StringPiece*
-# operator<<(std::ostream&, re2::StringPiece const&)
-__ZlsRSoRKN3re211StringPieceE
+# re2::operator<<*
+__ZN3re2ls*
# re2::FilteredRE2*
__ZN3re211FilteredRE2*
+__ZNK3re211FilteredRE2*
diff --git a/re2.pc b/re2.pc
new file mode 100644
index 0000000..d66cf51
--- /dev/null
+++ b/re2.pc
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@
+libdir=@libdir@
+
+Name: re2
+Description: RE2 is a fast, safe, thread-friendly regular expression engine.
+Version: 0.0.0
+Cflags: -std=c++11 -pthread -I${includedir}
+Libs: -pthread -L${libdir} -lre2
diff --git a/re2/Makefile b/re2/Makefile
deleted file mode 100644
index 8b13789..0000000
--- a/re2/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/re2/bitmap256.h b/re2/bitmap256.h
new file mode 100644
index 0000000..1abae99
--- /dev/null
+++ b/re2/bitmap256.h
@@ -0,0 +1,113 @@
+// Copyright 2016 The RE2 Authors. All Rights Reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifndef RE2_BITMAP256_H_
+#define RE2_BITMAP256_H_
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+#include <stdint.h>
+#include <string.h>
+
+#include "util/util.h"
+#include "util/logging.h"
+
+namespace re2 {
+
+class Bitmap256 {
+ public:
+ Bitmap256() {
+ memset(words_, 0, sizeof words_);
+ }
+
+ // Tests the bit with index c.
+ bool Test(int c) const {
+ DCHECK_GE(c, 0);
+ DCHECK_LE(c, 255);
+
+ return (words_[c / 64] & (1ULL << (c % 64))) != 0;
+ }
+
+ // Sets the bit with index c.
+ void Set(int c) {
+ DCHECK_GE(c, 0);
+ DCHECK_LE(c, 255);
+
+ words_[c / 64] |= (1ULL << (c % 64));
+ }
+
+ // Finds the next non-zero bit with index >= c.
+ // Returns -1 if no such bit exists.
+ int FindNextSetBit(int c) const;
+
+ private:
+ // Finds the least significant non-zero bit in n.
+ static int FindLSBSet(uint64_t n) {
+ DCHECK_NE(n, 0);
+
+#if defined(__GNUC__)
+ return __builtin_ctzll(n);
+#elif defined(_MSC_VER) && defined(_M_X64)
+ unsigned long c;
+ _BitScanForward64(&c, n);
+ return static_cast<int>(c);
+#elif defined(_MSC_VER) && defined(_M_IX86)
+ unsigned long c;
+ if (static_cast<uint32_t>(n) != 0) {
+ _BitScanForward(&c, static_cast<uint32_t>(n));
+ return static_cast<int>(c);
+ } else {
+ _BitScanForward(&c, static_cast<uint32_t>(n >> 32));
+ return static_cast<int>(c) + 32;
+ }
+#else
+ int c = 63;
+ for (int shift = 1 << 5; shift != 0; shift >>= 1) {
+ uint64_t word = n << shift;
+ if (word != 0) {
+ n = word;
+ c -= shift;
+ }
+ }
+ return c;
+#endif
+ }
+
+ uint64_t words_[4];
+};
+
+int Bitmap256::FindNextSetBit(int c) const {
+ DCHECK_GE(c, 0);
+ DCHECK_LE(c, 255);
+
+ // Check the word that contains the bit. Mask out any lower bits.
+ int i = c / 64;
+ uint64_t word = words_[i] & (~0ULL << (c % 64));
+ if (word != 0)
+ return (i * 64) + FindLSBSet(word);
+
+ // Check any following words.
+ i++;
+ switch (i) {
+ case 1:
+ if (words_[1] != 0)
+ return (1 * 64) + FindLSBSet(words_[1]);
+ FALLTHROUGH_INTENDED;
+ case 2:
+ if (words_[2] != 0)
+ return (2 * 64) + FindLSBSet(words_[2]);
+ FALLTHROUGH_INTENDED;
+ case 3:
+ if (words_[3] != 0)
+ return (3 * 64) + FindLSBSet(words_[3]);
+ FALLTHROUGH_INTENDED;
+ default:
+ return -1;
+ }
+}
+
+} // namespace re2
+
+#endif // RE2_BITMAP256_H_
diff --git a/re2/bitstate.cc b/re2/bitstate.cc
index 518d642..6e1b44c 100644
--- a/re2/bitstate.cc
+++ b/re2/bitstate.cc
@@ -17,6 +17,13 @@
// SearchBitState is a fast replacement for the NFA code on small
// regexps and texts when SearchOnePass cannot be used.
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <utility>
+
+#include "util/logging.h"
+#include "util/pod_array.h"
#include "re2/prog.h"
#include "re2/regexp.h"
@@ -31,7 +38,6 @@ struct Job {
class BitState {
public:
explicit BitState(Prog* prog);
- ~BitState();
// The usual Search prototype.
// Can only call Search once per BitState.
@@ -42,7 +48,7 @@ class BitState {
private:
inline bool ShouldVisit(int id, const char* p);
void Push(int id, const char* p, int arg);
- bool GrowStack();
+ void GrowStack();
bool TrySearch(int id, const char* p);
// Search parameters
@@ -52,20 +58,15 @@ class BitState {
bool anchored_; // whether search is anchored at text.begin()
bool longest_; // whether search wants leftmost-longest match
bool endmatch_; // whether match must end at text.end()
- StringPiece *submatch_; // submatches to fill in
+ StringPiece* submatch_; // submatches to fill in
int nsubmatch_; // # of submatches to fill in
// Search state
- const char** cap_; // capture registers
- int ncap_;
-
static const int VisitedBits = 32;
- uint32 *visited_; // bitmap: (Inst*, char*) pairs already backtracked
- int nvisited_; // # of words in bitmap
-
- Job *job_; // stack of text positions to explore
- int njob_;
- int maxjob_;
+ PODArray<uint32_t> visited_; // bitmap: (Inst*, char*) pairs visited
+ PODArray<const char*> cap_; // capture registers
+ PODArray<Job> job_; // stack of text positions to explore
+ int njob_; // stack size
};
BitState::BitState(Prog* prog)
@@ -75,26 +76,15 @@ BitState::BitState(Prog* prog)
endmatch_(false),
submatch_(NULL),
nsubmatch_(0),
- cap_(NULL),
- ncap_(0),
- visited_(NULL),
- nvisited_(0),
- job_(NULL),
- njob_(0),
- maxjob_(0) {
-}
-
-BitState::~BitState() {
- delete[] visited_;
- delete[] job_;
- delete[] cap_;
+ njob_(0) {
}
// Should the search visit the pair ip, p?
// If so, remember that it was visited so that the next time,
// we don't repeat the visit.
bool BitState::ShouldVisit(int id, const char* p) {
- uint n = id * (text_.size() + 1) + (p - text_.begin());
+ int n = id * static_cast<int>(text_.size()+1) +
+ static_cast<int>(p-text_.begin());
if (visited_[n/VisitedBits] & (1 << (n & (VisitedBits-1))))
return false;
visited_[n/VisitedBits] |= 1 << (n & (VisitedBits-1));
@@ -102,25 +92,22 @@ bool BitState::ShouldVisit(int id, const char* p) {
}
// Grow the stack.
-bool BitState::GrowStack() {
- // VLOG(0) << "Reallocate.";
- maxjob_ *= 2;
- Job* newjob = new Job[maxjob_];
- memmove(newjob, job_, njob_*sizeof job_[0]);
- delete[] job_;
- job_ = newjob;
- if (njob_ >= maxjob_) {
- LOG(DFATAL) << "Job stack overflow.";
- return false;
- }
- return true;
+void BitState::GrowStack() {
+ PODArray<Job> tmp(2*job_.size());
+ memmove(tmp.data(), job_.data(), njob_*sizeof job_[0]);
+ job_ = std::move(tmp);
}
// Push the triple (id, p, arg) onto the stack, growing it if necessary.
void BitState::Push(int id, const char* p, int arg) {
- if (njob_ >= maxjob_) {
- if (!GrowStack())
+ if (njob_ >= job_.size()) {
+ GrowStack();
+ if (njob_ >= job_.size()) {
+ LOG(DFATAL) << "GrowStack() failed: "
+ << "njob_ = " << njob_ << ", "
+ << "job_.size() = " << job_.size();
return;
+ }
}
int op = prog_->inst(id)->opcode();
if (op == kInstFail)
@@ -141,6 +128,7 @@ void BitState::Push(int id, const char* p, int arg) {
// Return whether it succeeded.
bool BitState::TrySearch(int id0, const char* p0) {
bool matched = false;
+ bool inaltmatch = false;
const char* end = text_.end();
njob_ = 0;
Push(id0, p0, 0);
@@ -159,81 +147,86 @@ bool BitState::TrySearch(int id0, const char* p0) {
// would have, but we avoid the stack
// manipulation.
if (0) {
+ Next:
+ // If the Match of a non-greedy AltMatch failed,
+ // we stop ourselves from trying the ByteRange,
+ // which would steer us off the short circuit.
+ if (prog_->inst(id)->last() || inaltmatch)
+ continue;
+ id++;
+
CheckAndLoop:
if (!ShouldVisit(id, p))
continue;
}
// Visit ip, p.
- // VLOG(0) << "Job: " << ip->id() << " "
- // << (p - text_.begin()) << " " << arg;
Prog::Inst* ip = prog_->inst(id);
switch (ip->opcode()) {
- case kInstFail:
default:
LOG(DFATAL) << "Unexpected opcode: " << ip->opcode() << " arg " << arg;
return false;
- case kInstAlt:
- // Cannot just
- // Push(ip->out1(), p, 0);
- // Push(ip->out(), p, 0);
- // If, during the processing of ip->out(), we encounter
- // ip->out1() via another path, we want to process it then.
- // Pushing it here will inhibit that. Instead, re-push
- // ip with arg==1 as a reminder to push ip->out1() later.
+ case kInstFail:
+ continue;
+
+ case kInstAltMatch:
switch (arg) {
case 0:
+ inaltmatch = true;
Push(id, p, 1); // come back when we're done
+
+ // One opcode is ByteRange; the other leads to Match
+ // (possibly via Nop or Capture).
+ if (ip->greedy(prog_)) {
+ // out1 is the match
+ Push(ip->out1(), p, 0);
+ id = ip->out1();
+ p = end;
+ goto CheckAndLoop;
+ }
+ // out is the match - non-greedy
+ Push(ip->out(), end, 0);
id = ip->out();
goto CheckAndLoop;
case 1:
- // Finished ip->out(); try ip->out1().
- arg = 0;
- id = ip->out1();
- goto CheckAndLoop;
+ inaltmatch = false;
+ continue;
}
- LOG(DFATAL) << "Bad arg in kInstCapture: " << arg;
+ LOG(DFATAL) << "Bad arg in kInstAltMatch: " << arg;
continue;
- case kInstAltMatch:
- // One opcode is byte range; the other leads to match.
- if (ip->greedy(prog_)) {
- // out1 is the match
- Push(ip->out1(), p, 0);
- id = ip->out1();
- p = end;
- goto CheckAndLoop;
- }
- // out is the match - non-greedy
- Push(ip->out(), end, 0);
- id = ip->out();
- goto CheckAndLoop;
-
case kInstByteRange: {
int c = -1;
if (p < end)
c = *p & 0xFF;
- if (ip->Matches(c)) {
- id = ip->out();
- p++;
- goto CheckAndLoop;
- }
- continue;
+ if (!ip->Matches(c))
+ goto Next;
+
+ if (!ip->last())
+ Push(id+1, p, 0); // try the next when we're done
+ id = ip->out();
+ p++;
+ goto CheckAndLoop;
}
case kInstCapture:
switch (arg) {
case 0:
- if (0 <= ip->cap() && ip->cap() < ncap_) {
+ if (!ip->last())
+ Push(id+1, p, 0); // try the next when we're done
+
+ if (0 <= ip->cap() && ip->cap() < cap_.size()) {
// Capture p to register, but save old value.
Push(id, cap_[ip->cap()], 1); // come back when we're done
cap_[ip->cap()] = p;
}
+
// Continue on.
id = ip->out();
goto CheckAndLoop;
+
case 1:
// Finished ip->out(); restore the old value.
cap_[ip->cap()] = p;
@@ -244,19 +237,23 @@ bool BitState::TrySearch(int id0, const char* p0) {
case kInstEmptyWidth:
if (ip->empty() & ~Prog::EmptyFlags(context_, p))
- continue;
+ goto Next;
+
+ if (!ip->last())
+ Push(id+1, p, 0); // try the next when we're done
id = ip->out();
goto CheckAndLoop;
case kInstNop:
+ if (!ip->last())
+ Push(id+1, p, 0); // try the next when we're done
id = ip->out();
goto CheckAndLoop;
case kInstMatch: {
if (endmatch_ && p != text_.end())
- continue;
+ goto Next;
- // VLOG(0) << "Found match.";
// We found a match. If the caller doesn't care
// where the match is, no point going further.
if (nsubmatch_ == 0)
@@ -270,7 +267,9 @@ bool BitState::TrySearch(int id0, const char* p0) {
if (submatch_[0].data() == NULL ||
(longest_ && p > submatch_[0].end())) {
for (int i = 0; i < nsubmatch_; i++)
- submatch_[i] = StringPiece(cap_[2*i], cap_[2*i+1] - cap_[2*i]);
+ submatch_[i] =
+ StringPiece(cap_[2 * i],
+ static_cast<size_t>(cap_[2 * i + 1] - cap_[2 * i]));
}
// If going for first match, we're done.
@@ -282,7 +281,7 @@ bool BitState::TrySearch(int id0, const char* p0) {
return true;
// Otherwise, continue on in hope of a longer match.
- continue;
+ goto Next;
}
}
}
@@ -308,22 +307,22 @@ bool BitState::Search(const StringPiece& text, const StringPiece& context,
submatch_ = submatch;
nsubmatch_ = nsubmatch;
for (int i = 0; i < nsubmatch_; i++)
- submatch_[i] = NULL;
+ submatch_[i] = StringPiece();
// Allocate scratch space.
- nvisited_ = (prog_->size() * (text.size()+1) + VisitedBits-1) / VisitedBits;
- visited_ = new uint32[nvisited_];
- memset(visited_, 0, nvisited_*sizeof visited_[0]);
- // VLOG(0) << "nvisited_ = " << nvisited_;
+ int nvisited = prog_->size() * static_cast<int>(text.size()+1);
+ nvisited = (nvisited + VisitedBits-1) / VisitedBits;
+ visited_ = PODArray<uint32_t>(nvisited);
+ memset(visited_.data(), 0, nvisited*sizeof visited_[0]);
- ncap_ = 2*nsubmatch;
- if (ncap_ < 2)
- ncap_ = 2;
- cap_ = new const char*[ncap_];
- memset(cap_, 0, ncap_*sizeof cap_[0]);
+ int ncap = 2*nsubmatch;
+ if (ncap < 2)
+ ncap = 2;
+ cap_ = PODArray<const char*>(ncap);
+ memset(cap_.data(), 0, ncap*sizeof cap_[0]);
- maxjob_ = 256;
- job_ = new Job[maxjob_];
+ // When sizeof(Job) == 16, we start with a nice round 4KiB. :)
+ job_ = PODArray<Job>(256);
// Anchored search must start at text.begin().
if (anchored_) {
@@ -338,6 +337,14 @@ bool BitState::Search(const StringPiece& text, const StringPiece& context,
// but we are not clearing visited_ between calls to TrySearch,
// so no work is duplicated and it ends up still being linear.
for (const char* p = text.begin(); p <= text.end(); p++) {
+ // Try to use memchr to find the first byte quickly.
+ int fb = prog_->first_byte();
+ if (fb >= 0 && p < text.end() && (p[0] & 0xFF) != fb) {
+ p = reinterpret_cast<const char*>(memchr(p, fb, text.end() - p));
+ if (p == NULL)
+ p = text.end();
+ }
+
cap_[0] = p;
if (TrySearch(prog_->start(), p)) // Match must be leftmost; done.
return true;
diff --git a/re2/compile.cc b/re2/compile.cc
index 9cddb71..3f8e0cc 100644
--- a/re2/compile.cc
+++ b/re2/compile.cc
@@ -8,6 +8,14 @@
// This file's external interface is just Regexp::CompileToProg.
// The Compiler class defined in this file is private.
+#include <stdint.h>
+#include <string.h>
+#include <unordered_map>
+#include <utility>
+
+#include "util/logging.h"
+#include "util/pod_array.h"
+#include "util/utf.h"
#include "re2/prog.h"
#include "re2/re2.h"
#include "re2/regexp.h"
@@ -28,14 +36,14 @@ namespace re2 {
// is always the fail instruction, which never appears on a list.
struct PatchList {
- uint32 p;
+ uint32_t p;
// Returns patch list containing just p.
- static PatchList Mk(uint32 p);
+ static PatchList Mk(uint32_t p);
// Patches all the entries on l to have value v.
// Caller must not ever use patch list again.
- static void Patch(Prog::Inst *inst0, PatchList l, uint32 v);
+ static void Patch(Prog::Inst *inst0, PatchList l, uint32_t v);
// Deref returns the next pointer pointed at by p.
static PatchList Deref(Prog::Inst *inst0, PatchList l);
@@ -44,10 +52,10 @@ struct PatchList {
static PatchList Append(Prog::Inst *inst0, PatchList l1, PatchList l2);
};
-static PatchList nullPatchList;
+static PatchList nullPatchList = { 0 };
// Returns patch list containing just p.
-PatchList PatchList::Mk(uint32 p) {
+PatchList PatchList::Mk(uint32_t p) {
PatchList l;
l.p = p;
return l;
@@ -64,7 +72,7 @@ PatchList PatchList::Deref(Prog::Inst* inst0, PatchList l) {
}
// Patches all the entries on l to have value v.
-void PatchList::Patch(Prog::Inst *inst0, PatchList l, uint32 val) {
+void PatchList::Patch(Prog::Inst *inst0, PatchList l, uint32_t val) {
while (l.p != 0) {
Prog::Inst* ip = &inst0[l.p>>1];
if (l.p&1) {
@@ -103,20 +111,17 @@ PatchList PatchList::Append(Prog::Inst* inst0, PatchList l1, PatchList l2) {
// Compiled program fragment.
struct Frag {
- uint32 begin;
+ uint32_t begin;
PatchList end;
- explicit Frag(LinkerInitialized) {}
Frag() : begin(0) { end.p = 0; } // needed so Frag can go in vector
- Frag(uint32 begin, PatchList end) : begin(begin), end(end) {}
+ Frag(uint32_t begin, PatchList end) : begin(begin), end(end) {}
};
-static Frag kNullFrag(LINKER_INITIALIZED);
-
// Input encodings.
enum Encoding {
kEncodingUTF8 = 1, // UTF-8 (0-10FFFF)
- kEncodingLatin1, // Latin1 (0-FF)
+ kEncodingLatin1, // Latin-1 (0-FF)
};
class Compiler : public Regexp::Walker<Frag> {
@@ -128,12 +133,11 @@ class Compiler : public Regexp::Walker<Frag> {
// Caller is responsible for deleting Prog when finished with it.
// If reversed is true, compiles for walking over the input
// string backward (reverses all concatenations).
- static Prog *Compile(Regexp* re, bool reversed, int64 max_mem);
+ static Prog *Compile(Regexp* re, bool reversed, int64_t max_mem);
// Compiles alternation of all the re to a new Prog.
// Each re has a match with an id equal to its index in the vector.
- static Prog* CompileSet(const RE2::Options& options, RE2::Anchor anchor,
- Regexp* re);
+ static Prog* CompileSet(Regexp* re, RE2::Anchor anchor, int64_t max_mem);
// Interface for Regexp::Walker, which helps traverse the Regexp.
// The walk is purely post-recursive: given the machines for the
@@ -165,7 +169,7 @@ class Compiler : public Regexp::Walker<Frag> {
Frag NoMatch();
// Returns a fragment that matches the empty string.
- Frag Match(int32 id);
+ Frag Match(int32_t id);
// Returns a no-op fragment.
Frag Nop();
@@ -181,9 +185,6 @@ class Compiler : public Regexp::Walker<Frag> {
// Returns -1 if no more instructions are available.
int AllocInst(int n);
- // Deletes unused instructions.
- void Trim();
-
// Rune range compiler.
// Begins a new alternation.
@@ -196,19 +197,35 @@ class Compiler : public Regexp::Walker<Frag> {
void Add_80_10ffff();
// New suffix that matches the byte range lo-hi, then goes to next.
- int RuneByteSuffix(uint8 lo, uint8 hi, bool foldcase, int next);
- int UncachedRuneByteSuffix(uint8 lo, uint8 hi, bool foldcase, int next);
+ int UncachedRuneByteSuffix(uint8_t lo, uint8_t hi, bool foldcase, int next);
+ int CachedRuneByteSuffix(uint8_t lo, uint8_t hi, bool foldcase, int next);
+
+ // Returns true iff the suffix is cached.
+ bool IsCachedRuneByteSuffix(int id);
// Adds a suffix to alternation.
void AddSuffix(int id);
+ // Adds a suffix to the trie starting from the given root node.
+ // Returns zero iff allocating an instruction fails. Otherwise, returns
+ // the current root node, which might be different from what was given.
+ int AddSuffixRecursive(int root, int id);
+
+ // Finds the trie node for the given suffix. Returns a Frag in order to
+ // distinguish between pointing at the root node directly (end.p == 0)
+ // and pointing at an Alt's out1 or out (end.p&1 == 1 or 0, respectively).
+ Frag FindByteRange(int root, int id);
+
+ // Compares two ByteRanges and returns true iff they are equal.
+ bool ByteRangeEqual(int id1, int id2);
+
// Returns the alternation of all the added suffixes.
Frag EndRange();
// Single rune.
Frag Literal(Rune r, bool foldcase);
- void Setup(Regexp::ParseFlags, int64, RE2::Anchor);
+ void Setup(Regexp::ParseFlags, int64_t, RE2::Anchor);
Prog* Finish();
// Returns .* where dot = any byte
@@ -220,20 +237,19 @@ class Compiler : public Regexp::Walker<Frag> {
Encoding encoding_; // Input encoding
bool reversed_; // Should program run backward over text?
- int max_inst_; // Maximum number of instructions.
-
- Prog::Inst* inst_; // Pointer to first instruction.
- int inst_len_; // Number of instructions used.
- int inst_cap_; // Number of instructions allocated.
+ PODArray<Prog::Inst> inst_;
+ int ninst_; // Number of instructions used.
+ int max_ninst_; // Maximum number of instructions.
- int64 max_mem_; // Total memory budget.
+ int64_t max_mem_; // Total memory budget.
- map<uint64, int> rune_cache_;
+ std::unordered_map<uint64_t, int> rune_cache_;
Frag rune_range_;
RE2::Anchor anchor_; // anchor mode for RE2::Set
- DISALLOW_EVIL_CONSTRUCTORS(Compiler);
+ Compiler(const Compiler&) = delete;
+ Compiler& operator=(const Compiler&) = delete;
};
Compiler::Compiler() {
@@ -241,53 +257,41 @@ Compiler::Compiler() {
failed_ = false;
encoding_ = kEncodingUTF8;
reversed_ = false;
- inst_ = NULL;
- inst_len_ = 0;
- inst_cap_ = 0;
- max_inst_ = 1; // make AllocInst for fail instruction okay
+ ninst_ = 0;
+ max_ninst_ = 1; // make AllocInst for fail instruction okay
max_mem_ = 0;
int fail = AllocInst(1);
inst_[fail].InitFail();
- max_inst_ = 0; // Caller must change
+ max_ninst_ = 0; // Caller must change
}
Compiler::~Compiler() {
delete prog_;
- delete[] inst_;
}
int Compiler::AllocInst(int n) {
- if (failed_ || inst_len_ + n > max_inst_) {
+ if (failed_ || ninst_ + n > max_ninst_) {
failed_ = true;
return -1;
}
- if (inst_len_ + n > inst_cap_) {
- if (inst_cap_ == 0)
- inst_cap_ = 8;
- while (inst_len_ + n > inst_cap_)
- inst_cap_ *= 2;
- Prog::Inst* ip = new Prog::Inst[inst_cap_];
- memmove(ip, inst_, inst_len_ * sizeof ip[0]);
- memset(ip + inst_len_, 0, (inst_cap_ - inst_len_) * sizeof ip[0]);
- delete[] inst_;
- inst_ = ip;
+ if (ninst_ + n > inst_.size()) {
+ int cap = inst_.size();
+ if (cap == 0)
+ cap = 8;
+ while (ninst_ + n > cap)
+ cap *= 2;
+ PODArray<Prog::Inst> inst(cap);
+ if (inst_.data() != NULL)
+ memmove(inst.data(), inst_.data(), ninst_*sizeof inst_[0]);
+ memset(inst.data() + ninst_, 0, (cap - ninst_)*sizeof inst_[0]);
+ inst_ = std::move(inst);
}
- int id = inst_len_;
- inst_len_ += n;
+ int id = ninst_;
+ ninst_ += n;
return id;
}
-void Compiler::Trim() {
- if (inst_len_ < inst_cap_) {
- Prog::Inst* ip = new Prog::Inst[inst_len_];
- memmove(ip, inst_, inst_len_ * sizeof ip[0]);
- delete[] inst_;
- inst_ = ip;
- inst_cap_ = inst_len_;
- }
-}
-
// These routines are somewhat hard to visualize in text --
// see http://swtch.com/~rsc/regexp/regexp1.html for
// pictures explaining what is going on here.
@@ -312,17 +316,18 @@ Frag Compiler::Cat(Frag a, Frag b) {
if (begin->opcode() == kInstNop &&
a.end.p == (a.begin << 1) &&
begin->out() == 0) {
- PatchList::Patch(inst_, a.end, b.begin); // in case refs to a somewhere
+ // in case refs to a somewhere
+ PatchList::Patch(inst_.data(), a.end, b.begin);
return b;
}
// To run backward over string, reverse all concatenations.
if (reversed_) {
- PatchList::Patch(inst_, b.end, a.begin);
+ PatchList::Patch(inst_.data(), b.end, a.begin);
return Frag(b.begin, a.end);
}
- PatchList::Patch(inst_, a.end, b.begin);
+ PatchList::Patch(inst_.data(), a.end, b.begin);
return Frag(a.begin, b.end);
}
@@ -339,7 +344,7 @@ Frag Compiler::Alt(Frag a, Frag b) {
return NoMatch();
inst_[id].InitAlt(a.begin, b.begin);
- return Frag(id, PatchList::Append(inst_, a.end, b.end));
+ return Frag(id, PatchList::Append(inst_.data(), a.end, b.end));
}
// When capturing submatches in like-Perl mode, a kOpAlt Inst
@@ -355,7 +360,7 @@ Frag Compiler::Star(Frag a, bool nongreedy) {
if (id < 0)
return NoMatch();
inst_[id].InitAlt(0, 0);
- PatchList::Patch(inst_, a.end, id);
+ PatchList::Patch(inst_.data(), a.end, id);
if (nongreedy) {
inst_[id].out1_ = a.begin;
return Frag(id, PatchList::Mk(id << 1));
@@ -374,6 +379,8 @@ Frag Compiler::Plus(Frag a, bool nongreedy) {
// Given a fragment for a, returns a fragment for a? or a?? (if nongreedy)
Frag Compiler::Quest(Frag a, bool nongreedy) {
+ if (IsNoMatch(a))
+ return Nop();
int id = AllocInst(1);
if (id < 0)
return NoMatch();
@@ -385,7 +392,7 @@ Frag Compiler::Quest(Frag a, bool nongreedy) {
inst_[id].InitAlt(a.begin, 0);
pl = PatchList::Mk((id << 1) | 1);
}
- return Frag(id, PatchList::Append(inst_, pl, a.end));
+ return Frag(id, PatchList::Append(inst_.data(), pl, a.end));
}
// Returns a fragment for the byte range lo-hi.
@@ -394,16 +401,6 @@ Frag Compiler::ByteRange(int lo, int hi, bool foldcase) {
if (id < 0)
return NoMatch();
inst_[id].InitByteRange(lo, hi, foldcase, 0);
- prog_->byte_inst_count_++;
- prog_->MarkByteRange(lo, hi);
- if (foldcase && lo <= 'z' && hi >= 'a') {
- if (lo < 'a')
- lo = 'a';
- if (hi > 'z')
- hi = 'z';
- if (lo <= hi)
- prog_->MarkByteRange(lo + 'A' - 'a', hi + 'A' - 'a');
- }
return Frag(id, PatchList::Mk(id << 1));
}
@@ -417,7 +414,7 @@ Frag Compiler::Nop() {
}
// Returns a fragment that signals a match.
-Frag Compiler::Match(int32 match_id) {
+Frag Compiler::Match(int32_t match_id) {
int id = AllocInst(1);
if (id < 0)
return NoMatch();
@@ -431,27 +428,19 @@ Frag Compiler::EmptyWidth(EmptyOp empty) {
if (id < 0)
return NoMatch();
inst_[id].InitEmptyWidth(empty, 0);
- if (empty & (kEmptyBeginLine|kEmptyEndLine))
- prog_->MarkByteRange('\n', '\n');
- if (empty & (kEmptyWordBoundary|kEmptyNonWordBoundary)) {
- int j;
- for (int i = 0; i < 256; i = j) {
- for (j = i+1; j < 256 && Prog::IsWordChar(i) == Prog::IsWordChar(j); j++)
- ;
- prog_->MarkByteRange(i, j-1);
- }
- }
return Frag(id, PatchList::Mk(id << 1));
}
// Given a fragment a, returns a fragment with capturing parens around a.
Frag Compiler::Capture(Frag a, int n) {
+ if (IsNoMatch(a))
+ return NoMatch();
int id = AllocInst(2);
if (id < 0)
return NoMatch();
inst_[id].InitCapture(2*n, a.begin);
inst_[id+1].InitCapture(2*n+1, 0);
- PatchList::Patch(inst_, a.end, id+1);
+ PatchList::Patch(inst_.data(), a.end, id+1);
return Frag(id, PatchList::Mk((id+1) << 1));
}
@@ -481,29 +470,29 @@ void Compiler::BeginRange() {
rune_range_.end = nullPatchList;
}
-int Compiler::UncachedRuneByteSuffix(uint8 lo, uint8 hi, bool foldcase,
+int Compiler::UncachedRuneByteSuffix(uint8_t lo, uint8_t hi, bool foldcase,
int next) {
Frag f = ByteRange(lo, hi, foldcase);
if (next != 0) {
- PatchList::Patch(inst_, f.end, next);
+ PatchList::Patch(inst_.data(), f.end, next);
} else {
- rune_range_.end = PatchList::Append(inst_, rune_range_.end, f.end);
+ rune_range_.end = PatchList::Append(inst_.data(), rune_range_.end, f.end);
}
return f.begin;
}
-int Compiler::RuneByteSuffix(uint8 lo, uint8 hi, bool foldcase, int next) {
- // In Latin1 mode, there's no point in caching.
- // In forward UTF-8 mode, only need to cache continuation bytes.
- if (encoding_ == kEncodingLatin1 ||
- (encoding_ == kEncodingUTF8 &&
- !reversed_ &&
- !(0x80 <= lo && hi <= 0xbf))) {
- return UncachedRuneByteSuffix(lo, hi, foldcase, next);
- }
+static uint64_t MakeRuneCacheKey(uint8_t lo, uint8_t hi, bool foldcase,
+ int next) {
+ return (uint64_t)next << 17 |
+ (uint64_t)lo << 9 |
+ (uint64_t)hi << 1 |
+ (uint64_t)foldcase;
+}
- uint64 key = ((uint64)next << 17) | (lo<<9) | (hi<<1) | foldcase;
- map<uint64, int>::iterator it = rune_cache_.find(key);
+int Compiler::CachedRuneByteSuffix(uint8_t lo, uint8_t hi, bool foldcase,
+ int next) {
+ uint64_t key = MakeRuneCacheKey(lo, hi, foldcase, next);
+ std::unordered_map<uint64_t, int>::const_iterator it = rune_cache_.find(key);
if (it != rune_cache_.end())
return it->second;
int id = UncachedRuneByteSuffix(lo, hi, foldcase, next);
@@ -511,12 +500,31 @@ int Compiler::RuneByteSuffix(uint8 lo, uint8 hi, bool foldcase, int next) {
return id;
}
+bool Compiler::IsCachedRuneByteSuffix(int id) {
+ uint8_t lo = inst_[id].lo_;
+ uint8_t hi = inst_[id].hi_;
+ bool foldcase = inst_[id].foldcase() != 0;
+ int next = inst_[id].out();
+
+ uint64_t key = MakeRuneCacheKey(lo, hi, foldcase, next);
+ return rune_cache_.find(key) != rune_cache_.end();
+}
+
void Compiler::AddSuffix(int id) {
+ if (failed_)
+ return;
+
if (rune_range_.begin == 0) {
rune_range_.begin = id;
return;
}
+ if (encoding_ == kEncodingUTF8) {
+ // Build a trie in order to reduce fanout.
+ rune_range_.begin = AddSuffixRecursive(rune_range_.begin, id);
+ return;
+ }
+
int alt = AllocInst(1);
if (alt < 0) {
rune_range_.begin = 0;
@@ -526,6 +534,102 @@ void Compiler::AddSuffix(int id) {
rune_range_.begin = alt;
}
+int Compiler::AddSuffixRecursive(int root, int id) {
+ DCHECK(inst_[root].opcode() == kInstAlt ||
+ inst_[root].opcode() == kInstByteRange);
+
+ Frag f = FindByteRange(root, id);
+ if (IsNoMatch(f)) {
+ int alt = AllocInst(1);
+ if (alt < 0)
+ return 0;
+ inst_[alt].InitAlt(root, id);
+ return alt;
+ }
+
+ int br;
+ if (f.end.p == 0)
+ br = root;
+ else if (f.end.p&1)
+ br = inst_[f.begin].out1();
+ else
+ br = inst_[f.begin].out();
+
+ if (IsCachedRuneByteSuffix(br)) {
+ // We can't fiddle with cached suffixes, so make a clone of the head.
+ int byterange = AllocInst(1);
+ if (byterange < 0)
+ return 0;
+ inst_[byterange].InitByteRange(inst_[br].lo(), inst_[br].hi(),
+ inst_[br].foldcase(), inst_[br].out());
+
+ // Ensure that the parent points to the clone, not to the original.
+ // Note that this could leave the head unreachable except via the cache.
+ br = byterange;
+ if (f.end.p == 0)
+ root = br;
+ else if (f.end.p&1)
+ inst_[f.begin].out1_ = br;
+ else
+ inst_[f.begin].set_out(br);
+ }
+
+ int out = inst_[id].out();
+ if (!IsCachedRuneByteSuffix(id)) {
+ // The head should be the instruction most recently allocated, so free it
+ // instead of leaving it unreachable.
+ DCHECK_EQ(id, ninst_-1);
+ inst_[id].out_opcode_ = 0;
+ inst_[id].out1_ = 0;
+ ninst_--;
+ }
+
+ out = AddSuffixRecursive(inst_[br].out(), out);
+ if (out == 0)
+ return 0;
+
+ inst_[br].set_out(out);
+ return root;
+}
+
+bool Compiler::ByteRangeEqual(int id1, int id2) {
+ return inst_[id1].lo() == inst_[id2].lo() &&
+ inst_[id1].hi() == inst_[id2].hi() &&
+ inst_[id1].foldcase() == inst_[id2].foldcase();
+}
+
+Frag Compiler::FindByteRange(int root, int id) {
+ if (inst_[root].opcode() == kInstByteRange) {
+ if (ByteRangeEqual(root, id))
+ return Frag(root, nullPatchList);
+ else
+ return NoMatch();
+ }
+
+ while (inst_[root].opcode() == kInstAlt) {
+ int out1 = inst_[root].out1();
+ if (ByteRangeEqual(out1, id))
+ return Frag(root, PatchList::Mk((root << 1) | 1));
+
+ // CharClass is a sorted list of ranges, so if out1 of the root Alt wasn't
+ // what we're looking for, then we can stop immediately. Unfortunately, we
+ // can't short-circuit the search in reverse mode.
+ if (!reversed_)
+ return NoMatch();
+
+ int out = inst_[root].out();
+ if (inst_[out].opcode() == kInstAlt)
+ root = out;
+ else if (ByteRangeEqual(out, id))
+ return Frag(root, PatchList::Mk(root << 1));
+ else
+ return NoMatch();
+ }
+
+ LOG(DFATAL) << "should never happen";
+ return NoMatch();
+}
+
Frag Compiler::EndRange() {
return rune_range_;
}
@@ -549,12 +653,13 @@ void Compiler::AddRuneRange(Rune lo, Rune hi, bool foldcase) {
}
void Compiler::AddRuneRangeLatin1(Rune lo, Rune hi, bool foldcase) {
- // Latin1 is easy: runes *are* bytes.
+ // Latin-1 is easy: runes *are* bytes.
if (lo > hi || lo > 0xFF)
return;
if (hi > 0xFF)
hi = 0xFF;
- AddSuffix(RuneByteSuffix(lo, hi, foldcase, 0));
+ AddSuffix(UncachedRuneByteSuffix(static_cast<uint8_t>(lo),
+ static_cast<uint8_t>(hi), foldcase, 0));
}
// Table describing how to make a UTF-8 matching machine
@@ -595,7 +700,8 @@ void Compiler::Add_80_10ffff() {
int next = 0;
if (p.next >= 0)
next = inst[p.next];
- inst[i] = UncachedRuneByteSuffix(p.lo, p.hi, false, next);
+ inst[i] = UncachedRuneByteSuffix(static_cast<uint8_t>(p.lo),
+ static_cast<uint8_t>(p.hi), false, next);
if ((p.lo & 0xC0) != 0x80)
AddSuffix(inst[i]);
}
@@ -624,13 +730,14 @@ void Compiler::AddRuneRangeUTF8(Rune lo, Rune hi, bool foldcase) {
// ASCII range is always a special case.
if (hi < Runeself) {
- AddSuffix(RuneByteSuffix(lo, hi, foldcase, 0));
+ AddSuffix(UncachedRuneByteSuffix(static_cast<uint8_t>(lo),
+ static_cast<uint8_t>(hi), foldcase, 0));
return;
}
// Split range into sections that agree on leading bytes.
for (int i = 1; i < UTFmax; i++) {
- uint m = (1<<(6*i)) - 1; // last i bytes of a UTF-8 sequence
+ uint32_t m = (1<<(6*i)) - 1; // last i bytes of a UTF-8 sequence
if ((lo & ~m) != (hi & ~m)) {
if ((lo & m) != 0) {
AddRuneRangeUTF8(lo, lo|m, foldcase);
@@ -646,19 +753,55 @@ void Compiler::AddRuneRangeUTF8(Rune lo, Rune hi, bool foldcase) {
}
// Finally. Generate byte matching equivalent for lo-hi.
- uint8 ulo[UTFmax], uhi[UTFmax];
+ uint8_t ulo[UTFmax], uhi[UTFmax];
int n = runetochar(reinterpret_cast<char*>(ulo), &lo);
int m = runetochar(reinterpret_cast<char*>(uhi), &hi);
(void)m; // USED(m)
DCHECK_EQ(n, m);
+ // The logic below encodes this thinking:
+ //
+ // 1. When we have built the whole suffix, we know that it cannot
+ // possibly be a suffix of anything longer: in forward mode, nothing
+ // else can occur before the leading byte; in reverse mode, nothing
+ // else can occur after the last continuation byte or else the leading
+ // byte would have to change. Thus, there is no benefit to caching
+ // the first byte of the suffix whereas there is a cost involved in
+ // cloning it if it begins a common prefix, which is fairly likely.
+ //
+ // 2. Conversely, the last byte of the suffix cannot possibly be a
+ // prefix of anything because next == 0, so we will never want to
+ // clone it, but it is fairly likely to be a common suffix. Perhaps
+ // more so in reverse mode than in forward mode because the former is
+ // "converging" towards lower entropy, but caching is still worthwhile
+ // for the latter in cases such as 80-BF.
+ //
+ // 3. Handling the bytes between the first and the last is less
+ // straightforward and, again, the approach depends on whether we are
+ // "converging" towards lower entropy: in forward mode, a single byte
+ // is unlikely to be part of a common suffix whereas a byte range
+ // is more likely so; in reverse mode, a byte range is unlikely to
+ // be part of a common suffix whereas a single byte is more likely
+ // so. The same benefit versus cost argument applies here.
int id = 0;
if (reversed_) {
- for (int i = 0; i < n; i++)
- id = RuneByteSuffix(ulo[i], uhi[i], false, id);
+ for (int i = 0; i < n; i++) {
+ // In reverse UTF-8 mode: cache the leading byte; don't cache the last
+ // continuation byte; cache anything else iff it's a single byte (XX-XX).
+ if (i == 0 || (ulo[i] == uhi[i] && i != n-1))
+ id = CachedRuneByteSuffix(ulo[i], uhi[i], false, id);
+ else
+ id = UncachedRuneByteSuffix(ulo[i], uhi[i], false, id);
+ }
} else {
- for (int i = n-1; i >= 0; i--)
- id = RuneByteSuffix(ulo[i], uhi[i], false, id);
+ for (int i = n-1; i >= 0; i--) {
+ // In forward UTF-8 mode: don't cache the leading byte; cache the last
+ // continuation byte; cache anything else iff it's a byte range (XX-YY).
+ if (i == n-1 || (ulo[i] < uhi[i] && i != 0))
+ id = CachedRuneByteSuffix(ulo[i], uhi[i], false, id);
+ else
+ id = UncachedRuneByteSuffix(ulo[i], uhi[i], false, id);
+ }
}
AddSuffix(id);
}
@@ -684,13 +827,13 @@ Frag Compiler::PreVisit(Regexp* re, Frag, bool* stop) {
if (failed_)
*stop = true;
- return kNullFrag; // not used by caller
+ return Frag(); // not used by caller
}
Frag Compiler::Literal(Rune r, bool foldcase) {
switch (encoding_) {
default:
- return kNullFrag;
+ return Frag();
case kEncodingLatin1:
return ByteRange(r, r, foldcase);
@@ -698,11 +841,11 @@ Frag Compiler::Literal(Rune r, bool foldcase) {
case kEncodingUTF8: {
if (r < Runeself) // Make common case fast.
return ByteRange(r, r, foldcase);
- uint8 buf[UTFmax];
+ uint8_t buf[UTFmax];
int n = runetochar(reinterpret_cast<char*>(buf), &r);
- Frag f = ByteRange((uint8)buf[0], buf[0], false);
+ Frag f = ByteRange((uint8_t)buf[0], buf[0], false);
for (int i = 1; i < n; i++)
- f = Cat(f, ByteRange((uint8)buf[i], buf[i], false));
+ f = Cat(f, ByteRange((uint8_t)buf[i], buf[i], false));
return f;
}
}
@@ -731,9 +874,11 @@ Frag Compiler::PostVisit(Regexp* re, Frag, Frag, Frag* child_frags,
case kRegexpHaveMatch: {
Frag f = Match(re->match_id());
- // Remember unanchored match to end of string.
- if (anchor_ != RE2::ANCHOR_BOTH)
- f = Cat(DotStar(), Cat(EmptyWidth(kEmptyEndText), f));
+ if (anchor_ == RE2::ANCHOR_BOTH) {
+ // Append \z or else the subexpression will effectively be unanchored.
+ // Complemented by the UNANCHORED case in CompileSet().
+ f = Cat(EmptyWidth(kEmptyEndText), f);
+ }
return f;
}
@@ -752,16 +897,16 @@ Frag Compiler::PostVisit(Regexp* re, Frag, Frag, Frag* child_frags,
}
case kRegexpStar:
- return Star(child_frags[0], re->parse_flags()&Regexp::NonGreedy);
+ return Star(child_frags[0], (re->parse_flags()&Regexp::NonGreedy) != 0);
case kRegexpPlus:
- return Plus(child_frags[0], re->parse_flags()&Regexp::NonGreedy);
+ return Plus(child_frags[0], (re->parse_flags()&Regexp::NonGreedy) != 0);
case kRegexpQuest:
- return Quest(child_frags[0], re->parse_flags()&Regexp::NonGreedy);
+ return Quest(child_frags[0], (re->parse_flags()&Regexp::NonGreedy) != 0);
case kRegexpLiteral:
- return Literal(re->rune(), re->parse_flags()&Regexp::FoldCase);
+ return Literal(re->rune(), (re->parse_flags()&Regexp::FoldCase) != 0);
case kRegexpLiteralString: {
// Concatenation of literals.
@@ -769,7 +914,8 @@ Frag Compiler::PostVisit(Regexp* re, Frag, Frag, Frag* child_frags,
return Nop();
Frag f;
for (int i = 0; i < re->nrunes(); i++) {
- Frag f1 = Literal(re->runes()[i], re->parse_flags()&Regexp::FoldCase);
+ Frag f1 = Literal(re->runes()[i],
+ (re->parse_flags()&Regexp::FoldCase) != 0);
if (i == 0)
f = f1;
else
@@ -814,7 +960,8 @@ Frag Compiler::PostVisit(Regexp* re, Frag, Frag, Frag* child_frags,
// If this range contains all of A-Za-z or none of it,
// the fold flag is unnecessary; don't bother.
bool fold = foldascii;
- if ((i->lo <= 'A' && 'z' <= i->hi) || i->hi < 'A' || 'z' < i->lo)
+ if ((i->lo <= 'A' && 'z' <= i->hi) || i->hi < 'A' || 'z' < i->lo ||
+ ('Z' < i->lo && i->hi < 'a'))
fold = false;
AddRuneRange(i->lo, i->hi, fold);
@@ -871,12 +1018,11 @@ static bool IsAnchorStart(Regexp** pre, int depth) {
if (re->nsub() > 0) {
sub = re->sub()[0]->Incref();
if (IsAnchorStart(&sub, depth+1)) {
- Regexp** subcopy = new Regexp*[re->nsub()];
+ PODArray<Regexp*> subcopy(re->nsub());
subcopy[0] = sub; // already have reference
for (int i = 1; i < re->nsub(); i++)
subcopy[i] = re->sub()[i]->Incref();
- *pre = Regexp::Concat(subcopy, re->nsub(), re->parse_flags());
- delete[] subcopy;
+ *pre = Regexp::Concat(subcopy.data(), re->nsub(), re->parse_flags());
re->Decref();
return true;
}
@@ -919,12 +1065,11 @@ static bool IsAnchorEnd(Regexp** pre, int depth) {
if (re->nsub() > 0) {
sub = re->sub()[re->nsub() - 1]->Incref();
if (IsAnchorEnd(&sub, depth+1)) {
- Regexp** subcopy = new Regexp*[re->nsub()];
+ PODArray<Regexp*> subcopy(re->nsub());
subcopy[re->nsub() - 1] = sub; // already have reference
for (int i = 0; i < re->nsub() - 1; i++)
subcopy[i] = re->sub()[i]->Incref();
- *pre = Regexp::Concat(subcopy, re->nsub(), re->parse_flags());
- delete[] subcopy;
+ *pre = Regexp::Concat(subcopy.data(), re->nsub(), re->parse_flags());
re->Decref();
return true;
}
@@ -948,7 +1093,7 @@ static bool IsAnchorEnd(Regexp** pre, int depth) {
return false;
}
-void Compiler::Setup(Regexp::ParseFlags flags, int64 max_mem,
+void Compiler::Setup(Regexp::ParseFlags flags, int64_t max_mem,
RE2::Anchor anchor) {
prog_->set_flags(flags);
@@ -956,15 +1101,15 @@ void Compiler::Setup(Regexp::ParseFlags flags, int64 max_mem,
encoding_ = kEncodingLatin1;
max_mem_ = max_mem;
if (max_mem <= 0) {
- max_inst_ = 100000; // more than enough
- } else if (max_mem <= sizeof(Prog)) {
+ max_ninst_ = 100000; // more than enough
+ } else if (static_cast<size_t>(max_mem) <= sizeof(Prog)) {
// No room for anything.
- max_inst_ = 0;
+ max_ninst_ = 0;
} else {
- int64 m = (max_mem - sizeof(Prog)) / sizeof(Prog::Inst);
+ int64_t m = (max_mem - sizeof(Prog)) / sizeof(Prog::Inst);
// Limit instruction count so that inst->id() fits nicely in an int.
// SparseArray also assumes that the indices (inst->id()) are ints.
- // The call to WalkExponential uses 2*max_inst_ below,
+ // The call to WalkExponential uses 2*max_ninst_ below,
// and other places in the code use 2 or 3 * prog->size().
// Limiting to 2^24 should avoid overflow in those places.
// (The point of allowing more than 32 bits of memory is to
@@ -977,7 +1122,7 @@ void Compiler::Setup(Regexp::ParseFlags flags, int64 max_mem,
if (m > Prog::Inst::kMaxInst)
m = Prog::Inst::kMaxInst;
- max_inst_ = m;
+ max_ninst_ = static_cast<int>(m);
}
anchor_ = anchor;
@@ -988,10 +1133,9 @@ void Compiler::Setup(Regexp::ParseFlags flags, int64 max_mem,
// If reversed is true, compiles a program that expects
// to run over the input string backward (reverses all concatenations).
// The reversed flag is also recorded in the returned program.
-Prog* Compiler::Compile(Regexp* re, bool reversed, int64 max_mem) {
+Prog* Compiler::Compile(Regexp* re, bool reversed, int64_t max_mem) {
Compiler c;
-
- c.Setup(re->parse_flags(), max_mem, RE2::ANCHOR_BOTH /* unused */);
+ c.Setup(re->parse_flags(), max_mem, RE2::UNANCHORED /* unused */);
c.reversed_ = reversed;
// Simplify to remove things like counted repetitions
@@ -1006,7 +1150,7 @@ Prog* Compiler::Compile(Regexp* re, bool reversed, int64 max_mem) {
bool is_anchor_end = IsAnchorEnd(&sre, 0);
// Generate fragment for entire regexp.
- Frag f = c.WalkExponential(sre, kNullFrag, 2*c.max_inst_);
+ Frag all = c.WalkExponential(sre, Frag(), 2*c.max_ninst_);
sre->Decref();
if (c.failed_)
return NULL;
@@ -1015,10 +1159,10 @@ Prog* Compiler::Compile(Regexp* re, bool reversed, int64 max_mem) {
// Turn off c.reversed_ (if it is set) to force the remaining concatenations
// to behave normally.
c.reversed_ = false;
- Frag all = c.Cat(f, c.Match(0));
- c.prog_->set_start(all.begin);
+ all = c.Cat(all, c.Match(0));
- if (reversed) {
+ c.prog_->set_reversed(reversed);
+ if (c.prog_->reversed()) {
c.prog_->set_anchor_start(is_anchor_end);
c.prog_->set_anchor_end(is_anchor_start);
} else {
@@ -1026,15 +1170,12 @@ Prog* Compiler::Compile(Regexp* re, bool reversed, int64 max_mem) {
c.prog_->set_anchor_end(is_anchor_end);
}
- // Also create unanchored version, which starts with a .*? loop.
- if (c.prog_->anchor_start()) {
- c.prog_->set_start_unanchored(c.prog_->start());
- } else {
- Frag unanchored = c.Cat(c.DotStar(), all);
- c.prog_->set_start_unanchored(unanchored.begin);
+ c.prog_->set_start(all.begin);
+ if (!c.prog_->anchor_start()) {
+ // Also create unanchored version, which starts with a .*? loop.
+ all = c.Cat(c.DotStar(), all);
}
-
- c.prog_->set_reversed(reversed);
+ c.prog_->set_start_unanchored(all.begin);
// Hand ownership of prog_ to caller.
return c.Finish();
@@ -1046,25 +1187,22 @@ Prog* Compiler::Finish() {
if (prog_->start() == 0 && prog_->start_unanchored() == 0) {
// No possible matches; keep Fail instruction only.
- inst_len_ = 1;
+ ninst_ = 1;
}
- // Trim instruction to minimum array and transfer to Prog.
- Trim();
- prog_->inst_ = inst_;
- prog_->size_ = inst_len_;
- inst_ = NULL;
-
- // Compute byte map.
- prog_->ComputeByteMap();
+ // Hand off the array to Prog.
+ prog_->inst_ = std::move(inst_);
+ prog_->size_ = ninst_;
prog_->Optimize();
+ prog_->Flatten();
+ prog_->ComputeByteMap();
// Record remaining memory for DFA.
if (max_mem_ <= 0) {
prog_->set_dfa_mem(1<<20);
} else {
- int64 m = max_mem_ - sizeof(Prog) - inst_len_*sizeof(Prog::Inst);
+ int64_t m = max_mem_ - sizeof(Prog) - prog_->size_*sizeof(Prog::Inst);
if (m < 0)
m = 0;
prog_->set_dfa_mem(m);
@@ -1076,11 +1214,11 @@ Prog* Compiler::Finish() {
}
// Converts Regexp to Prog.
-Prog* Regexp::CompileToProg(int64 max_mem) {
+Prog* Regexp::CompileToProg(int64_t max_mem) {
return Compiler::Compile(this, false, max_mem);
}
-Prog* Regexp::CompileToReverseProg(int64 max_mem) {
+Prog* Regexp::CompileToReverseProg(int64_t max_mem) {
return Compiler::Compile(this, true, max_mem);
}
@@ -1089,29 +1227,29 @@ Frag Compiler::DotStar() {
}
// Compiles RE set to Prog.
-Prog* Compiler::CompileSet(const RE2::Options& options, RE2::Anchor anchor,
- Regexp* re) {
+Prog* Compiler::CompileSet(Regexp* re, RE2::Anchor anchor, int64_t max_mem) {
Compiler c;
+ c.Setup(re->parse_flags(), max_mem, anchor);
- Regexp::ParseFlags pf = static_cast<Regexp::ParseFlags>(options.ParseFlags());
- c.Setup(pf, options.max_mem(), anchor);
+ Regexp* sre = re->Simplify();
+ if (sre == NULL)
+ return NULL;
- // Compile alternation of fragments.
- Frag all = c.WalkExponential(re, kNullFrag, 2*c.max_inst_);
- re->Decref();
+ Frag all = c.WalkExponential(sre, Frag(), 2*c.max_ninst_);
+ sre->Decref();
if (c.failed_)
return NULL;
+ c.prog_->set_anchor_start(true);
+ c.prog_->set_anchor_end(true);
+
if (anchor == RE2::UNANCHORED) {
- // The trailing .* was added while handling kRegexpHaveMatch.
- // We just have to add the leading one.
+ // Prepend .* or else the expression will effectively be anchored.
+ // Complemented by the ANCHOR_BOTH case in PostVisit().
all = c.Cat(c.DotStar(), all);
}
-
c.prog_->set_start(all.begin);
c.prog_->set_start_unanchored(all.begin);
- c.prog_->set_anchor_start(true);
- c.prog_->set_anchor_end(true);
Prog* prog = c.Finish();
if (prog == NULL)
@@ -1119,11 +1257,11 @@ Prog* Compiler::CompileSet(const RE2::Options& options, RE2::Anchor anchor,
// Make sure DFA has enough memory to operate,
// since we're not going to fall back to the NFA.
- bool failed;
+ bool dfa_failed = false;
StringPiece sp = "hello, world";
prog->SearchDFA(sp, sp, Prog::kAnchored, Prog::kManyMatch,
- NULL, &failed, NULL);
- if (failed) {
+ NULL, &dfa_failed, NULL);
+ if (dfa_failed) {
delete prog;
return NULL;
}
@@ -1131,9 +1269,8 @@ Prog* Compiler::CompileSet(const RE2::Options& options, RE2::Anchor anchor,
return prog;
}
-Prog* Prog::CompileSet(const RE2::Options& options, RE2::Anchor anchor,
- Regexp* re) {
- return Compiler::CompileSet(options, anchor, re);
+Prog* Prog::CompileSet(Regexp* re, RE2::Anchor anchor, int64_t max_mem) {
+ return Compiler::CompileSet(re, anchor, max_mem);
}
} // namespace re2
diff --git a/re2/dfa.cc b/re2/dfa.cc
index 2556c0f..89b9b77 100644
--- a/re2/dfa.cc
+++ b/re2/dfa.cc
@@ -21,15 +21,34 @@
//
// See http://swtch.com/~rsc/regexp/ for a very bare-bones equivalent.
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+#include <atomic>
+#include <deque>
+#include <mutex>
+#include <new>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "util/logging.h"
+#include "util/mix.h"
+#include "util/mutex.h"
+#include "util/pod_array.h"
+#include "util/sparse_set.h"
+#include "util/strutil.h"
#include "re2/prog.h"
#include "re2/stringpiece.h"
-#include "util/atomicops.h"
-#include "util/flags.h"
-#include "util/sparse_set.h"
-DEFINE_bool(re2_dfa_bail_when_slow, true,
- "Whether the RE2 DFA should bail out early "
- "if the NFA would be faster (for testing).");
+// Silence "zero-sized array in struct/union" warning for DFA::State::next_.
+#ifdef _MSC_VER
+#pragma warning(disable: 4200)
+#endif
namespace re2 {
@@ -44,9 +63,12 @@ static void* memrchr(const void* s, int c, size_t n) {
}
#endif
+// Controls whether the DFA should bail out early if the NFA would be faster.
+static bool dfa_should_bail_when_slow = true;
+
// Changing this to true compiles in prints that trace execution of the DFA.
// Generates a lot of output -- only useful for debugging.
-static const bool DebugDFA = false;
+static const bool ExtraDebug = false;
// A DFA implementation of a regular expression program.
// Since this is entirely a forward declaration mandated by C++,
@@ -54,7 +76,7 @@ static const bool DebugDFA = false;
// the comments in the sections that follow the DFA definition.
class DFA {
public:
- DFA(Prog* prog, Prog::MatchKind kind, int64 max_mem);
+ DFA(Prog* prog, Prog::MatchKind kind, int64_t max_mem);
~DFA();
bool ok() const { return !init_failed_; }
Prog::MatchKind kind() { return kind_; }
@@ -74,11 +96,13 @@ class DFA {
// memory), it sets *failed and returns false.
bool Search(const StringPiece& text, const StringPiece& context,
bool anchored, bool want_earliest_match, bool run_forward,
- bool* failed, const char** ep, vector<int>* matches);
+ bool* failed, const char** ep, SparseSet* matches);
- // Builds out all states for the entire DFA. FOR TESTING ONLY
- // Returns number of states.
- int BuildAllStates();
+ // Builds out all states for the entire DFA.
+ // If cb is not empty, it receives one callback per state built.
+ // Returns the number of states built.
+ // FOR TESTING OR EXPERIMENTAL PURPOSES ONLY.
+ int BuildAllStates(const Prog::DFAStateCallback& cb);
// Computes min and max for matching strings. Won't return strings
// bigger than maxlen.
@@ -86,101 +110,78 @@ class DFA {
// These data structures are logically private, but C++ makes it too
// difficult to mark them as such.
- class Workq;
class RWLocker;
class StateSaver;
+ class Workq;
// A single DFA state. The DFA is represented as a graph of these
// States, linked by the next_ pointers. If in state s and reading
// byte c, the next state should be s->next_[c].
struct State {
- inline bool IsMatch() const { return flag_ & kFlagMatch; }
- void SaveMatch(vector<int>* v);
+ inline bool IsMatch() const { return (flag_ & kFlagMatch) != 0; }
+ void SaveMatch(std::vector<int>* v);
int* inst_; // Instruction pointers in the state.
int ninst_; // # of inst_ pointers.
- uint flag_; // Empty string bitfield flags in effect on the way
+ uint32_t flag_; // Empty string bitfield flags in effect on the way
// into this state, along with kFlagMatch if this
// is a matching state.
- State** next_; // Outgoing arrows from State,
+
+// Work around the bug affecting flexible array members in GCC 6.x (for x >= 1).
+// (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70932)
+#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && __GNUC_MINOR__ >= 1
+ std::atomic<State*> next_[0]; // Outgoing arrows from State,
+#else
+ std::atomic<State*> next_[]; // Outgoing arrows from State,
+#endif
+
// one per input byte class
};
enum {
kByteEndText = 256, // imaginary byte at end of text
- kFlagEmptyMask = 0xFFF, // State.flag_: bits holding kEmptyXXX flags
- kFlagMatch = 0x1000, // State.flag_: this is a matching state
- kFlagLastWord = 0x2000, // State.flag_: last byte was a word char
+ kFlagEmptyMask = 0xFF, // State.flag_: bits holding kEmptyXXX flags
+ kFlagMatch = 0x0100, // State.flag_: this is a matching state
+ kFlagLastWord = 0x0200, // State.flag_: last byte was a word char
kFlagNeedShift = 16, // needed kEmpty bits are or'ed in shifted left
};
-#ifndef STL_MSVC
- // STL function structures for use with unordered_set.
+ struct StateHash {
+ size_t operator()(const State* a) const {
+ DCHECK(a != NULL);
+ HashMix mix(a->flag_);
+ for (int i = 0; i < a->ninst_; i++)
+ mix.Mix(a->inst_[i]);
+ mix.Mix(0);
+ return mix.get();
+ }
+ };
+
struct StateEqual {
bool operator()(const State* a, const State* b) const {
+ DCHECK(a != NULL);
+ DCHECK(b != NULL);
if (a == b)
return true;
- if (a == NULL || b == NULL)
+ if (a->flag_ != b->flag_)
return false;
if (a->ninst_ != b->ninst_)
return false;
- if (a->flag_ != b->flag_)
- return false;
for (int i = 0; i < a->ninst_; i++)
if (a->inst_[i] != b->inst_[i])
return false;
- return true; // they're equal
- }
- };
-#endif // STL_MSVC
- struct StateHash {
- size_t operator()(const State* a) const {
- if (a == NULL)
- return 0;
- const char* s = reinterpret_cast<const char*>(a->inst_);
- int len = a->ninst_ * sizeof a->inst_[0];
- if (sizeof(size_t) == sizeof(uint32))
- return Hash32StringWithSeed(s, len, a->flag_);
- else
- return Hash64StringWithSeed(s, len, a->flag_);
- }
-#ifdef STL_MSVC
- // Less than operator.
- bool operator()(const State* a, const State* b) const {
- if (a == b)
- return false;
- if (a == NULL || b == NULL)
- return a == NULL;
- if (a->ninst_ != b->ninst_)
- return a->ninst_ < b->ninst_;
- if (a->flag_ != b->flag_)
- return a->flag_ < b->flag_;
- for (int i = 0; i < a->ninst_; ++i)
- if (a->inst_[i] != b->inst_[i])
- return a->inst_[i] < b->inst_[i];
- return false; // they're equal
+ return true;
}
- // The two public members are required by msvc. 4 and 8 are default values.
- // Reference: http://msdn.microsoft.com/en-us/library/1s1byw77.aspx
- static const size_t bucket_size = 4;
- static const size_t min_buckets = 8;
-#endif // STL_MSVC
};
-#ifdef STL_MSVC
- typedef unordered_set<State*, StateHash> StateSet;
-#else // !STL_MSVC
- typedef unordered_set<State*, StateHash, StateEqual> StateSet;
-#endif // STL_MSVC
-
+ typedef std::unordered_set<State*, StateHash, StateEqual> StateSet;
private:
- // Special "firstbyte" values for a state. (Values >= 0 denote actual bytes.)
+ // Special "first_byte" values for a state. (Values >= 0 denote actual bytes.)
enum {
kFbUnknown = -1, // No analysis has been performed.
- kFbMany = -2, // Many bytes will lead out of this state.
- kFbNone = -3, // No bytes lead out of this state.
+ kFbNone = -2, // The first-byte trick cannot be used.
};
enum {
@@ -205,11 +206,11 @@ class DFA {
// Looks up and returns the State corresponding to a Workq.
// L >= mutex_
- State* WorkqToCachedState(Workq* q, uint flag);
+ State* WorkqToCachedState(Workq* q, Workq* mq, uint32_t flag);
// Looks up and returns a State matching the inst, ninst, and flag.
// L >= mutex_
- State* CachedState(int* inst, int ninst, uint flag);
+ State* CachedState(int* inst, int ninst, uint32_t flag);
// Clear the cache entirely.
// Must hold cache_mutex_.w or be in destructor.
@@ -217,7 +218,7 @@ class DFA {
// Converts a State into a Workq: the opposite of WorkqToCachedState.
// L >= mutex_
- static void StateToWorkq(State* s, Workq* q);
+ void StateToWorkq(State* s, Workq* q);
// Runs a State on a given byte, returning the next state.
State* RunStateOnByteUnlocked(State*, int); // cache_mutex_.r <= L < mutex_
@@ -228,18 +229,16 @@ class DFA {
// sets *ismatch to true.
// L >= mutex_
void RunWorkqOnByte(Workq* q, Workq* nq,
- int c, uint flag, bool* ismatch,
- Prog::MatchKind kind,
- int new_byte_loop);
+ int c, uint32_t flag, bool* ismatch);
// Runs a Workq on a set of empty-string flags, producing a new Workq in nq.
// L >= mutex_
- void RunWorkqOnEmptyString(Workq* q, Workq* nq, uint flag);
+ void RunWorkqOnEmptyString(Workq* q, Workq* nq, uint32_t flag);
// Adds the instruction id to the Workq, following empty arrows
// according to flag.
// L >= mutex_
- void AddToQueue(Workq* q, int id, uint flag);
+ void AddToQueue(Workq* q, int id, uint32_t flag);
// For debugging, returns a text representation of State.
static string DumpState(State* state);
@@ -256,7 +255,7 @@ class DFA {
want_earliest_match(false),
run_forward(false),
start(NULL),
- firstbyte(kFbUnknown),
+ first_byte(kFbUnknown),
cache_lock(cache_lock),
failed(false),
ep(NULL),
@@ -268,37 +267,39 @@ class DFA {
bool want_earliest_match;
bool run_forward;
State* start;
- int firstbyte;
+ int first_byte;
RWLocker *cache_lock;
bool failed; // "out" parameter: whether search gave up
const char* ep; // "out" parameter: end pointer for match
- vector<int>* matches;
+ SparseSet* matches;
private:
- DISALLOW_EVIL_CONSTRUCTORS(SearchParams);
+ SearchParams(const SearchParams&) = delete;
+ SearchParams& operator=(const SearchParams&) = delete;
};
// Before each search, the parameters to Search are analyzed by
// AnalyzeSearch to determine the state in which to start and the
- // "firstbyte" for that state, if any.
+ // "first_byte" for that state, if any.
struct StartInfo {
- StartInfo() : start(NULL), firstbyte(kFbUnknown) { }
+ StartInfo() : start(NULL), first_byte(kFbUnknown) {}
State* start;
- volatile int firstbyte;
+ std::atomic<int> first_byte;
};
- // Fills in params->start and params->firstbyte using
+ // Fills in params->start and params->first_byte using
// the other search parameters. Returns true on success,
// false on failure.
// cache_mutex_.r <= L < mutex_
bool AnalyzeSearch(SearchParams* params);
- bool AnalyzeSearchHelper(SearchParams* params, StartInfo* info, uint flags);
+ bool AnalyzeSearchHelper(SearchParams* params, StartInfo* info,
+ uint32_t flags);
// The generic search loop, inlined to create specialized versions.
// cache_mutex_.r <= L < mutex_
// Might unlock and relock cache_mutex_ via params->cache_lock.
inline bool InlinedSearchLoop(SearchParams* params,
- bool have_firstbyte,
+ bool have_first_byte,
bool want_earliest_match,
bool run_forward);
@@ -340,7 +341,6 @@ class DFA {
// Constant after initialization.
Prog* prog_; // The regular expression program to run.
Prog::MatchKind kind_; // The kind of DFA.
- int start_unanchored_; // start of unanchored program
bool init_failed_; // initialization failed (out of memory)
Mutex mutex_; // mutex_ >= cache_mutex_.r
@@ -348,8 +348,7 @@ class DFA {
// Scratch areas, protected by mutex_.
Workq* q0_; // Two pre-allocated work queues.
Workq* q1_;
- int* astack_; // Pre-allocated stack for AddToQueue
- int nastack_;
+ PODArray<int> stack_; // Pre-allocated stack for AddToQueue
// State* cache. Many threads use and add to the cache simultaneously,
// holding cache_mutex_ for reading and mutex_ (above) when adding.
@@ -358,16 +357,15 @@ class DFA {
// readers. Any State* pointers are only valid while cache_mutex_
// is held.
Mutex cache_mutex_;
- int64 mem_budget_; // Total memory budget for all States.
- int64 state_budget_; // Amount of memory remaining for new States.
+ int64_t mem_budget_; // Total memory budget for all States.
+ int64_t state_budget_; // Amount of memory remaining for new States.
StateSet state_cache_; // All States computed so far.
StartInfo start_[kMaxStart];
- bool cache_warned_; // have printed to LOG(INFO) about the cache
};
-// Shorthand for casting to uint8*.
-static inline const uint8* BytePtr(const void* v) {
- return reinterpret_cast<const uint8*>(v);
+// Shorthand for casting to uint8_t*.
+static inline const uint8_t* BytePtr(const void* v) {
+ return reinterpret_cast<const uint8_t*>(v);
}
// Work queues
@@ -376,6 +374,10 @@ static inline const uint8* BytePtr(const void* v) {
// in the work queue when in leftmost-longest matching mode.
#define Mark (-1)
+// Separates the match IDs from the instructions in inst_.
+// Used only for "many match" DFA states.
+#define MatchSep (-2)
+
// Internally, the DFA uses a sparse array of
// program instruction pointers as a work queue.
// In leftmost longest mode, marks separate sections
@@ -428,36 +430,35 @@ class DFA::Workq : public SparseSet {
int maxmark_; // maximum number of marks
int nextmark_; // id of next mark
bool last_was_mark_; // last inserted was mark
- DISALLOW_EVIL_CONSTRUCTORS(Workq);
+
+ Workq(const Workq&) = delete;
+ Workq& operator=(const Workq&) = delete;
};
-DFA::DFA(Prog* prog, Prog::MatchKind kind, int64 max_mem)
+DFA::DFA(Prog* prog, Prog::MatchKind kind, int64_t max_mem)
: prog_(prog),
kind_(kind),
init_failed_(false),
q0_(NULL),
q1_(NULL),
- astack_(NULL),
- mem_budget_(max_mem),
- cache_warned_(false) {
- if (DebugDFA)
+ mem_budget_(max_mem) {
+ if (ExtraDebug)
fprintf(stderr, "\nkind %d\n%s\n", (int)kind_, prog_->DumpUnanchored().c_str());
int nmark = 0;
- start_unanchored_ = 0;
- if (kind_ == Prog::kLongestMatch) {
- nmark = prog->size();
- start_unanchored_ = prog->start_unanchored();
- }
- nastack_ = 2 * prog->size() + nmark;
-
- // Account for space needed for DFA, q0, q1, astack.
+ if (kind_ == Prog::kLongestMatch)
+ nmark = prog_->size();
+ // See DFA::AddToQueue() for why this is so.
+ int nstack = prog_->inst_count(kInstCapture) +
+ prog_->inst_count(kInstEmptyWidth) +
+ prog_->inst_count(kInstNop) +
+ nmark + 1; // + 1 for start inst
+
+ // Account for space needed for DFA, q0, q1, stack.
mem_budget_ -= sizeof(DFA);
mem_budget_ -= (prog_->size() + nmark) *
(sizeof(int)+sizeof(int)) * 2; // q0, q1
- mem_budget_ -= nastack_ * sizeof(int); // astack
+ mem_budget_ -= nstack * sizeof(int); // stack
if (mem_budget_ < 0) {
- LOG(INFO) << StringPrintf("DFA out of memory: prog size %lld mem %lld",
- prog_->size(), max_mem);
init_failed_ = true;
return;
}
@@ -468,24 +469,24 @@ DFA::DFA(Prog* prog, Prog::MatchKind kind, int64 max_mem)
// At minimum, the search requires room for two states in order
// to limp along, restarting frequently. We'll get better performance
// if there is room for a larger number of states, say 20.
- int64 one_state = sizeof(State) + (prog_->size()+nmark)*sizeof(int) +
- (prog_->bytemap_range()+1)*sizeof(State*);
+ // Note that a state stores list heads only, so we use the program
+ // list count for the upper bound, not the program size.
+ int nnext = prog_->bytemap_range() + 1; // + 1 for kByteEndText slot
+ int64_t one_state = sizeof(State) + nnext*sizeof(std::atomic<State*>) +
+ (prog_->list_count()+nmark)*sizeof(int);
if (state_budget_ < 20*one_state) {
- LOG(INFO) << StringPrintf("DFA out of memory: prog size %lld mem %lld",
- prog_->size(), max_mem);
init_failed_ = true;
return;
}
- q0_ = new Workq(prog->size(), nmark);
- q1_ = new Workq(prog->size(), nmark);
- astack_ = new int[nastack_];
+ q0_ = new Workq(prog_->size(), nmark);
+ q1_ = new Workq(prog_->size(), nmark);
+ stack_ = PODArray<int>(nstack);
}
DFA::~DFA() {
delete q0_;
delete q1_;
- delete[] astack_;
ClearCache();
}
@@ -507,7 +508,7 @@ DFA::~DFA() {
string DFA::DumpWorkq(Workq* q) {
string s;
const char* sep = "";
- for (DFA::Workq::iterator it = q->begin(); it != q->end(); ++it) {
+ for (Workq::iterator it = q->begin(); it != q->end(); ++it) {
if (q->is_mark(*it)) {
StringAppendF(&s, "|");
sep = "";
@@ -534,6 +535,9 @@ string DFA::DumpState(State* state) {
if (state->inst_[i] == Mark) {
StringAppendF(&s, "|");
sep = "";
+ } else if (state->inst_[i] == MatchSep) {
+ StringAppendF(&s, "||");
+ sep = "";
} else {
StringAppendF(&s, "%s%d", sep, state->inst_[i]);
sep = ",";
@@ -601,9 +605,10 @@ string DFA::DumpState(State* state) {
// Looks in the State cache for a State matching q, flag.
// If one is found, returns it. If one is not found, allocates one,
// inserts it in the cache, and returns it.
-DFA::State* DFA::WorkqToCachedState(Workq* q, uint flag) {
- if (DEBUG_MODE)
- mutex_.AssertHeld();
+// If mq is not null, MatchSep and the match IDs in mq will be appended
+// to the State.
+DFA::State* DFA::WorkqToCachedState(Workq* q, Workq* mq, uint32_t flag) {
+ //mutex_.AssertHeld();
// Construct array of instruction ids for the new state.
// Only ByteRange, EmptyWidth, and Match instructions are useful to keep:
@@ -611,10 +616,10 @@ DFA::State* DFA::WorkqToCachedState(Workq* q, uint flag) {
// RunWorkqOnEmptyString or RunWorkqOnByte.
int* inst = new int[q->size()];
int n = 0;
- uint needflags = 0; // flags needed by kInstEmptyWidth instructions
- bool sawmatch = false; // whether queue contains guaranteed kInstMatch
- bool sawmark = false; // whether queue contains a Mark
- if (DebugDFA)
+ uint32_t needflags = 0; // flags needed by kInstEmptyWidth instructions
+ bool sawmatch = false; // whether queue contains guaranteed kInstMatch
+ bool sawmark = false; // whether queue contains a Mark
+ if (ExtraDebug)
fprintf(stderr, "WorkqToCachedState %s [%#x]", DumpWorkq(q).c_str(), flag);
for (Workq::iterator it = q->begin(); it != q->end(); ++it) {
int id = *it;
@@ -640,36 +645,22 @@ DFA::State* DFA::WorkqToCachedState(Workq* q, uint flag) {
(kind_ != Prog::kLongestMatch || !sawmark) &&
(flag & kFlagMatch)) {
delete[] inst;
- if (DebugDFA)
+ if (ExtraDebug)
fprintf(stderr, " -> FullMatchState\n");
return FullMatchState;
}
- // Fall through.
- case kInstByteRange: // These are useful.
- case kInstEmptyWidth:
- case kInstMatch:
- case kInstAlt: // Not useful, but necessary [*]
- inst[n++] = *it;
+ FALLTHROUGH_INTENDED;
+ default:
+ // Record iff id is the head of its list, which must
+ // be the case if id-1 is the last of *its* list. :)
+ if (prog_->inst(id-1)->last())
+ inst[n++] = *it;
if (ip->opcode() == kInstEmptyWidth)
needflags |= ip->empty();
if (ip->opcode() == kInstMatch && !prog_->anchor_end())
sawmatch = true;
break;
-
- default: // The rest are not.
- break;
}
-
- // [*] kInstAlt would seem useless to record in a state, since
- // we've already followed both its arrows and saved all the
- // interesting states we can reach from there. The problem
- // is that one of the empty-width instructions might lead
- // back to the same kInstAlt (if an empty-width operator is starred),
- // producing a different evaluation order depending on whether
- // we keep the kInstAlt to begin with. Sigh.
- // A specific case that this affects is /(^|a)+/ matching "a".
- // If we don't save the kInstAlt, we will match the whole "a" (0,1)
- // but in fact the correct leftmost-first match is the leading "" (0,0).
}
DCHECK_LE(n, q->size());
if (n > 0 && inst[n-1] == Mark)
@@ -701,7 +692,7 @@ DFA::State* DFA::WorkqToCachedState(Workq* q, uint flag) {
// if the state is *not* a matching state.
if (n == 0 && flag == 0) {
delete[] inst;
- if (DebugDFA)
+ if (ExtraDebug)
fprintf(stderr, " -> DeadState\n");
return DeadState;
}
@@ -716,13 +707,24 @@ DFA::State* DFA::WorkqToCachedState(Workq* q, uint flag) {
int* markp = ip;
while (markp < ep && *markp != Mark)
markp++;
- sort(ip, markp);
+ std::sort(ip, markp);
if (markp < ep)
markp++;
ip = markp;
}
}
+ // Append MatchSep and the match IDs in mq if necessary.
+ if (mq != NULL) {
+ inst[n++] = MatchSep;
+ for (Workq::iterator i = mq->begin(); i != mq->end(); ++i) {
+ int id = *i;
+ Prog::Inst* ip = prog_->inst(id);
+ if (ip->opcode() == kInstMatch)
+ inst[n++] = ip->match_id();
+ }
+ }
+
// Save the needed empty-width flags in the top bits for use later.
flag |= needflags << kFlagNeedShift;
@@ -734,42 +736,50 @@ DFA::State* DFA::WorkqToCachedState(Workq* q, uint flag) {
// Looks in the State cache for a State matching inst, ninst, flag.
// If one is found, returns it. If one is not found, allocates one,
// inserts it in the cache, and returns it.
-DFA::State* DFA::CachedState(int* inst, int ninst, uint flag) {
- if (DEBUG_MODE)
- mutex_.AssertHeld();
+DFA::State* DFA::CachedState(int* inst, int ninst, uint32_t flag) {
+ //mutex_.AssertHeld();
// Look in the cache for a pre-existing state.
- State state = { inst, ninst, flag, NULL };
+ // We have to initialise the struct like this because otherwise
+ // MSVC will complain about the flexible array member. :(
+ State state;
+ state.inst_ = inst;
+ state.ninst_ = ninst;
+ state.flag_ = flag;
StateSet::iterator it = state_cache_.find(&state);
if (it != state_cache_.end()) {
- if (DebugDFA)
+ if (ExtraDebug)
fprintf(stderr, " -cached-> %s\n", DumpState(*it).c_str());
return *it;
}
// Must have enough memory for new state.
// In addition to what we're going to allocate,
- // the state cache hash table seems to incur about 32 bytes per
+ // the state cache hash table seems to incur about 40 bytes per
// State*, empirically.
- const int kStateCacheOverhead = 32;
+ const int kStateCacheOverhead = 40;
int nnext = prog_->bytemap_range() + 1; // + 1 for kByteEndText slot
- int mem = sizeof(State) + nnext*sizeof(State*) + ninst*sizeof(int);
+ int mem = sizeof(State) + nnext*sizeof(std::atomic<State*>) +
+ ninst*sizeof(int);
if (mem_budget_ < mem + kStateCacheOverhead) {
mem_budget_ = -1;
return NULL;
}
mem_budget_ -= mem + kStateCacheOverhead;
- // Allocate new state, along with room for next and inst.
- char* space = new char[mem];
- State* s = reinterpret_cast<State*>(space);
- s->next_ = reinterpret_cast<State**>(s + 1);
- s->inst_ = reinterpret_cast<int*>(s->next_ + nnext);
- memset(s->next_, 0, nnext*sizeof s->next_[0]);
+ // Allocate new state along with room for next_ and inst_.
+ char* space = std::allocator<char>().allocate(mem);
+ State* s = new (space) State;
+ (void) new (s->next_) std::atomic<State*>[nnext];
+ // Work around a unfortunate bug in older versions of libstdc++.
+ // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64658)
+ for (int i = 0; i < nnext; i++)
+ (void) new (s->next_ + i) std::atomic<State*>(NULL);
+ s->inst_ = new (s->next_ + nnext) int[ninst];
memmove(s->inst_, inst, ninst*sizeof s->inst_[0]);
s->ninst_ = ninst;
s->flag_ = flag;
- if (DebugDFA)
+ if (ExtraDebug)
fprintf(stderr, " -> %s\n", DumpState(s).c_str());
// Put state in cache and return it.
@@ -779,47 +789,59 @@ DFA::State* DFA::CachedState(int* inst, int ninst, uint flag) {
// Clear the cache. Must hold cache_mutex_.w or be in destructor.
void DFA::ClearCache() {
- // In case state_cache_ doesn't support deleting entries
- // during iteration, copy into a vector and then delete.
- vector<State*> v;
- v.reserve(state_cache_.size());
- for (StateSet::iterator it = state_cache_.begin();
- it != state_cache_.end(); ++it)
- v.push_back(*it);
+ StateSet::iterator begin = state_cache_.begin();
+ StateSet::iterator end = state_cache_.end();
+ while (begin != end) {
+ StateSet::iterator tmp = begin;
+ ++begin;
+ // Deallocate the blob of memory that we allocated in DFA::CachedState().
+ // We recompute mem in order to benefit from sized delete where possible.
+ int ninst = (*tmp)->ninst_;
+ int nnext = prog_->bytemap_range() + 1; // + 1 for kByteEndText slot
+ int mem = sizeof(State) + nnext*sizeof(std::atomic<State*>) +
+ ninst*sizeof(int);
+ std::allocator<char>().deallocate(reinterpret_cast<char*>(*tmp), mem);
+ }
state_cache_.clear();
- for (int i = 0; i < v.size(); i++)
- delete[] reinterpret_cast<const char*>(v[i]);
}
// Copies insts in state s to the work queue q.
void DFA::StateToWorkq(State* s, Workq* q) {
q->clear();
for (int i = 0; i < s->ninst_; i++) {
- if (s->inst_[i] == Mark)
+ if (s->inst_[i] == Mark) {
q->mark();
- else
- q->insert_new(s->inst_[i]);
+ } else if (s->inst_[i] == MatchSep) {
+ // Nothing after this is an instruction!
+ break;
+ } else {
+ // Explore from the head of the list.
+ AddToQueue(q, s->inst_[i], s->flag_ & kFlagEmptyMask);
+ }
}
}
-// Adds ip to the work queue, following empty arrows according to flag
-// and expanding kInstAlt instructions (two-target gotos).
-void DFA::AddToQueue(Workq* q, int id, uint flag) {
-
- // Use astack_ to hold our stack of states yet to process.
- // It is sized to have room for nastack_ == 2*prog->size() + nmark
- // instructions, which is enough: each instruction can be
- // processed by the switch below only once, and the processing
- // pushes at most two instructions plus maybe a mark.
- // (If we're using marks, nmark == prog->size(); otherwise nmark == 0.)
- int* stk = astack_;
+// Adds ip to the work queue, following empty arrows according to flag.
+void DFA::AddToQueue(Workq* q, int id, uint32_t flag) {
+
+ // Use stack_ to hold our stack of instructions yet to process.
+ // It was preallocated as follows:
+ // one entry per Capture;
+ // one entry per EmptyWidth; and
+ // one entry per Nop.
+ // This reflects the maximum number of stack pushes that each can
+ // perform. (Each instruction can be processed at most once.)
+ // When using marks, we also added nmark == prog_->size().
+ // (Otherwise, nmark == 0.)
+ int* stk = stack_.data();
int nstk = 0;
stk[nstk++] = id;
while (nstk > 0) {
- DCHECK_LE(nstk, nastack_);
+ DCHECK_LE(nstk, stack_.size());
id = stk[--nstk];
+ Loop:
if (id == Mark) {
q->mark();
continue;
@@ -829,9 +851,8 @@ void DFA::AddToQueue(Workq* q, int id, uint flag) {
continue;
// If ip is already on the queue, nothing to do.
- // Otherwise add it. We don't actually keep all the ones
- // that get added -- for example, kInstAlt is ignored
- // when on a work queue -- but adding all ip's here
+ // Otherwise add it. We don't actually keep all the
+ // ones that get added, but adding all of them here
// increases the likelihood of q->contains(id),
// reducing the amount of duplicated work.
if (q->contains(id))
@@ -841,37 +862,46 @@ void DFA::AddToQueue(Workq* q, int id, uint flag) {
// Process instruction.
Prog::Inst* ip = prog_->inst(id);
switch (ip->opcode()) {
- case kInstFail: // can't happen: discarded above
+ default:
+ LOG(DFATAL) << "unhandled opcode: " << ip->opcode();
break;
case kInstByteRange: // just save these on the queue
case kInstMatch:
- break;
+ if (ip->last())
+ break;
+ id = id+1;
+ goto Loop;
case kInstCapture: // DFA treats captures as no-ops.
case kInstNop:
- stk[nstk++] = ip->out();
- break;
-
- case kInstAlt: // two choices: expand both, in order
- case kInstAltMatch:
- // Want to visit out then out1, so push on stack in reverse order.
- // This instruction is the [00-FF]* loop at the beginning of
- // a leftmost-longest unanchored search, separate out from out1
- // with a Mark, so that out1's threads (which will start farther
- // to the right in the string being searched) are lower priority
- // than the current ones.
- stk[nstk++] = ip->out1();
- if (q->maxmark() > 0 &&
+ if (!ip->last())
+ stk[nstk++] = id+1;
+
+ // If this instruction is the [00-FF]* loop at the beginning of
+ // a leftmost-longest unanchored search, separate with a Mark so
+ // that future threads (which will start farther to the right in
+ // the input string) are lower priority than current threads.
+ if (ip->opcode() == kInstNop && q->maxmark() > 0 &&
id == prog_->start_unanchored() && id != prog_->start())
stk[nstk++] = Mark;
- stk[nstk++] = ip->out();
- break;
+ id = ip->out();
+ goto Loop;
+
+ case kInstAltMatch:
+ DCHECK(!ip->last());
+ id = id+1;
+ goto Loop;
case kInstEmptyWidth:
- if ((ip->empty() & flag) == ip->empty())
- stk[nstk++] = ip->out();
- break;
+ if (!ip->last())
+ stk[nstk++] = id+1;
+
+ // Continue on if we have all the right flag bits.
+ if (ip->empty() & ~flag)
+ break;
+ id = ip->out();
+ goto Loop;
}
}
}
@@ -892,7 +922,7 @@ void DFA::AddToQueue(Workq* q, int id, uint flag) {
// and then processing only $. Doing the two-step sequence won't match
// ^$^$^$ but processing ^ and $ simultaneously will (and is the behavior
// exhibited by existing implementations).
-void DFA::RunWorkqOnEmptyString(Workq* oldq, Workq* newq, uint flag) {
+void DFA::RunWorkqOnEmptyString(Workq* oldq, Workq* newq, uint32_t flag) {
newq->clear();
for (Workq::iterator i = oldq->begin(); i != oldq->end(); ++i) {
if (oldq->is_mark(*i))
@@ -907,11 +937,8 @@ void DFA::RunWorkqOnEmptyString(Workq* oldq, Workq* newq, uint flag) {
// means to match c$. Sets the bool *ismatch to true if the end of the
// regular expression program has been reached (the regexp has matched).
void DFA::RunWorkqOnByte(Workq* oldq, Workq* newq,
- int c, uint flag, bool* ismatch,
- Prog::MatchKind kind,
- int new_byte_loop) {
- if (DEBUG_MODE)
- mutex_.AssertHeld();
+ int c, uint32_t flag, bool* ismatch) {
+ //mutex_.AssertHeld();
newq->clear();
for (Workq::iterator i = oldq->begin(); i != oldq->end(); ++i) {
@@ -924,10 +951,13 @@ void DFA::RunWorkqOnByte(Workq* oldq, Workq* newq,
int id = *i;
Prog::Inst* ip = prog_->inst(id);
switch (ip->opcode()) {
+ default:
+ LOG(DFATAL) << "unhandled opcode: " << ip->opcode();
+ break;
+
case kInstFail: // never succeeds
case kInstCapture: // already followed
case kInstNop: // already followed
- case kInstAlt: // already followed
case kInstAltMatch: // already followed
case kInstEmptyWidth: // already followed
break;
@@ -938,10 +968,11 @@ void DFA::RunWorkqOnByte(Workq* oldq, Workq* newq,
break;
case kInstMatch:
- if (prog_->anchor_end() && c != kByteEndText)
+ if (prog_->anchor_end() && c != kByteEndText &&
+ kind_ != Prog::kManyMatch)
break;
*ismatch = true;
- if (kind == Prog::kFirstMatch) {
+ if (kind_ == Prog::kFirstMatch) {
// Can stop processing work queue since we found a match.
return;
}
@@ -949,7 +980,7 @@ void DFA::RunWorkqOnByte(Workq* oldq, Workq* newq,
}
}
- if (DebugDFA)
+ if (ExtraDebug)
fprintf(stderr, "%s on %d[%#x] -> %s [%d]\n", DumpWorkq(oldq).c_str(),
c, flag, DumpWorkq(newq).c_str(), *ismatch);
}
@@ -965,8 +996,8 @@ DFA::State* DFA::RunStateOnByteUnlocked(State* state, int c) {
// Processes input byte c in state, returning new state.
DFA::State* DFA::RunStateOnByte(State* state, int c) {
- if (DEBUG_MODE)
- mutex_.AssertHeld();
+ //mutex_.AssertHeld();
+
if (state <= SpecialStateMax) {
if (state == FullMatchState) {
// It is convenient for routines like PossibleMatchRange
@@ -988,9 +1019,7 @@ DFA::State* DFA::RunStateOnByte(State* state, int c) {
}
// If someone else already computed this, return it.
- MaybeReadMemoryBarrier(); // On alpha we need to ensure read ordering
- State* ns = state->next_[ByteMap(c)];
- ANNOTATE_HAPPENS_AFTER(ns);
+ State* ns = state->next_[ByteMap(c)].load(std::memory_order_relaxed);
if (ns != NULL)
return ns;
@@ -1001,10 +1030,10 @@ DFA::State* DFA::RunStateOnByte(State* state, int c) {
// around this byte. Before the byte we have the flags recorded
// in the State structure itself. After the byte we have
// nothing yet (but that will change: read on).
- uint needflag = state->flag_ >> kFlagNeedShift;
- uint beforeflag = state->flag_ & kFlagEmptyMask;
- uint oldbeforeflag = beforeflag;
- uint afterflag = 0;
+ uint32_t needflag = state->flag_ >> kFlagNeedShift;
+ uint32_t beforeflag = state->flag_ & kFlagEmptyMask;
+ uint32_t oldbeforeflag = beforeflag;
+ uint32_t afterflag = 0;
if (c == '\n') {
// Insert implicit $ and ^ around \n
@@ -1020,8 +1049,8 @@ DFA::State* DFA::RunStateOnByte(State* state, int c) {
// The state flag kFlagLastWord says whether the last
// byte processed was a word character. Use that info to
// insert empty-width (non-)word boundaries.
- bool islastword = state->flag_ & kFlagLastWord;
- bool isword = (c != kByteEndText && Prog::IsWordChar(c));
+ bool islastword = (state->flag_ & kFlagLastWord) != 0;
+ bool isword = c != kByteEndText && Prog::IsWordChar(static_cast<uint8_t>(c));
if (isword == islastword)
beforeflag |= kEmptyNonWordBoundary;
else
@@ -1031,43 +1060,31 @@ DFA::State* DFA::RunStateOnByte(State* state, int c) {
// Only useful to rerun on empty string if there are new, useful flags.
if (beforeflag & ~oldbeforeflag & needflag) {
RunWorkqOnEmptyString(q0_, q1_, beforeflag);
+ using std::swap;
swap(q0_, q1_);
}
bool ismatch = false;
- RunWorkqOnByte(q0_, q1_, c, afterflag, &ismatch, kind_, start_unanchored_);
-
- // Most of the time, we build the state from the output of
- // RunWorkqOnByte, so swap q0_ and q1_ here. However, so that
- // RE2::Set can tell exactly which match instructions
- // contributed to the match, don't swap if c is kByteEndText.
- // The resulting state wouldn't be correct for further processing
- // of the string, but we're at the end of the text so that's okay.
- // Leaving q0_ alone preseves the match instructions that led to
- // the current setting of ismatch.
- if (c != kByteEndText || kind_ != Prog::kManyMatch)
- swap(q0_, q1_);
+ RunWorkqOnByte(q0_, q1_, c, afterflag, &ismatch);
+ using std::swap;
+ swap(q0_, q1_);
// Save afterflag along with ismatch and isword in new state.
- uint flag = afterflag;
+ uint32_t flag = afterflag;
if (ismatch)
flag |= kFlagMatch;
if (isword)
flag |= kFlagLastWord;
- ns = WorkqToCachedState(q0_, flag);
+ if (ismatch && kind_ == Prog::kManyMatch)
+ ns = WorkqToCachedState(q0_, q1_, flag);
+ else
+ ns = WorkqToCachedState(q0_, NULL, flag);
+ // Flush ns before linking to it.
// Write barrier before updating state->next_ so that the
// main search loop can proceed without any locking, for speed.
// (Otherwise it would need one mutex operation per input byte.)
- // The annotations below tell race detectors that:
- // a) the access to next_ should be ignored,
- // b) 'ns' is properly published.
- WriteMemoryBarrier(); // Flush ns before linking to it.
-
- ANNOTATE_IGNORE_WRITES_BEGIN();
- ANNOTATE_HAPPENS_BEFORE(ns);
- state->next_[ByteMap(c)] = ns;
- ANNOTATE_IGNORE_WRITES_END();
+ state->next_[ByteMap(c)].store(ns, std::memory_order_release);
return ns;
}
@@ -1101,21 +1118,15 @@ class DFA::RWLocker {
// Notice that the lock is *released* temporarily.
void LockForWriting();
- // Returns whether the lock is already held for writing.
- bool IsLockedForWriting() {
- return writing_;
- }
-
private:
Mutex* mu_;
bool writing_;
- DISALLOW_EVIL_CONSTRUCTORS(RWLocker);
+ RWLocker(const RWLocker&) = delete;
+ RWLocker& operator=(const RWLocker&) = delete;
};
-DFA::RWLocker::RWLocker(Mutex* mu)
- : mu_(mu), writing_(false) {
-
+DFA::RWLocker::RWLocker(Mutex* mu) : mu_(mu), writing_(false) {
mu_->ReaderLock();
}
@@ -1124,16 +1135,16 @@ DFA::RWLocker::RWLocker(Mutex* mu)
void DFA::RWLocker::LockForWriting() NO_THREAD_SAFETY_ANALYSIS {
if (!writing_) {
mu_->ReaderUnlock();
- mu_->Lock();
+ mu_->WriterLock();
writing_ = true;
}
}
DFA::RWLocker::~RWLocker() {
- if (writing_)
- mu_->WriterUnlock();
- else
+ if (!writing_)
mu_->ReaderUnlock();
+ else
+ mu_->WriterUnlock();
}
@@ -1150,24 +1161,12 @@ DFA::RWLocker::~RWLocker() {
void DFA::ResetCache(RWLocker* cache_lock) {
// Re-acquire the cache_mutex_ for writing (exclusive use).
- bool was_writing = cache_lock->IsLockedForWriting();
cache_lock->LockForWriting();
- // If we already held cache_mutex_ for writing, it means
- // this invocation of Search() has already reset the
- // cache once already. That's a pretty clear indication
- // that the cache is too small. Warn about that, once.
- // TODO(rsc): Only warn if state_cache_.size() < some threshold.
- if (was_writing && !cache_warned_) {
- LOG(INFO) << "DFA memory cache could be too small: "
- << "only room for " << state_cache_.size() << " states.";
- cache_warned_ = true;
- }
-
// Clear the cache, reset the memory budget.
for (int i = 0; i < kMaxStart; i++) {
start_[i].start = NULL;
- start_[i].firstbyte = kFbUnknown;
+ start_[i].first_byte.store(kFbUnknown, std::memory_order_relaxed);
}
ClearCache();
mem_budget_ = state_budget_;
@@ -1206,11 +1205,12 @@ class DFA::StateSaver {
DFA* dfa_; // the DFA to use
int* inst_; // saved info from State
int ninst_;
- uint flag_;
+ uint32_t flag_;
bool is_special_; // whether original state was special
State* special_; // if is_special_, the original state
- DISALLOW_EVIL_CONSTRUCTORS(StateSaver);
+ StateSaver(const StateSaver&) = delete;
+ StateSaver& operator=(const StateSaver&) = delete;
};
DFA::StateSaver::StateSaver(DFA* dfa, State* state) {
@@ -1283,7 +1283,7 @@ DFA::State* DFA::StateSaver::Restore() {
// Instead, it can call memchr to search very quickly for the byte c.
// Whether the start state has this property is determined during a
// pre-compilation pass, and if so, the byte b is passed to the search
-// loop as the "firstbyte" argument, along with a boolean "have_firstbyte".
+// loop as the "first_byte" argument, along with a boolean "have_first_byte".
//
// Fourth, the desired behavior is to search for the leftmost-best match
// (approximately, the same one that Perl would find), which is not
@@ -1316,25 +1316,40 @@ DFA::State* DFA::StateSaver::Restore() {
// making them function arguments lets the inliner specialize
// this function to each combination (see two paragraphs above).
inline bool DFA::InlinedSearchLoop(SearchParams* params,
- bool have_firstbyte,
+ bool have_first_byte,
bool want_earliest_match,
bool run_forward) {
State* start = params->start;
- const uint8* bp = BytePtr(params->text.begin()); // start of text
- const uint8* p = bp; // text scanning point
- const uint8* ep = BytePtr(params->text.end()); // end of text
- const uint8* resetp = NULL; // p at last cache reset
- if (!run_forward)
+ const uint8_t* bp = BytePtr(params->text.begin()); // start of text
+ const uint8_t* p = bp; // text scanning point
+ const uint8_t* ep = BytePtr(params->text.end()); // end of text
+ const uint8_t* resetp = NULL; // p at last cache reset
+ if (!run_forward) {
+ using std::swap;
swap(p, ep);
+ }
- const uint8* bytemap = prog_->bytemap();
- const uint8* lastmatch = NULL; // most recent matching position in text
+ const uint8_t* bytemap = prog_->bytemap();
+ const uint8_t* lastmatch = NULL; // most recent matching position in text
bool matched = false;
+
State* s = start;
+ if (ExtraDebug)
+ fprintf(stderr, "@stx: %s\n", DumpState(s).c_str());
if (s->IsMatch()) {
matched = true;
lastmatch = p;
+ if (ExtraDebug)
+ fprintf(stderr, "match @stx! [%s]\n", DumpState(s).c_str());
+ if (params->matches != NULL && kind_ == Prog::kManyMatch) {
+ for (int i = s->ninst_ - 1; i >= 0; i--) {
+ int id = s->inst_[i];
+ if (id == MatchSep)
+ break;
+ params->matches->insert(id);
+ }
+ }
if (want_earliest_match) {
params->ep = reinterpret_cast<const char*>(lastmatch);
return true;
@@ -1342,21 +1357,22 @@ inline bool DFA::InlinedSearchLoop(SearchParams* params,
}
while (p != ep) {
- if (DebugDFA)
- fprintf(stderr, "@%d: %s\n", static_cast<int>(p - bp),
- DumpState(s).c_str());
- if (have_firstbyte && s == start) {
- // In start state, only way out is to find firstbyte,
+ if (ExtraDebug)
+ fprintf(stderr, "@%td: %s\n",
+ p - bp, DumpState(s).c_str());
+
+ if (have_first_byte && s == start) {
+ // In start state, only way out is to find first_byte,
// so use optimized assembly in memchr to skip ahead.
- // If firstbyte isn't found, we can skip to the end
+ // If first_byte isn't found, we can skip to the end
// of the string.
if (run_forward) {
- if ((p = BytePtr(memchr(p, params->firstbyte, ep - p))) == NULL) {
+ if ((p = BytePtr(memchr(p, params->first_byte, ep - p))) == NULL) {
p = ep;
break;
}
} else {
- if ((p = BytePtr(memrchr(ep, params->firstbyte, p - ep))) == NULL) {
+ if ((p = BytePtr(memrchr(ep, params->first_byte, p - ep))) == NULL) {
p = ep;
break;
}
@@ -1388,9 +1404,7 @@ inline bool DFA::InlinedSearchLoop(SearchParams* params,
// Okay to use bytemap[] not ByteMap() here, because
// c is known to be an actual byte and not kByteEndText.
- MaybeReadMemoryBarrier(); // On alpha we need to ensure read ordering
- State* ns = s->next_[bytemap[c]];
- ANNOTATE_HAPPENS_AFTER(ns);
+ State* ns = s->next_[bytemap[c]].load(std::memory_order_acquire);
if (ns == NULL) {
ns = RunStateOnByteUnlocked(s, c);
if (ns == NULL) {
@@ -1402,8 +1416,8 @@ inline bool DFA::InlinedSearchLoop(SearchParams* params,
// same at about 2 MB/s. Unless we're processing an average
// of 10 bytes per state computation, fail so that RE2 can
// fall back to the NFA.
- if (FLAGS_re2_dfa_bail_when_slow && resetp != NULL &&
- (p - resetp) < 10*state_cache_.size()) {
+ if (dfa_should_bail_when_slow && resetp != NULL &&
+ static_cast<size_t>(p - resetp) < 10*state_cache_.size()) {
params->failed = true;
return false;
}
@@ -1440,8 +1454,8 @@ inline bool DFA::InlinedSearchLoop(SearchParams* params,
params->ep = reinterpret_cast<const char*>(ep);
return true;
}
- s = ns;
+ s = ns;
if (s->IsMatch()) {
matched = true;
// The DFA notices the match one byte late,
@@ -1450,11 +1464,17 @@ inline bool DFA::InlinedSearchLoop(SearchParams* params,
lastmatch = p - 1;
else
lastmatch = p + 1;
- if (DebugDFA)
- fprintf(stderr, "match @%d! [%s]\n",
- static_cast<int>(lastmatch - bp),
- DumpState(s).c_str());
-
+ if (ExtraDebug)
+ fprintf(stderr, "match @%td! [%s]\n",
+ lastmatch - bp, DumpState(s).c_str());
+ if (params->matches != NULL && kind_ == Prog::kManyMatch) {
+ for (int i = s->ninst_ - 1; i >= 0; i--) {
+ int id = s->inst_[i];
+ if (id == MatchSep)
+ break;
+ params->matches->insert(id);
+ }
+ }
if (want_earliest_match) {
params->ep = reinterpret_cast<const char*>(lastmatch);
return true;
@@ -1464,6 +1484,9 @@ inline bool DFA::InlinedSearchLoop(SearchParams* params,
// Process one more byte to see if it triggers a match.
// (Remember, matches are delayed one byte.)
+ if (ExtraDebug)
+ fprintf(stderr, "@etx: %s\n", DumpState(s).c_str());
+
int lastbyte;
if (run_forward) {
if (params->text.end() == params->context.end())
@@ -1477,9 +1500,7 @@ inline bool DFA::InlinedSearchLoop(SearchParams* params,
lastbyte = params->text.begin()[-1] & 0xFF;
}
- MaybeReadMemoryBarrier(); // On alpha we need to ensure read ordering
- State* ns = s->next_[ByteMap(lastbyte)];
- ANNOTATE_HAPPENS_AFTER(ns);
+ State* ns = s->next_[ByteMap(lastbyte)].load(std::memory_order_acquire);
if (ns == NULL) {
ns = RunStateOnByteUnlocked(s, lastbyte);
if (ns == NULL) {
@@ -1497,29 +1518,32 @@ inline bool DFA::InlinedSearchLoop(SearchParams* params,
}
}
}
- s = ns;
- if (DebugDFA)
- fprintf(stderr, "@_: %s\n", DumpState(s).c_str());
- if (s == FullMatchState) {
+ if (ns <= SpecialStateMax) {
+ if (ns == DeadState) {
+ params->ep = reinterpret_cast<const char*>(lastmatch);
+ return matched;
+ }
+ // FullMatchState
params->ep = reinterpret_cast<const char*>(ep);
return true;
}
- if (s > SpecialStateMax && s->IsMatch()) {
+
+ s = ns;
+ if (s->IsMatch()) {
matched = true;
lastmatch = p;
- if (params->matches && kind_ == Prog::kManyMatch) {
- vector<int>* v = params->matches;
- v->clear();
- for (int i = 0; i < s->ninst_; i++) {
- Prog::Inst* ip = prog_->inst(s->inst_[i]);
- if (ip->opcode() == kInstMatch)
- v->push_back(ip->match_id());
+ if (ExtraDebug)
+ fprintf(stderr, "match @etx! [%s]\n", DumpState(s).c_str());
+ if (params->matches != NULL && kind_ == Prog::kManyMatch) {
+ for (int i = s->ninst_ - 1; i >= 0; i--) {
+ int id = s->inst_[i];
+ if (id == MatchSep)
+ break;
+ params->matches->insert(id);
}
}
- if (DebugDFA)
- fprintf(stderr, "match @%d! [%s]\n", static_cast<int>(lastmatch - bp),
- DumpState(s).c_str());
}
+
params->ep = reinterpret_cast<const char*>(lastmatch);
return matched;
}
@@ -1553,7 +1577,7 @@ bool DFA::SearchTTT(SearchParams* params) {
// For debugging, calls the general code directly.
bool DFA::SlowSearchLoop(SearchParams* params) {
return InlinedSearchLoop(params,
- params->firstbyte >= 0,
+ params->first_byte >= 0,
params->want_earliest_match,
params->run_forward);
}
@@ -1574,8 +1598,8 @@ bool DFA::FastSearchLoop(SearchParams* params) {
&DFA::SearchTTT,
};
- bool have_firstbyte = (params->firstbyte >= 0);
- int index = 4 * have_firstbyte +
+ bool have_first_byte = params->first_byte >= 0;
+ int index = 4 * have_first_byte +
2 * params->want_earliest_match +
1 * params->run_forward;
return (this->*Searches[index])(params);
@@ -1614,14 +1638,14 @@ bool DFA::AnalyzeSearch(SearchParams* params) {
// Sanity check: make sure that text lies within context.
if (text.begin() < context.begin() || text.end() > context.end()) {
- LOG(DFATAL) << "Text is not inside context.";
+ LOG(DFATAL) << "context does not contain text";
params->start = DeadState;
return true;
}
// Determine correct search type.
int start;
- uint flags;
+ uint32_t flags;
if (params->run_forward) {
if (text.begin() == context.begin()) {
start = kStartBeginText;
@@ -1651,7 +1675,7 @@ bool DFA::AnalyzeSearch(SearchParams* params) {
flags = 0;
}
}
- if (params->anchored || prog_->anchor_start())
+ if (params->anchored)
start |= kStartAnchored;
StartInfo* info = &start_[start];
@@ -1667,79 +1691,62 @@ bool DFA::AnalyzeSearch(SearchParams* params) {
}
}
- if (DebugDFA)
- fprintf(stderr, "anchored=%d fwd=%d flags=%#x state=%s firstbyte=%d\n",
+ if (ExtraDebug)
+ fprintf(stderr, "anchored=%d fwd=%d flags=%#x state=%s first_byte=%d\n",
params->anchored, params->run_forward, flags,
- DumpState(info->start).c_str(), info->firstbyte);
+ DumpState(info->start).c_str(), info->first_byte.load());
params->start = info->start;
- params->firstbyte = ANNOTATE_UNPROTECTED_READ(info->firstbyte);
+ params->first_byte = info->first_byte.load(std::memory_order_acquire);
return true;
}
// Fills in info if needed. Returns true on success, false on failure.
bool DFA::AnalyzeSearchHelper(SearchParams* params, StartInfo* info,
- uint flags) {
- // Quick check; okay because of memory barriers below.
- if (ANNOTATE_UNPROTECTED_READ(info->firstbyte) != kFbUnknown) {
- ANNOTATE_HAPPENS_AFTER(&info->firstbyte);
+ uint32_t flags) {
+ // Quick check.
+ int fb = info->first_byte.load(std::memory_order_acquire);
+ if (fb != kFbUnknown)
return true;
- }
MutexLock l(&mutex_);
- if (info->firstbyte != kFbUnknown) {
- ANNOTATE_HAPPENS_AFTER(&info->firstbyte);
+ fb = info->first_byte.load(std::memory_order_relaxed);
+ if (fb != kFbUnknown)
return true;
- }
q0_->clear();
AddToQueue(q0_,
params->anchored ? prog_->start() : prog_->start_unanchored(),
flags);
- info->start = WorkqToCachedState(q0_, flags);
+ info->start = WorkqToCachedState(q0_, NULL, flags);
if (info->start == NULL)
return false;
if (info->start == DeadState) {
- ANNOTATE_HAPPENS_BEFORE(&info->firstbyte);
- WriteMemoryBarrier(); // Synchronize with "quick check" above.
- info->firstbyte = kFbNone;
+ // Synchronize with "quick check" above.
+ info->first_byte.store(kFbNone, std::memory_order_release);
return true;
}
if (info->start == FullMatchState) {
- ANNOTATE_HAPPENS_BEFORE(&info->firstbyte);
- WriteMemoryBarrier(); // Synchronize with "quick check" above.
- info->firstbyte = kFbNone; // will be ignored
+ // Synchronize with "quick check" above.
+ info->first_byte.store(kFbNone, std::memory_order_release); // will be ignored
return true;
}
- // Compute info->firstbyte by running state on all
- // possible byte values, looking for a single one that
- // leads to a different state.
- int firstbyte = kFbNone;
- for (int i = 0; i < 256; i++) {
- State* s = RunStateOnByte(info->start, i);
- if (s == NULL) {
- ANNOTATE_HAPPENS_BEFORE(&info->firstbyte);
- WriteMemoryBarrier(); // Synchronize with "quick check" above.
- info->firstbyte = firstbyte;
- return false;
- }
- if (s == info->start)
- continue;
- // Goes to new state...
- if (firstbyte == kFbNone) {
- firstbyte = i; // ... first one
- } else {
- firstbyte = kFbMany; // ... too many
- break;
- }
- }
- ANNOTATE_HAPPENS_BEFORE(&info->firstbyte);
- WriteMemoryBarrier(); // Synchronize with "quick check" above.
- info->firstbyte = firstbyte;
+ // Even if we have a first_byte, we cannot use it when anchored and,
+ // less obviously, we cannot use it when we are going to need flags.
+ // This trick works only when there is a single byte that leads to a
+ // different state!
+ int first_byte = prog_->first_byte();
+ if (first_byte == -1 ||
+ params->anchored ||
+ info->start->flag_ >> kFlagNeedShift != 0)
+ first_byte = kFbNone;
+
+ // Synchronize with "quick check" above.
+ info->first_byte.store(first_byte, std::memory_order_release);
return true;
}
@@ -1751,7 +1758,7 @@ bool DFA::Search(const StringPiece& text,
bool run_forward,
bool* failed,
const char** epp,
- vector<int>* matches) {
+ SparseSet* matches) {
*epp = NULL;
if (!ok()) {
*failed = true;
@@ -1759,10 +1766,10 @@ bool DFA::Search(const StringPiece& text,
}
*failed = false;
- if (DebugDFA) {
+ if (ExtraDebug) {
fprintf(stderr, "\nprogram:\n%s\n", prog_->DumpUnanchored().c_str());
fprintf(stderr, "text %s anchored=%d earliest=%d fwd=%d kind %d\n",
- text.as_string().c_str(), anchored, want_earliest_match,
+ string(text).c_str(), anchored, want_earliest_match,
run_forward, kind_);
}
@@ -1786,7 +1793,7 @@ bool DFA::Search(const StringPiece& text,
*epp = text.end();
return true;
}
- if (DebugDFA)
+ if (ExtraDebug)
fprintf(stderr, "start %s\n", DumpState(params.start).c_str());
bool ret = FastSearchLoop(&params);
if (params.failed) {
@@ -1797,64 +1804,38 @@ bool DFA::Search(const StringPiece& text,
return ret;
}
-// Deletes dfa.
-//
-// This is a separate function so that
-// prog.h can be used without moving the definition of
-// class DFA out of this file. If you set
-// prog->dfa_ = dfa;
-// then you also have to set
-// prog->delete_dfa_ = DeleteDFA;
-// so that ~Prog can delete the dfa.
-static void DeleteDFA(DFA* dfa) {
- delete dfa;
-}
-
DFA* Prog::GetDFA(MatchKind kind) {
- DFA*volatile* pdfa;
- if (kind == kFirstMatch || kind == kManyMatch) {
- pdfa = &dfa_first_;
- } else {
- kind = kLongestMatch;
- pdfa = &dfa_longest_;
- }
-
- // Quick check; okay because of memory barrier below.
- DFA *dfa = ANNOTATE_UNPROTECTED_READ(*pdfa);
- if (dfa != NULL) {
- ANNOTATE_HAPPENS_AFTER(dfa);
- return dfa;
- }
-
- MutexLock l(&dfa_mutex_);
- dfa = *pdfa;
- if (dfa != NULL) {
- ANNOTATE_HAPPENS_AFTER(dfa);
- return dfa;
- }
-
// For a forward DFA, half the memory goes to each DFA.
+ // However, if it is a "many match" DFA, then there is
+ // no counterpart with which the memory must be shared.
+ //
// For a reverse DFA, all the memory goes to the
// "longest match" DFA, because RE2 never does reverse
// "first match" searches.
- int64 m = dfa_mem_/2;
- if (reversed_) {
- if (kind == kLongestMatch || kind == kManyMatch)
- m = dfa_mem_;
- else
- m = 0;
+ if (kind == kFirstMatch) {
+ std::call_once(dfa_first_once_, [](Prog* prog) {
+ prog->dfa_first_ = new DFA(prog, kFirstMatch, prog->dfa_mem_ / 2);
+ }, this);
+ return dfa_first_;
+ } else if (kind == kManyMatch) {
+ std::call_once(dfa_first_once_, [](Prog* prog) {
+ prog->dfa_first_ = new DFA(prog, kManyMatch, prog->dfa_mem_);
+ }, this);
+ return dfa_first_;
+ } else {
+ std::call_once(dfa_longest_once_, [](Prog* prog) {
+ if (!prog->reversed_)
+ prog->dfa_longest_ = new DFA(prog, kLongestMatch, prog->dfa_mem_ / 2);
+ else
+ prog->dfa_longest_ = new DFA(prog, kLongestMatch, prog->dfa_mem_);
+ }, this);
+ return dfa_longest_;
}
- dfa = new DFA(this, kind, m);
- delete_dfa_ = DeleteDFA;
-
- // Synchronize with "quick check" above.
- ANNOTATE_HAPPENS_BEFORE(dfa);
- WriteMemoryBarrier();
- *pdfa = dfa;
-
- return dfa;
}
+void Prog::DeleteDFA(DFA* dfa) {
+ delete dfa;
+}
// Executes the regexp program to search in text,
// which itself is inside the larger context. (As a convenience,
@@ -1867,8 +1848,8 @@ DFA* Prog::GetDFA(MatchKind kind) {
// This is the only external interface (class DFA only exists in this file).
//
bool Prog::SearchDFA(const StringPiece& text, const StringPiece& const_context,
- Anchor anchor, MatchKind kind,
- StringPiece* match0, bool* failed, vector<int>* matches) {
+ Anchor anchor, MatchKind kind, StringPiece* match0,
+ bool* failed, SparseSet* matches) {
*failed = false;
StringPiece context = const_context;
@@ -1877,9 +1858,8 @@ bool Prog::SearchDFA(const StringPiece& text, const StringPiece& const_context,
bool carat = anchor_start();
bool dollar = anchor_end();
if (reversed_) {
- bool t = carat;
- carat = dollar;
- dollar = t;
+ using std::swap;
+ swap(carat, dollar);
}
if (carat && context.begin() != text.begin())
return false;
@@ -1891,7 +1871,7 @@ bool Prog::SearchDFA(const StringPiece& text, const StringPiece& const_context,
bool anchored = anchor == kAnchored || anchor_start() || kind == kFullMatch;
bool endmatch = false;
if (kind == kManyMatch) {
- endmatch = true;
+ // This is split out in order to avoid clobbering kind.
} else if (kind == kFullMatch || anchor_end()) {
endmatch = true;
kind = kLongestMatch;
@@ -1899,17 +1879,22 @@ bool Prog::SearchDFA(const StringPiece& text, const StringPiece& const_context,
// If the caller doesn't care where the match is (just whether one exists),
// then we can stop at the very first match we find, the so-called
- // "shortest match".
- bool want_shortest_match = false;
- if (match0 == NULL && !endmatch) {
- want_shortest_match = true;
+ // "earliest match".
+ bool want_earliest_match = false;
+ if (kind == kManyMatch) {
+ // This is split out in order to avoid clobbering kind.
+ if (matches == NULL) {
+ want_earliest_match = true;
+ }
+ } else if (match0 == NULL && !endmatch) {
+ want_earliest_match = true;
kind = kLongestMatch;
}
DFA* dfa = GetDFA(kind);
const char* ep;
bool matched = dfa->Search(text, context, anchored,
- want_shortest_match, !reversed_,
+ want_earliest_match, !reversed_,
failed, &ep, matches);
if (*failed)
return false;
@@ -1923,51 +1908,89 @@ bool Prog::SearchDFA(const StringPiece& text, const StringPiece& const_context,
// as the beginning.
if (match0) {
if (reversed_)
- *match0 = StringPiece(ep, text.end() - ep);
+ *match0 = StringPiece(ep, static_cast<size_t>(text.end() - ep));
else
- *match0 = StringPiece(text.begin(), ep - text.begin());
+ *match0 =
+ StringPiece(text.begin(), static_cast<size_t>(ep - text.begin()));
}
return true;
}
// Build out all states in DFA. Returns number of states.
-int DFA::BuildAllStates() {
+int DFA::BuildAllStates(const Prog::DFAStateCallback& cb) {
if (!ok())
return 0;
// Pick out start state for unanchored search
// at beginning of text.
RWLocker l(&cache_mutex_);
- SearchParams params(NULL, NULL, &l);
+ SearchParams params(StringPiece(), StringPiece(), &l);
params.anchored = false;
- if (!AnalyzeSearch(&params) || params.start <= SpecialStateMax)
+ if (!AnalyzeSearch(&params) ||
+ params.start == NULL ||
+ params.start == DeadState)
return 0;
// Add start state to work queue.
- StateSet queued;
- vector<State*> q;
- queued.insert(params.start);
+ // Note that any State* that we handle here must point into the cache,
+ // so we can simply depend on pointer-as-a-number hashing and equality.
+ std::unordered_map<State*, int> m;
+ std::deque<State*> q;
+ m.emplace(params.start, static_cast<int>(m.size()));
q.push_back(params.start);
+ // Compute the input bytes needed to cover all of the next pointers.
+ int nnext = prog_->bytemap_range() + 1; // + 1 for kByteEndText slot
+ std::vector<int> input(nnext);
+ for (int c = 0; c < 256; c++) {
+ int b = prog_->bytemap()[c];
+ while (c < 256-1 && prog_->bytemap()[c+1] == b)
+ c++;
+ input[b] = c;
+ }
+ input[prog_->bytemap_range()] = kByteEndText;
+
+ // Scratch space for the output.
+ std::vector<int> output(nnext);
+
// Flood to expand every state.
- for (int i = 0; i < q.size(); i++) {
- State* s = q[i];
- for (int c = 0; c < 257; c++) {
+ bool oom = false;
+ while (!q.empty()) {
+ State* s = q.front();
+ q.pop_front();
+ for (int c : input) {
State* ns = RunStateOnByteUnlocked(s, c);
- if (ns > SpecialStateMax && queued.find(ns) == queued.end()) {
- queued.insert(ns);
+ if (ns == NULL) {
+ oom = true;
+ break;
+ }
+ if (ns == DeadState) {
+ output[ByteMap(c)] = -1;
+ continue;
+ }
+ if (m.find(ns) == m.end()) {
+ m.emplace(ns, static_cast<int>(m.size()));
q.push_back(ns);
}
+ output[ByteMap(c)] = m[ns];
}
+ if (cb)
+ cb(oom ? NULL : output.data(),
+ s == FullMatchState || s->IsMatch());
+ if (oom)
+ break;
}
- return q.size();
+ return static_cast<int>(m.size());
}
// Build out all states in DFA for kind. Returns number of states.
-int Prog::BuildEntireDFA(MatchKind kind) {
- //LOG(ERROR) << "BuildEntireDFA is only for testing.";
- return GetDFA(kind)->BuildAllStates();
+int Prog::BuildEntireDFA(MatchKind kind, const DFAStateCallback& cb) {
+ return GetDFA(kind)->BuildAllStates(cb);
+}
+
+void Prog::TEST_dfa_should_bail_when_slow(bool b) {
+ dfa_should_bail_when_slow = b;
}
// Computes min and max for matching string.
@@ -1989,11 +2012,11 @@ bool DFA::PossibleMatchRange(string* min, string* max, int maxlen) {
// Also note that previously_visited_states[UnseenStatePtr] will, in the STL
// tradition, implicitly insert a '0' value at first use. We take advantage
// of that property below.
- map<State*, int> previously_visited_states;
+ std::unordered_map<State*, int> previously_visited_states;
// Pick out start state for anchored search at beginning of text.
RWLocker l(&cache_mutex_);
- SearchParams params(NULL, NULL, &l);
+ SearchParams params(StringPiece(), StringPiece(), &l);
params.anchored = true;
if (!AnalyzeSearch(&params))
return false;
@@ -2033,16 +2056,14 @@ bool DFA::PossibleMatchRange(string* min, string* max, int maxlen) {
// Build minimum prefix.
State* s = params.start;
min->clear();
+ MutexLock lock(&mutex_);
for (int i = 0; i < maxlen; i++) {
- if (previously_visited_states[s] > kMaxEltRepetitions) {
- VLOG(2) << "Hit kMaxEltRepetitions=" << kMaxEltRepetitions
- << " for state s=" << s << " and min=" << CEscape(*min);
+ if (previously_visited_states[s] > kMaxEltRepetitions)
break;
- }
previously_visited_states[s]++;
// Stop if min is a match.
- State* ns = RunStateOnByteUnlocked(s, kByteEndText);
+ State* ns = RunStateOnByte(s, kByteEndText);
if (ns == NULL) // DFA out of memory
return false;
if (ns != DeadState && (ns == FullMatchState || ns->IsMatch()))
@@ -2051,13 +2072,13 @@ bool DFA::PossibleMatchRange(string* min, string* max, int maxlen) {
// Try to extend the string with low bytes.
bool extended = false;
for (int j = 0; j < 256; j++) {
- ns = RunStateOnByteUnlocked(s, j);
+ ns = RunStateOnByte(s, j);
if (ns == NULL) // DFA out of memory
return false;
if (ns == FullMatchState ||
(ns > SpecialStateMax && ns->ninst_ > 0)) {
extended = true;
- min->append(1, j);
+ min->append(1, static_cast<char>(j));
s = ns;
break;
}
@@ -2071,23 +2092,20 @@ bool DFA::PossibleMatchRange(string* min, string* max, int maxlen) {
s = params.start;
max->clear();
for (int i = 0; i < maxlen; i++) {
- if (previously_visited_states[s] > kMaxEltRepetitions) {
- VLOG(2) << "Hit kMaxEltRepetitions=" << kMaxEltRepetitions
- << " for state s=" << s << " and max=" << CEscape(*max);
+ if (previously_visited_states[s] > kMaxEltRepetitions)
break;
- }
previously_visited_states[s] += 1;
// Try to extend the string with high bytes.
bool extended = false;
for (int j = 255; j >= 0; j--) {
- State* ns = RunStateOnByteUnlocked(s, j);
+ State* ns = RunStateOnByte(s, j);
if (ns == NULL)
return false;
if (ns == FullMatchState ||
(ns > SpecialStateMax && ns->ninst_ > 0)) {
extended = true;
- max->append(1, j);
+ max->append(1, static_cast<char>(j));
s = ns;
break;
}
@@ -2099,7 +2117,7 @@ bool DFA::PossibleMatchRange(string* min, string* max, int maxlen) {
}
// Stopped while still adding to *max - round aaaaaaaaaa... to aaaa...b
- *max = PrefixSuccessor(*max);
+ PrefixSuccessor(max);
// If there are no bytes left, we have no way to say "there is no maximum
// string". We could make the interface more complicated and be able to
@@ -2115,18 +2133,9 @@ bool DFA::PossibleMatchRange(string* min, string* max, int maxlen) {
// PossibleMatchRange for a Prog.
bool Prog::PossibleMatchRange(string* min, string* max, int maxlen) {
- DFA* dfa = NULL;
- {
- MutexLock l(&dfa_mutex_);
- // Have to use dfa_longest_ to get all strings for full matches.
- // For example, (a|aa) never matches aa in first-match mode.
- if (dfa_longest_ == NULL) {
- dfa_longest_ = new DFA(this, Prog::kLongestMatch, dfa_mem_/2);
- delete_dfa_ = DeleteDFA;
- }
- dfa = dfa_longest_;
- }
- return dfa->PossibleMatchRange(min, max, maxlen);
+ // Have to use dfa_longest_ to get all strings for full matches.
+ // For example, (a|aa) never matches aa in first-match mode.
+ return GetDFA(kLongestMatch)->PossibleMatchRange(min, max, maxlen);
}
} // namespace re2
diff --git a/re2/filtered_re2.cc b/re2/filtered_re2.cc
index f576258..12f638a 100644
--- a/re2/filtered_re2.cc
+++ b/re2/filtered_re2.cc
@@ -2,9 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "re2/filtered_re2.h"
+
+#include <stddef.h>
#include <string>
+
#include "util/util.h"
-#include "re2/filtered_re2.h"
+#include "util/logging.h"
#include "re2/prefilter.h"
#include "re2/prefilter_tree.h"
@@ -15,8 +19,13 @@ FilteredRE2::FilteredRE2()
prefilter_tree_(new PrefilterTree()) {
}
+FilteredRE2::FilteredRE2(int min_atom_len)
+ : compiled_(false),
+ prefilter_tree_(new PrefilterTree(min_atom_len)) {
+}
+
FilteredRE2::~FilteredRE2() {
- for (int i = 0; i < re2_vec_.size(); i++)
+ for (size_t i = 0; i < re2_vec_.size(); i++)
delete re2_vec_[i];
delete prefilter_tree_;
}
@@ -33,20 +42,25 @@ RE2::ErrorCode FilteredRE2::Add(const StringPiece& pattern,
}
delete re;
} else {
- *id = re2_vec_.size();
+ *id = static_cast<int>(re2_vec_.size());
re2_vec_.push_back(re);
}
return code;
}
-void FilteredRE2::Compile(vector<string>* atoms) {
- if (compiled_ || re2_vec_.size() == 0) {
- LOG(INFO) << "C: " << compiled_ << " S:" << re2_vec_.size();
+void FilteredRE2::Compile(std::vector<string>* atoms) {
+ if (compiled_) {
+ LOG(ERROR) << "Compile called already.";
return;
}
- for (int i = 0; i < re2_vec_.size(); i++) {
+ if (re2_vec_.empty()) {
+ LOG(ERROR) << "Compile called before Add.";
+ return;
+ }
+
+ for (size_t i = 0; i < re2_vec_.size(); i++) {
Prefilter* prefilter = Prefilter::FromRE2(re2_vec_[i]);
prefilter_tree_->Add(prefilter);
}
@@ -56,21 +70,21 @@ void FilteredRE2::Compile(vector<string>* atoms) {
}
int FilteredRE2::SlowFirstMatch(const StringPiece& text) const {
- for (int i = 0; i < re2_vec_.size(); i++)
+ for (size_t i = 0; i < re2_vec_.size(); i++)
if (RE2::PartialMatch(text, *re2_vec_[i]))
- return i;
+ return static_cast<int>(i);
return -1;
}
int FilteredRE2::FirstMatch(const StringPiece& text,
- const vector<int>& atoms) const {
+ const std::vector<int>& atoms) const {
if (!compiled_) {
- LOG(DFATAL) << "FirstMatch called before Compile";
+ LOG(DFATAL) << "FirstMatch called before Compile.";
return -1;
}
- vector<int> regexps;
+ std::vector<int> regexps;
prefilter_tree_->RegexpsGivenStrings(atoms, &regexps);
- for (int i = 0; i < regexps.size(); i++)
+ for (size_t i = 0; i < regexps.size(); i++)
if (RE2::PartialMatch(text, *re2_vec_[regexps[i]]))
return regexps[i];
return -1;
@@ -78,22 +92,27 @@ int FilteredRE2::FirstMatch(const StringPiece& text,
bool FilteredRE2::AllMatches(
const StringPiece& text,
- const vector<int>& atoms,
- vector<int>* matching_regexps) const {
+ const std::vector<int>& atoms,
+ std::vector<int>* matching_regexps) const {
matching_regexps->clear();
- vector<int> regexps;
+ std::vector<int> regexps;
prefilter_tree_->RegexpsGivenStrings(atoms, &regexps);
- for (int i = 0; i < regexps.size(); i++)
+ for (size_t i = 0; i < regexps.size(); i++)
if (RE2::PartialMatch(text, *re2_vec_[regexps[i]]))
matching_regexps->push_back(regexps[i]);
return !matching_regexps->empty();
}
-void FilteredRE2::RegexpsGivenStrings(const vector<int>& matched_atoms,
- vector<int>* passed_regexps) {
- prefilter_tree_->RegexpsGivenStrings(matched_atoms, passed_regexps);
+void FilteredRE2::AllPotentials(
+ const std::vector<int>& atoms,
+ std::vector<int>* potential_regexps) const {
+ prefilter_tree_->RegexpsGivenStrings(atoms, potential_regexps);
}
+void FilteredRE2::RegexpsGivenStrings(const std::vector<int>& matched_atoms,
+ std::vector<int>* passed_regexps) {
+ prefilter_tree_->RegexpsGivenStrings(matched_atoms, passed_regexps);
+}
void FilteredRE2::PrintPrefilter(int regexpid) {
prefilter_tree_->PrintPrefilter(regexpid);
diff --git a/re2/filtered_re2.h b/re2/filtered_re2.h
index 64b35be..b1317cc 100644
--- a/re2/filtered_re2.h
+++ b/re2/filtered_re2.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_FILTERED_RE2_H_
+#define RE2_FILTERED_RE2_H_
+
// The class FilteredRE2 is used as a wrapper to multiple RE2 regexps.
// It provides a prefilter mechanism that helps in cutting down the
// number of regexps that need to be actually searched.
@@ -18,20 +21,19 @@
// indices of strings that were found in the text to get the actual
// regexp matches.
-#ifndef RE2_FILTERED_RE2_H_
-#define RE2_FILTERED_RE2_H_
-
+#include <string>
#include <vector>
+
#include "re2/re2.h"
namespace re2 {
-using std::vector;
class PrefilterTree;
class FilteredRE2 {
public:
FilteredRE2();
+ explicit FilteredRE2(int min_atom_len);
~FilteredRE2();
// Uses RE2 constructor to create a RE2 object (re). Returns
@@ -47,7 +49,7 @@ class FilteredRE2 {
// the search text should be lowercased first to find matching
// strings from the set of strings returned by Compile. Call after
// all Add calls are done.
- void Compile(vector<string>* strings_to_match);
+ void Compile(std::vector<string>* strings_to_match);
// Returns the index of the first matching regexp.
// Returns -1 on no match. Can be called prior to Compile.
@@ -59,16 +61,24 @@ class FilteredRE2 {
// Returns -1 on no match. Compile has to be called before
// calling this.
int FirstMatch(const StringPiece& text,
- const vector<int>& atoms) const;
+ const std::vector<int>& atoms) const;
// Returns the indices of all matching regexps, after first clearing
// matched_regexps.
bool AllMatches(const StringPiece& text,
- const vector<int>& atoms,
- vector<int>* matching_regexps) const;
+ const std::vector<int>& atoms,
+ std::vector<int>* matching_regexps) const;
+
+ // Returns the indices of all potentially matching regexps after first
+ // clearing potential_regexps.
+ // A regexp is potentially matching if it passes the filter.
+ // If a regexp passes the filter it may still not match.
+ // A regexp that does not pass the filter is guaranteed to not match.
+ void AllPotentials(const std::vector<int>& atoms,
+ std::vector<int>* potential_regexps) const;
// The number of regexps added.
- int NumRegexps() const { return re2_vec_.size(); }
+ int NumRegexps() const { return static_cast<int>(re2_vec_.size()); }
private:
@@ -79,11 +89,11 @@ class FilteredRE2 {
void PrintPrefilter(int regexpid);
// Useful for testing and debugging.
- void RegexpsGivenStrings(const vector<int>& matched_atoms,
- vector<int>* passed_regexps);
+ void RegexpsGivenStrings(const std::vector<int>& matched_atoms,
+ std::vector<int>* passed_regexps);
// All the regexps in the FilteredRE2.
- vector<RE2*> re2_vec_;
+ std::vector<RE2*> re2_vec_;
// Has the FilteredRE2 been compiled using Compile()
bool compiled_;
@@ -91,9 +101,8 @@ class FilteredRE2 {
// An AND-OR tree of string atoms used for filtering regexps.
PrefilterTree* prefilter_tree_;
- //DISALLOW_EVIL_CONSTRUCTORS(FilteredRE2);
- FilteredRE2(const FilteredRE2&);
- void operator=(const FilteredRE2&);
+ FilteredRE2(const FilteredRE2&) = delete;
+ FilteredRE2& operator=(const FilteredRE2&) = delete;
};
} // namespace re2
diff --git a/re2/fuzzing/re2_fuzzer.cc b/re2/fuzzing/re2_fuzzer.cc
new file mode 100644
index 0000000..83971a1
--- /dev/null
+++ b/re2/fuzzing/re2_fuzzer.cc
@@ -0,0 +1,169 @@
+// Copyright 2016 The RE2 Authors. All Rights Reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <map>
+#include <memory>
+#include <queue>
+#include <string>
+
+#include "re2/prefilter.h"
+#include "re2/re2.h"
+
+using re2::StringPiece;
+using std::string;
+
+// NOT static, NOT signed.
+uint8_t dummy = 0;
+
+void Test(StringPiece pattern, const RE2::Options& options, StringPiece text) {
+ RE2 re(pattern, options);
+ if (!re.ok())
+ return;
+
+ // Don't waste time fuzzing high-size programs.
+ // They can cause bug reports due to fuzzer timeouts.
+ int size = re.ProgramSize();
+ if (size > 9999)
+ return;
+ int rsize = re.ReverseProgramSize();
+ if (rsize > 9999)
+ return;
+
+ // Don't waste time fuzzing high-fanout programs.
+ // They can cause bug reports due to fuzzer timeouts.
+ std::map<int, int> histogram;
+ int fanout = re.ProgramFanout(&histogram);
+ if (fanout > 9)
+ return;
+ int rfanout = re.ReverseProgramFanout(&histogram);
+ if (rfanout > 9)
+ return;
+
+ // Don't waste time fuzzing programs with large substrings.
+ // They can cause bug reports due to fuzzer timeouts when they
+ // are repetitions (e.g. hundreds of NUL bytes) and matching is
+ // unanchored. And they aren't interesting for fuzzing purposes.
+ std::unique_ptr<re2::Prefilter> prefilter(re2::Prefilter::FromRE2(&re));
+ if (prefilter == nullptr)
+ return;
+ std::queue<re2::Prefilter*> nodes;
+ nodes.push(prefilter.get());
+ while (!nodes.empty()) {
+ re2::Prefilter* node = nodes.front();
+ nodes.pop();
+ if (node->op() == re2::Prefilter::ATOM) {
+ if (node->atom().size() > 9)
+ return;
+ } else if (node->op() == re2::Prefilter::AND ||
+ node->op() == re2::Prefilter::OR) {
+ for (re2::Prefilter* sub : *node->subs())
+ nodes.push(sub);
+ }
+ }
+
+ if (re.NumberOfCapturingGroups() == 0) {
+ // Avoid early return due to too many arguments.
+ StringPiece sp = text;
+ RE2::FullMatch(sp, re);
+ RE2::PartialMatch(sp, re);
+ RE2::Consume(&sp, re);
+ sp = text; // Reset.
+ RE2::FindAndConsume(&sp, re);
+ } else {
+ // Okay, we have at least one capturing group...
+ // Try conversion for variously typed arguments.
+ StringPiece sp = text;
+ short s;
+ RE2::FullMatch(sp, re, &s);
+ long l;
+ RE2::PartialMatch(sp, re, &l);
+ float f;
+ RE2::Consume(&sp, re, &f);
+ sp = text; // Reset.
+ double d;
+ RE2::FindAndConsume(&sp, re, &d);
+ }
+
+ string s = string(text);
+ RE2::Replace(&s, re, "");
+ s = string(text); // Reset.
+ RE2::GlobalReplace(&s, re, "");
+
+ string min, max;
+ re.PossibleMatchRange(&min, &max, /*maxlen=*/9);
+
+ // Exercise some other API functionality.
+ dummy += re.NamedCapturingGroups().size();
+ dummy += re.CapturingGroupNames().size();
+ dummy += RE2::QuoteMeta(pattern).size();
+}
+
+// Entry point for libFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size == 0 || size > 999)
+ return 0;
+
+ // Crudely limit the use of ., \p, \P, \d, \D, \s, \S, \w and \W.
+ // Otherwise, we will waste time on inputs that have long runs of various
+ // character classes. The fuzzer has shown itself to be easily capable of
+ // generating such patterns that fall within the other limits, but result
+ // in timeouts nonetheless. The marginal cost is high - even more so when
+ // counted repetition is involved - whereas the marginal benefit is zero.
+ // TODO(junyer): Handle [:isalnum:] et al. when they start to cause pain.
+ int cc = 0;
+ for (size_t i = 0; i < size; i++) {
+ if (data[i] == '.')
+ cc++;
+ if (data[i] != '\\')
+ continue;
+ i++;
+ if (i >= size)
+ break;
+ if (data[i] == 'p' || data[i] == 'P' ||
+ data[i] == 'd' || data[i] == 'D' ||
+ data[i] == 's' || data[i] == 'S' ||
+ data[i] == 'w' || data[i] == 'W')
+ cc++;
+ }
+ if (cc > 9)
+ return 0;
+
+ // The one-at-a-time hash by Bob Jenkins.
+ uint32_t hash = 0;
+ for (size_t i = 0; i < size; i++) {
+ hash += data[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+
+ RE2::Options options;
+ options.set_log_errors(false);
+ options.set_max_mem(64 << 20);
+ options.set_encoding(hash & 1 ? RE2::Options::EncodingLatin1
+ : RE2::Options::EncodingUTF8);
+ options.set_posix_syntax(hash & 2);
+ options.set_longest_match(hash & 4);
+ options.set_literal(hash & 8);
+ options.set_never_nl(hash & 16);
+ options.set_dot_nl(hash & 32);
+ options.set_never_capture(hash & 64);
+ options.set_case_sensitive(hash & 128);
+ options.set_perl_classes(hash & 256);
+ options.set_word_boundary(hash & 512);
+ options.set_one_line(hash & 1024);
+
+ const char* ptr = reinterpret_cast<const char*>(data);
+ int len = static_cast<int>(size);
+
+ StringPiece pattern(ptr, len);
+ StringPiece text(ptr, len);
+ Test(pattern, options, text);
+
+ return 0;
+}
diff --git a/re2/make_perl_groups.pl b/re2/make_perl_groups.pl
index d5eaa59..d9fcdaf 100755
--- a/re2/make_perl_groups.pl
+++ b/re2/make_perl_groups.pl
@@ -32,14 +32,20 @@
"\\w",
);
+%overrides = (
+ # Prior to Perl 5.18, \s did not match vertical tab.
+ # RE2 preserves that original behaviour.
+ "\\s:11" => 0,
+);
+
sub ComputeClass($) {
+ my ($cname) = @_;
my @ranges;
- my ($class) = @_;
- my $regexp = "[$class]";
+ my $regexp = qr/[$cname]/;
my $start = -1;
for (my $i=0; $i<=129; $i++) {
if ($i == 129) { $i = 256; }
- if ($i <= 128 && chr($i) =~ $regexp) {
+ if ($i <= 128 && ($overrides{"$cname:$i"} // chr($i) =~ $regexp)) {
if ($start < 0) {
$start = $i;
}
@@ -54,15 +60,15 @@ sub ComputeClass($) {
}
sub PrintClass($$@) {
- my ($cname, $name, @ranges) = @_;
- print "static URange16 code${cname}[] = { /* $name */\n";
+ my ($cnum, $cname, @ranges) = @_;
+ print "static const URange16 code${cnum}[] = { /* $cname */\n";
for (my $i=0; $i<@ranges; $i++) {
my @a = @{$ranges[$i]};
printf "\t{ 0x%x, 0x%x },\n", $a[0], $a[1];
}
print "};\n";
my $n = @ranges;
- my $escname = $name;
+ my $escname = $cname;
$escname =~ s/\\/\\\\/g;
$negname = $escname;
if ($negname =~ /:/) {
@@ -70,25 +76,25 @@ sub PrintClass($$@) {
} else {
$negname =~ y/a-z/A-Z/;
}
- return "{ \"$escname\", +1, code$cname, $n }", "{ \"$negname\", -1, code$cname, $n }";
+ return "{ \"$escname\", +1, code$cnum, $n }", "{ \"$negname\", -1, code$cnum, $n }";
}
-my $gen = 0;
+my $cnum = 0;
sub PrintClasses($@) {
- my ($cname, @classes) = @_;
+ my ($pname, @classes) = @_;
my @entries;
- foreach my $cl (@classes) {
- my @ranges = ComputeClass($cl);
- push @entries, PrintClass(++$gen, $cl, @ranges);
+ foreach my $cname (@classes) {
+ my @ranges = ComputeClass($cname);
+ push @entries, PrintClass(++$cnum, $cname, @ranges);
}
- print "UGroup ${cname}_groups[] = {\n";
+ print "const UGroup ${pname}_groups[] = {\n";
foreach my $e (@entries) {
print "\t$e,\n";
}
print "};\n";
my $count = @entries;
- print "int num_${cname}_groups = $count;\n";
+ print "const int num_${pname}_groups = $count;\n";
}
print <<EOF;
diff --git a/re2/make_unicode_casefold.py b/re2/make_unicode_casefold.py
index 3375d2e..d215eb1 100755
--- a/re2/make_unicode_casefold.py
+++ b/re2/make_unicode_casefold.py
@@ -9,7 +9,8 @@
"""Generate C++ table for Unicode case folding."""
-import unicode, sys
+import sys
+import unicode
_header = """
// GENERATED BY make_unicode_casefold.py; DO NOT EDIT.
@@ -130,11 +131,11 @@ def main():
foldpairs.sort()
foldranges = _MakeRanges(foldpairs)
print "// %d groups, %d pairs, %d ranges" % (len(casegroups), len(foldpairs), len(foldranges))
- print "CaseFold unicode_%s[] = {" % (name,)
+ print "const CaseFold unicode_%s[] = {" % (name,)
for lo, hi, delta in foldranges:
print "\t{ %d, %d, %s }," % (lo, hi, delta)
print "};"
- print "int num_unicode_%s = %d;" % (name, len(foldranges),)
+ print "const int num_unicode_%s = %d;" % (name, len(foldranges),)
print ""
print _header
diff --git a/re2/make_unicode_groups.py b/re2/make_unicode_groups.py
index c2e25c1..e97d47e 100755
--- a/re2/make_unicode_groups.py
+++ b/re2/make_unicode_groups.py
@@ -41,7 +41,7 @@ def MakeRanges(codes):
def PrintRanges(type, name, ranges):
"""Print the ranges as an array of type named name."""
- print "static %s %s[] = {" % (type, name,)
+ print "static const %s %s[] = {" % (type, name,)
for lo, hi in ranges:
print "\t{ %d, %d }," % (lo, hi)
print "};"
@@ -74,7 +74,7 @@ def PrintGroup(name, codes):
ugroup = "{ \"%s\", +1" % (name,)
# if len(code16) > 0:
- # PrintCodes("uint16", name+"_code16", code16)
+ # PrintCodes("uint16_t", name+"_code16", code16)
# ugroup += ", %s_code16, %d" % (name, len(code16))
# else:
# ugroup += ", 0, 0"
@@ -99,12 +99,12 @@ def main():
for name, codes in unicode.Scripts().iteritems():
ugroups.append(PrintGroup(name, codes))
print "// %d 16-bit ranges, %d 32-bit ranges" % (n16, n32)
- print "UGroup unicode_groups[] = {";
+ print "const UGroup unicode_groups[] = {";
ugroups.sort()
for ug in ugroups:
print "\t%s," % (ug,)
print "};"
- print "int num_unicode_groups = %d;" % (len(ugroups),)
+ print "const int num_unicode_groups = %d;" % (len(ugroups),)
print _trailer
if __name__ == '__main__':
diff --git a/re2/mimics_pcre.cc b/re2/mimics_pcre.cc
index fc6dd4a..ad197be 100644
--- a/re2/mimics_pcre.cc
+++ b/re2/mimics_pcre.cc
@@ -23,6 +23,7 @@
// Regexp::MimicsPCRE checks for any of these conditions.
#include "util/util.h"
+#include "util/logging.h"
#include "re2/regexp.h"
#include "re2/walker-inl.h"
@@ -124,7 +125,8 @@ class EmptyStringWalker : public Regexp::Walker<bool> {
}
private:
- DISALLOW_EVIL_CONSTRUCTORS(EmptyStringWalker);
+ EmptyStringWalker(const EmptyStringWalker&) = delete;
+ EmptyStringWalker& operator=(const EmptyStringWalker&) = delete;
};
// Called after visiting re's children. child_args contains the return
diff --git a/re2/nfa.cc b/re2/nfa.cc
index 8c4f761..04d4c6f 100644
--- a/re2/nfa.cc
+++ b/re2/nfa.cc
@@ -24,13 +24,25 @@
// Like Thompson's original machine and like the DFA implementation, this
// implementation notices a match only once it is one byte past it.
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
#include "re2/prog.h"
#include "re2/regexp.h"
+#include "util/logging.h"
+#include "util/pod_array.h"
#include "util/sparse_array.h"
#include "util/sparse_set.h"
+#include "util/strutil.h"
namespace re2 {
+static const bool ExtraDebug = false;
+
class NFA {
public:
NFA(Prog* prog);
@@ -51,12 +63,10 @@ class NFA {
bool anchored, bool longest,
StringPiece* submatch, int nsubmatch);
- static const int Debug = 0;
-
private:
struct Thread {
union {
- int id;
+ int ref;
Thread* next; // when on free list
};
const char** capture;
@@ -64,16 +74,8 @@ class NFA {
// State for explicit stack in AddToThreadq.
struct AddState {
- int id; // Inst to process
- int j;
- const char* cap_j; // if j>=0, set capture[j] = cap_j before processing ip
-
- AddState()
- : id(0), j(-1), cap_j(NULL) {}
- explicit AddState(int id)
- : id(id), j(-1), cap_j(NULL) {}
- AddState(int id, const char* cap_j, int j)
- : id(id), j(j), cap_j(cap_j) {}
+ int id; // Inst to process
+ Thread* t; // if not null, set t0 = t before processing id
};
// Threadq is a list of threads. The list is sorted by the order
@@ -82,52 +84,51 @@ class NFA {
typedef SparseArray<Thread*> Threadq;
inline Thread* AllocThread();
- inline void FreeThread(Thread*);
+ inline Thread* Incref(Thread* t);
+ inline void Decref(Thread* t);
- // Add id (or its children, following unlabeled arrows)
- // to the workqueue q with associated capture info.
- void AddToThreadq(Threadq* q, int id, int flag,
- const char* p, const char** capture);
+ // Follows all empty arrows from id0 and enqueues all the states reached.
+ // Enqueues only the ByteRange instructions that match byte c.
+ // context is used (with p) for evaluating empty-width specials.
+ // p is the current input position, and t0 is the current thread.
+ void AddToThreadq(Threadq* q, int id0, int c, const StringPiece& context,
+ const char* p, Thread* t0);
// Run runq on byte c, appending new states to nextq.
// Updates matched_ and match_ as new, better matches are found.
- // p is position of the next byte (the one after c)
- // in the input string, used when processing capturing parens.
- // flag is the bitwise or of Bol, Eol, etc., specifying whether
- // ^, $ and \b match the current input point (after c).
- inline int Step(Threadq* runq, Threadq* nextq, int c, int flag, const char* p);
+ // context is used (with p) for evaluating empty-width specials.
+ // p is the position of byte c in the input string for AddToThreadq;
+ // p-1 will be used when processing Match instructions.
+ // Frees all the threads on runq.
+ // If there is a shortcut to the end, returns that shortcut.
+ int Step(Threadq* runq, Threadq* nextq, int c, const StringPiece& context,
+ const char* p);
// Returns text version of capture information, for debugging.
string FormatCapture(const char** capture);
inline void CopyCapture(const char** dst, const char** src);
- // Computes whether all matches must begin with the same first
- // byte, and if so, returns that byte. If not, returns -1.
- int ComputeFirstByte();
-
- Prog* prog_; // underlying program
- int start_; // start instruction in program
- int ncapture_; // number of submatches to track
- bool longest_; // whether searching for longest match
- bool endmatch_; // whether match must end at text.end()
- const char* btext_; // beginning of text being matched (for FormatSubmatch)
- const char* etext_; // end of text being matched (for endmatch_)
- Threadq q0_, q1_; // pre-allocated for Search.
- const char** match_; // best match so far
- bool matched_; // any match so far?
- AddState* astack_; // pre-allocated for AddToThreadq
- int nastack_;
- int first_byte_; // required first byte for match, or -1 if none
-
- Thread* free_threads_; // free list
-
- DISALLOW_EVIL_CONSTRUCTORS(NFA);
+ Prog* prog_; // underlying program
+ int start_; // start instruction in program
+ int ncapture_; // number of submatches to track
+ bool longest_; // whether searching for longest match
+ bool endmatch_; // whether match must end at text.end()
+ const char* btext_; // beginning of text being matched (for FormatSubmatch)
+ const char* etext_; // end of text being matched (for endmatch_)
+ Threadq q0_, q1_; // pre-allocated for Search.
+ PODArray<AddState> stack_; // pre-allocated for AddToThreadq
+ Thread* free_threads_; // free list
+ const char** match_; // best match so far
+ bool matched_; // any match so far?
+
+ NFA(const NFA&) = delete;
+ NFA& operator=(const NFA&) = delete;
};
NFA::NFA(Prog* prog) {
prog_ = prog;
- start_ = prog->start();
+ start_ = prog_->start();
ncapture_ = 0;
longest_ = false;
endmatch_ = false;
@@ -135,17 +136,18 @@ NFA::NFA(Prog* prog) {
etext_ = NULL;
q0_.resize(prog_->size());
q1_.resize(prog_->size());
- nastack_ = 2*prog_->size();
- astack_ = new AddState[nastack_];
+ // See NFA::AddToThreadq() for why this is so.
+ int nstack = 2*prog_->inst_count(kInstCapture) +
+ prog_->inst_count(kInstEmptyWidth) +
+ prog_->inst_count(kInstNop) + 1; // + 1 for start inst
+ stack_ = PODArray<AddState>(nstack);
+ free_threads_ = NULL;
match_ = NULL;
matched_ = false;
- free_threads_ = NULL;
- first_byte_ = ComputeFirstByte();
}
NFA::~NFA() {
delete[] match_;
- delete[] astack_;
Thread* next;
for (Thread* t = free_threads_; t; t = next) {
next = t->next;
@@ -154,24 +156,36 @@ NFA::~NFA() {
}
}
-void NFA::FreeThread(Thread *t) {
- if (t == NULL)
- return;
- t->next = free_threads_;
- free_threads_ = t;
-}
-
NFA::Thread* NFA::AllocThread() {
Thread* t = free_threads_;
if (t == NULL) {
t = new Thread;
+ t->ref = 1;
t->capture = new const char*[ncapture_];
return t;
}
free_threads_ = t->next;
+ t->ref = 1;
return t;
}
+NFA::Thread* NFA::Incref(Thread* t) {
+ DCHECK(t != NULL);
+ t->ref++;
+ return t;
+}
+
+void NFA::Decref(Thread* t) {
+ if (t == NULL)
+ return;
+ t->ref--;
+ if (t->ref > 0)
+ return;
+ DCHECK_EQ(t->ref, 0);
+ t->next = free_threads_;
+ free_threads_ = t;
+}
+
void NFA::CopyCapture(const char** dst, const char** src) {
for (int i = 0; i < ncapture_; i+=2) {
dst[i] = src[i];
@@ -180,35 +194,43 @@ void NFA::CopyCapture(const char** dst, const char** src) {
}
// Follows all empty arrows from id0 and enqueues all the states reached.
-// The bits in flag (Bol, Eol, etc.) specify whether ^, $ and \b match.
-// The pointer p is the current input position, and m is the
-// current set of match boundaries.
-void NFA::AddToThreadq(Threadq* q, int id0, int flag,
- const char* p, const char** capture) {
+// Enqueues only the ByteRange instructions that match byte c.
+// context is used (with p) for evaluating empty-width specials.
+// p is the current input position, and t0 is the current thread.
+void NFA::AddToThreadq(Threadq* q, int id0, int c, const StringPiece& context,
+ const char* p, Thread* t0) {
if (id0 == 0)
return;
- // Astack_ is pre-allocated to avoid resize operations.
- // It has room for 2*prog_->size() entries, which is enough:
- // Each inst in prog can be processed at most once,
- // pushing at most two entries on stk.
-
+ // Use stack_ to hold our stack of instructions yet to process.
+ // It was preallocated as follows:
+ // two entries per Capture;
+ // one entry per EmptyWidth; and
+ // one entry per Nop.
+ // This reflects the maximum number of stack pushes that each can
+ // perform. (Each instruction can be processed at most once.)
+ AddState* stk = stack_.data();
int nstk = 0;
- AddState* stk = astack_;
- stk[nstk++] = AddState(id0);
+ stk[nstk++] = {id0, NULL};
while (nstk > 0) {
- DCHECK_LE(nstk, nastack_);
- const AddState& a = stk[--nstk];
- if (a.j >= 0)
- capture[a.j] = a.cap_j;
+ DCHECK_LE(nstk, stack_.size());
+ AddState a = stk[--nstk];
+
+ Loop:
+ if (a.t != NULL) {
+ // t0 was a thread that we allocated and copied in order to
+ // record the capture, so we must now decref it.
+ Decref(t0);
+ t0 = a.t;
+ }
int id = a.id;
if (id == 0)
continue;
if (q->has_index(id)) {
- if (Debug)
- fprintf(stderr, " [%d%s]\n", id, FormatCapture(capture).c_str());
+ if (ExtraDebug)
+ fprintf(stderr, " [%d%s]\n", id, FormatCapture(t0->capture).c_str());
continue;
}
@@ -216,8 +238,7 @@ void NFA::AddToThreadq(Threadq* q, int id0, int flag,
// or we might not. Even if not, it is necessary to have it,
// so that we don't revisit id0 during the recursion.
q->set_new(id, NULL);
-
- Thread** tp = &q->find(id)->second;
+ Thread** tp = &q->get_existing(id);
int j;
Thread* t;
Prog::Inst* ip = prog_->inst(id);
@@ -231,81 +252,95 @@ void NFA::AddToThreadq(Threadq* q, int id0, int flag,
case kInstAltMatch:
// Save state; will pick up at next byte.
- t = AllocThread();
- t->id = id;
- CopyCapture(t->capture, capture);
+ t = Incref(t0);
*tp = t;
- // fall through
- case kInstAlt:
- // Explore alternatives.
- stk[nstk++] = AddState(ip->out1());
- stk[nstk++] = AddState(ip->out());
- break;
+ DCHECK(!ip->last());
+ a = {id+1, NULL};
+ goto Loop;
case kInstNop:
+ if (!ip->last())
+ stk[nstk++] = {id+1, NULL};
+
// Continue on.
- stk[nstk++] = AddState(ip->out());
- break;
+ a = {ip->out(), NULL};
+ goto Loop;
case kInstCapture:
+ if (!ip->last())
+ stk[nstk++] = {id+1, NULL};
+
if ((j=ip->cap()) < ncapture_) {
- // Push a dummy whose only job is to restore capture[j]
+ // Push a dummy whose only job is to restore t0
// once we finish exploring this possibility.
- stk[nstk++] = AddState(0, capture[j], j);
+ stk[nstk++] = {0, t0};
// Record capture.
- capture[j] = p;
+ t = AllocThread();
+ CopyCapture(t->capture, t0->capture);
+ t->capture[j] = p;
+ t0 = t;
}
- stk[nstk++] = AddState(ip->out());
- break;
+ a = {ip->out(), NULL};
+ goto Loop;
- case kInstMatch:
case kInstByteRange:
+ if (!ip->Matches(c))
+ goto Next;
+ FALLTHROUGH_INTENDED;
+
+ case kInstMatch:
// Save state; will pick up at next byte.
- t = AllocThread();
- t->id = id;
- CopyCapture(t->capture, capture);
+ t = Incref(t0);
*tp = t;
- if (Debug)
- fprintf(stderr, " + %d%s [%p]\n", id, FormatCapture(t->capture).c_str(), t);
- break;
+ if (ExtraDebug)
+ fprintf(stderr, " + %d%s\n", id, FormatCapture(t0->capture).c_str());
+
+ Next:
+ if (ip->last())
+ break;
+ a = {id+1, NULL};
+ goto Loop;
case kInstEmptyWidth:
+ if (!ip->last())
+ stk[nstk++] = {id+1, NULL};
+
// Continue on if we have all the right flag bits.
- if (ip->empty() & ~flag)
+ if (ip->empty() & ~Prog::EmptyFlags(context, p))
break;
- stk[nstk++] = AddState(ip->out());
- break;
+ a = {ip->out(), NULL};
+ goto Loop;
}
}
}
// Run runq on byte c, appending new states to nextq.
-// Updates match as new, better matches are found.
-// p is position of the byte c in the input string,
-// used when processing capturing parens.
-// flag is the bitwise or of Bol, Eol, etc., specifying whether
-// ^, $ and \b match the current input point (after c).
+// Updates matched_ and match_ as new, better matches are found.
+// context is used (with p) for evaluating empty-width specials.
+// p is the position of byte c in the input string for AddToThreadq;
+// p-1 will be used when processing Match instructions.
// Frees all the threads on runq.
// If there is a shortcut to the end, returns that shortcut.
-int NFA::Step(Threadq* runq, Threadq* nextq, int c, int flag, const char* p) {
+int NFA::Step(Threadq* runq, Threadq* nextq, int c, const StringPiece& context,
+ const char* p) {
nextq->clear();
for (Threadq::iterator i = runq->begin(); i != runq->end(); ++i) {
- Thread* t = i->second;
+ Thread* t = i->value();
if (t == NULL)
continue;
if (longest_) {
// Can skip any threads started after our current best match.
if (matched_ && match_[0] < t->capture[0]) {
- FreeThread(t);
+ Decref(t);
continue;
}
}
- int id = t->id;
+ int id = i->index();
Prog::Inst* ip = prog_->inst(id);
switch (ip->opcode()) {
@@ -315,8 +350,7 @@ int NFA::Step(Threadq* runq, Threadq* nextq, int c, int flag, const char* p) {
break;
case kInstByteRange:
- if (ip->Matches(c))
- AddToThreadq(nextq, ip->out(), flag, p+1, t->capture);
+ AddToThreadq(nextq, ip->out(), c, context, p, t);
break;
case kInstAltMatch:
@@ -324,52 +358,58 @@ int NFA::Step(Threadq* runq, Threadq* nextq, int c, int flag, const char* p) {
break;
// The match is ours if we want it.
if (ip->greedy(prog_) || longest_) {
- CopyCapture((const char**)match_, t->capture);
- FreeThread(t);
+ CopyCapture(match_, t->capture);
+ matched_ = true;
+
+ Decref(t);
for (++i; i != runq->end(); ++i)
- FreeThread(i->second);
+ Decref(i->value());
runq->clear();
- matched_ = true;
if (ip->greedy(prog_))
return ip->out1();
return ip->out();
}
break;
- case kInstMatch:
- if (endmatch_ && p != etext_)
+ case kInstMatch: {
+ // Avoid invoking undefined behavior when p happens
+ // to be null - and p-1 would be meaningless anyway.
+ if (p == NULL)
+ break;
+
+ if (endmatch_ && p-1 != etext_)
break;
- const char* old = t->capture[1]; // previous end pointer
- t->capture[1] = p;
if (longest_) {
// Leftmost-longest mode: save this match only if
// it is either farther to the left or at the same
// point but longer than an existing match.
if (!matched_ || t->capture[0] < match_[0] ||
- (t->capture[0] == match_[0] && t->capture[1] > match_[1]))
- CopyCapture((const char**)match_, t->capture);
+ (t->capture[0] == match_[0] && p-1 > match_[1])) {
+ CopyCapture(match_, t->capture);
+ match_[1] = p-1;
+ matched_ = true;
+ }
} else {
// Leftmost-biased mode: this match is by definition
// better than what we've already found (see next line).
- CopyCapture((const char**)match_, t->capture);
+ CopyCapture(match_, t->capture);
+ match_[1] = p-1;
+ matched_ = true;
// Cut off the threads that can only find matches
// worse than the one we just found: don't run the
// rest of the current Threadq.
- t->capture[0] = old;
- FreeThread(t);
+ Decref(t);
for (++i; i != runq->end(); ++i)
- FreeThread(i->second);
+ Decref(i->value());
runq->clear();
- matched_ = true;
return 0;
}
- t->capture[0] = old;
- matched_ = true;
break;
+ }
}
- FreeThread(t);
+ Decref(t);
}
runq->clear();
return 0;
@@ -391,12 +431,6 @@ string NFA::FormatCapture(const char** capture) {
return s;
}
-// Returns whether haystack contains needle's memory.
-static bool StringPieceContains(const StringPiece haystack, const StringPiece needle) {
- return haystack.begin() <= needle.begin() &&
- haystack.end() >= needle.end();
-}
-
bool NFA::Search(const StringPiece& text, const StringPiece& const_context,
bool anchored, bool longest,
StringPiece* submatch, int nsubmatch) {
@@ -407,12 +441,9 @@ bool NFA::Search(const StringPiece& text, const StringPiece& const_context,
if (context.begin() == NULL)
context = text;
- if (!StringPieceContains(context, text)) {
- LOG(FATAL) << "Bad args: context does not contain text "
- << reinterpret_cast<const void*>(context.begin())
- << "+" << context.size() << " "
- << reinterpret_cast<const void*>(text.begin())
- << "+" << text.size();
+ // Sanity check: make sure that text lies within context.
+ if (text.begin() < context.begin() || text.end() > context.end()) {
+ LOG(DFATAL) << "context does not contain text";
return false;
}
@@ -445,16 +476,13 @@ bool NFA::Search(const StringPiece& text, const StringPiece& const_context,
match_ = new const char*[ncapture_];
matched_ = false;
- memset(match_, 0, ncapture_*sizeof match_[0]);
// For debugging prints.
btext_ = context.begin();
- if (Debug) {
+ if (ExtraDebug)
fprintf(stderr, "NFA::Search %s (context: %s) anchored=%d longest=%d\n",
- text.as_string().c_str(), context.as_string().c_str(), anchored,
- longest);
- }
+ string(text).c_str(), string(context).c_str(), anchored, longest);
// Set up search.
Threadq* runq = &q0_;
@@ -462,60 +490,32 @@ bool NFA::Search(const StringPiece& text, const StringPiece& const_context,
runq->clear();
nextq->clear();
memset(&match_[0], 0, ncapture_*sizeof match_[0]);
- const char* bp = context.begin();
- int c = -1;
- int wasword = 0;
-
- if (text.begin() > context.begin()) {
- c = text.begin()[-1] & 0xFF;
- wasword = Prog::IsWordChar(c);
- }
// Loop over the text, stepping the machine.
for (const char* p = text.begin();; p++) {
- // Check for empty-width specials.
- int flag = 0;
-
- // ^ and \A
- if (p == context.begin())
- flag |= kEmptyBeginText | kEmptyBeginLine;
- else if (p <= context.end() && p[-1] == '\n')
- flag |= kEmptyBeginLine;
-
- // $ and \z
- if (p == context.end())
- flag |= kEmptyEndText | kEmptyEndLine;
- else if (p < context.end() && p[0] == '\n')
- flag |= kEmptyEndLine;
-
- // \b and \B
- int isword = 0;
- if (p < context.end())
- isword = Prog::IsWordChar(p[0] & 0xFF);
-
- if (isword != wasword)
- flag |= kEmptyWordBoundary;
- else
- flag |= kEmptyNonWordBoundary;
-
- if (Debug) {
- fprintf(stderr, "%c[%#x/%d/%d]:", p > text.end() ? '$' : p == bp ? '^' : c, flag, isword, wasword);
+ if (ExtraDebug) {
+ int c = 0;
+ if (p == context.begin())
+ c = '^';
+ else if (p > text.end())
+ c = '$';
+ else if (p < text.end())
+ c = p[0] & 0xFF;
+
+ fprintf(stderr, "%c:", c);
for (Threadq::iterator i = runq->begin(); i != runq->end(); ++i) {
- Thread* t = i->second;
+ Thread* t = i->value();
if (t == NULL)
continue;
- fprintf(stderr, " %d%s", t->id,
- FormatCapture((const char**)t->capture).c_str());
+ fprintf(stderr, " %d%s", i->index(), FormatCapture(t->capture).c_str());
}
fprintf(stderr, "\n");
}
- // Process previous character (waited until now to avoid
- // repeating the flag computation above).
- // This is a no-op the first time around the loop, because
- // runq is empty.
- int id = Step(runq, nextq, c, flag, p-1);
+ // This is a no-op the first time around the loop because runq is empty.
+ int id = Step(runq, nextq, p < text.end() ? p[0] & 0xFF : -1, context, p);
DCHECK_EQ(runq->size(), 0);
+ using std::swap;
swap(nextq, runq);
nextq->clear();
if (id != 0) {
@@ -529,7 +529,8 @@ bool NFA::Search(const StringPiece& text, const StringPiece& const_context,
break;
case kInstCapture:
- match_[ip->cap()] = p;
+ if (ip->cap() < ncapture_)
+ match_[ip->cap()] = p;
id = ip->out();
continue;
@@ -541,14 +542,6 @@ bool NFA::Search(const StringPiece& text, const StringPiece& const_context,
match_[1] = p;
matched_ = true;
break;
-
- case kInstEmptyWidth:
- if (ip->empty() & ~(kEmptyEndLine|kEmptyEndText)) {
- LOG(DFATAL) << "Unexpected empty-width in short circuit: " << ip->empty();
- break;
- }
- id = ip->out();
- continue;
}
break;
}
@@ -566,72 +559,56 @@ bool NFA::Search(const StringPiece& text, const StringPiece& const_context,
// If there's a required first byte for an unanchored search
// and we're not in the middle of any possible matches,
// use memchr to search for the byte quickly.
- if (!anchored && first_byte_ >= 0 && runq->size() == 0 &&
- p < text.end() && (p[0] & 0xFF) != first_byte_) {
- p = reinterpret_cast<const char*>(memchr(p, first_byte_,
- text.end() - p));
+ int fb = prog_->first_byte();
+ if (!anchored && runq->size() == 0 &&
+ fb >= 0 && p < text.end() && (p[0] & 0xFF) != fb) {
+ p = reinterpret_cast<const char*>(memchr(p, fb, text.end() - p));
if (p == NULL) {
p = text.end();
- isword = 0;
- } else {
- isword = Prog::IsWordChar(p[0] & 0xFF);
}
- flag = Prog::EmptyFlags(context, p);
}
- // Steal match storage (cleared but unused as of yet)
- // temporarily to hold match boundaries for new thread.
- match_[0] = p;
- AddToThreadq(runq, start_, flag, p, match_);
- match_[0] = NULL;
+ Thread* t = AllocThread();
+ CopyCapture(t->capture, match_);
+ t->capture[0] = p;
+ AddToThreadq(runq, start_, p < text.end() ? p[0] & 0xFF : -1, context, p,
+ t);
+ Decref(t);
}
// If all the threads have died, stop early.
if (runq->size() == 0) {
- if (Debug)
+ if (ExtraDebug)
fprintf(stderr, "dead\n");
break;
}
-
- if (p == text.end())
- c = 0;
- else
- c = *p & 0xFF;
- wasword = isword;
-
- // Will run step(runq, nextq, c, ...) on next iteration. See above.
}
for (Threadq::iterator i = runq->begin(); i != runq->end(); ++i)
- FreeThread(i->second);
+ Decref(i->value());
if (matched_) {
for (int i = 0; i < nsubmatch; i++)
- submatch[i].set(match_[2*i], match_[2*i+1] - match_[2*i]);
- if (Debug)
- fprintf(stderr, "match (%d,%d)\n",
- static_cast<int>(match_[0] - btext_),
- static_cast<int>(match_[1] - btext_));
+ submatch[i] =
+ StringPiece(match_[2 * i],
+ static_cast<size_t>(match_[2 * i + 1] - match_[2 * i]));
+ if (ExtraDebug)
+ fprintf(stderr, "match (%td,%td)\n",
+ match_[0] - btext_, match_[1] - btext_);
return true;
}
- VLOG(1) << "No matches found";
return false;
}
// Computes whether all successful matches have a common first byte,
// and if so, returns that byte. If not, returns -1.
-int NFA::ComputeFirstByte() {
- if (start_ == 0)
- return -1;
-
- int b = -1; // first byte, not yet computed
-
- typedef SparseSet Workq;
- Workq q(prog_->size());
- q.insert(start_);
- for (Workq::iterator it = q.begin(); it != q.end(); ++it) {
+int Prog::ComputeFirstByte() {
+ int b = -1;
+ SparseSet q(size());
+ q.insert(start());
+ for (SparseSet::iterator it = q.begin(); it != q.end(); ++it) {
int id = *it;
- Prog::Inst* ip = prog_->inst(id);
+ Prog::Inst* ip = inst(id);
switch (ip->opcode()) {
default:
LOG(DFATAL) << "unhandled " << ip->opcode() << " in ComputeFirstByte";
@@ -642,6 +619,9 @@ int NFA::ComputeFirstByte() {
return -1;
case kInstByteRange:
+ if (!ip->last())
+ q.insert(id+1);
+
// Must match only a single byte
if (ip->lo() != ip->hi())
return -1;
@@ -658,6 +638,9 @@ int NFA::ComputeFirstByte() {
case kInstNop:
case kInstCapture:
case kInstEmptyWidth:
+ if (!ip->last())
+ q.insert(id+1);
+
// Continue on.
// Ignore ip->empty() flags for kInstEmptyWidth
// in order to be as conservative as possible
@@ -666,13 +649,9 @@ int NFA::ComputeFirstByte() {
q.insert(ip->out());
break;
- case kInstAlt:
case kInstAltMatch:
- // Explore alternatives.
- if (ip->out())
- q.insert(ip->out());
- if (ip->out1())
- q.insert(ip->out1());
+ DCHECK(!ip->last());
+ q.insert(id+1);
break;
case kInstFail:
@@ -686,7 +665,7 @@ bool
Prog::SearchNFA(const StringPiece& text, const StringPiece& context,
Anchor anchor, MatchKind kind,
StringPiece* match, int nmatch) {
- if (NFA::Debug)
+ if (ExtraDebug)
Dump();
NFA nfa(this);
@@ -705,5 +684,63 @@ Prog::SearchNFA(const StringPiece& text, const StringPiece& context,
return true;
}
-} // namespace re2
+// For each instruction i in the program reachable from the start, compute the
+// number of instructions reachable from i by following only empty transitions
+// and record that count as fanout[i].
+//
+// fanout holds the results and is also the work queue for the outer iteration.
+// reachable holds the reached nodes for the inner iteration.
+void Prog::Fanout(SparseArray<int>* fanout) {
+ DCHECK_EQ(fanout->max_size(), size());
+ SparseSet reachable(size());
+ fanout->clear();
+ fanout->set_new(start(), 0);
+ for (SparseArray<int>::iterator i = fanout->begin(); i != fanout->end(); ++i) {
+ int* count = &i->value();
+ reachable.clear();
+ reachable.insert(i->index());
+ for (SparseSet::iterator j = reachable.begin(); j != reachable.end(); ++j) {
+ int id = *j;
+ Prog::Inst* ip = inst(id);
+ switch (ip->opcode()) {
+ default:
+ LOG(DFATAL) << "unhandled " << ip->opcode() << " in Prog::Fanout()";
+ break;
+
+ case kInstByteRange:
+ if (!ip->last())
+ reachable.insert(id+1);
+
+ (*count)++;
+ if (!fanout->has_index(ip->out())) {
+ fanout->set_new(ip->out(), 0);
+ }
+ break;
+
+ case kInstAltMatch:
+ DCHECK(!ip->last());
+ reachable.insert(id+1);
+ break;
+
+ case kInstCapture:
+ case kInstEmptyWidth:
+ case kInstNop:
+ if (!ip->last())
+ reachable.insert(id+1);
+ reachable.insert(ip->out());
+ break;
+
+ case kInstMatch:
+ if (!ip->last())
+ reachable.insert(id+1);
+ break;
+
+ case kInstFail:
+ break;
+ }
+ }
+ }
+}
+
+} // namespace re2
diff --git a/re2/onepass.cc b/re2/onepass.cc
index 1c49988..7d39290 100644
--- a/re2/onepass.cc
+++ b/re2/onepass.cc
@@ -50,17 +50,30 @@
// See also Anne Brüggemann-Klein and Derick Wood,
// "One-unambiguous regular languages", Information and Computation 142(2).
+#include <stdint.h>
#include <string.h>
+#include <algorithm>
#include <map>
+#include <string>
+#include <vector>
+
#include "util/util.h"
-#include "util/arena.h"
+#include "util/logging.h"
+#include "util/pod_array.h"
#include "util/sparse_set.h"
+#include "util/strutil.h"
+#include "util/utf.h"
#include "re2/prog.h"
#include "re2/stringpiece.h"
+// Silence "zero-sized array in struct/union" warning for OneState::action.
+#ifdef _MSC_VER
+#pragma warning(disable: 4200)
+#endif
+
namespace re2 {
-static const int Debug = 0;
+static const bool ExtraDebug = false;
// The key insight behind this implementation is that the
// non-determinism in an NFA for a one-pass regular expression
@@ -126,19 +139,16 @@ static const int Debug = 0;
// whether a set of conditions required to finish a match at that
// point in the input rather than process the next byte.
-// A state in the one-pass NFA (aka DFA) - just an array of actions.
-struct OneState;
-
// A state in the one-pass NFA - just an array of actions indexed
// by the bytemap_[] of the next input byte. (The bytemap
// maps next input bytes into equivalence classes, to reduce
// the memory footprint.)
struct OneState {
- uint32 matchcond; // conditions to match right now.
- uint32 action[1];
+ uint32_t matchcond; // conditions to match right now.
+ uint32_t action[];
};
-// The uint32 conditions in the action are a combination of
+// The uint32_t conditions in the action are a combination of
// condition and capture bits and the next state. The bottom 16 bits
// are the condition and capture bits, and the top 16 are the index of
// the next state.
@@ -155,8 +165,8 @@ struct OneState {
// and kEmptyNonWordBoundary, so we can use that as a sentinel
// instead of needing an extra bit.
-static const int kIndexShift = 16; // number of bits below index
-static const int kEmptyShift = 6; // number of empty flags in prog.h
+static const int kIndexShift = 16; // number of bits below index
+static const int kEmptyShift = 6; // number of empty flags in prog.h
static const int kRealCapShift = kEmptyShift + 1;
static const int kRealMaxCap = (kIndexShift - kRealCapShift) / 2 * 2;
@@ -164,23 +174,23 @@ static const int kRealMaxCap = (kIndexShift - kRealCapShift) / 2 * 2;
static const int kCapShift = kRealCapShift - 2;
static const int kMaxCap = kRealMaxCap + 2;
-static const uint32 kMatchWins = 1 << kEmptyShift;
-static const uint32 kCapMask = ((1 << kRealMaxCap) - 1) << kRealCapShift;
+static const uint32_t kMatchWins = 1 << kEmptyShift;
+static const uint32_t kCapMask = ((1 << kRealMaxCap) - 1) << kRealCapShift;
-static const uint32 kImpossible = kEmptyWordBoundary | kEmptyNonWordBoundary;
+static const uint32_t kImpossible = kEmptyWordBoundary | kEmptyNonWordBoundary;
// Check, at compile time, that prog.h agrees with math above.
// This function is never called.
void OnePass_Checks() {
- COMPILE_ASSERT((1<<kEmptyShift)-1 == kEmptyAllFlags,
- kEmptyShift_disagrees_with_kEmptyAllFlags);
+ static_assert((1<<kEmptyShift)-1 == kEmptyAllFlags,
+ "kEmptyShift disagrees with kEmptyAllFlags");
// kMaxCap counts pointers, kMaxOnePassCapture counts pairs.
- COMPILE_ASSERT(kMaxCap == Prog::kMaxOnePassCapture*2,
- kMaxCap_disagrees_with_kMaxOnePassCapture);
+ static_assert(kMaxCap == Prog::kMaxOnePassCapture*2,
+ "kMaxCap disagrees with kMaxOnePassCapture");
}
-static bool Satisfy(uint32 cond, const StringPiece& context, const char* p) {
- uint32 satisfied = Prog::EmptyFlags(context, p);
+static bool Satisfy(uint32_t cond, const StringPiece& context, const char* p) {
+ uint32_t satisfied = Prog::EmptyFlags(context, p);
if (cond & kEmptyAllFlags & ~satisfied)
return false;
return true;
@@ -188,20 +198,17 @@ static bool Satisfy(uint32 cond, const StringPiece& context, const char* p) {
// Apply the capture bits in cond, saving p to the appropriate
// locations in cap[].
-static void ApplyCaptures(uint32 cond, const char* p,
+static void ApplyCaptures(uint32_t cond, const char* p,
const char** cap, int ncap) {
for (int i = 2; i < ncap; i++)
if (cond & (1 << kCapShift << i))
cap[i] = p;
}
-// Compute a node pointer.
-// Basically (OneState*)(nodes + statesize*nodeindex)
-// but the version with the C++ casts overflows 80 characters (and is ugly).
-static inline OneState* IndexToNode(volatile uint8* nodes, int statesize,
+// Computes the OneState* for the given nodeindex.
+static inline OneState* IndexToNode(uint8_t* nodes, int statesize,
int nodeindex) {
- return reinterpret_cast<OneState*>(
- const_cast<uint8*>(nodes + statesize*nodeindex));
+ return reinterpret_cast<OneState*>(nodes + statesize*nodeindex);
}
bool Prog::SearchOnePass(const StringPiece& text,
@@ -237,30 +244,27 @@ bool Prog::SearchOnePass(const StringPiece& text,
if (anchor_end())
kind = kFullMatch;
- // State and act are marked volatile to
- // keep the compiler from re-ordering the
- // memory accesses walking over the NFA.
- // This is worth about 5%.
- volatile OneState* state = onepass_start_;
- volatile uint8* nodes = onepass_nodes_;
- volatile uint32 statesize = onepass_statesize_;
- uint8* bytemap = bytemap_;
+ uint8_t* nodes = onepass_nodes_;
+ int statesize = sizeof(OneState) + bytemap_range()*sizeof(uint32_t);
+ // start() is always mapped to the zeroth OneState.
+ OneState* state = IndexToNode(nodes, statesize, 0);
+ uint8_t* bytemap = bytemap_;
const char* bp = text.begin();
const char* ep = text.end();
const char* p;
bool matched = false;
matchcap[0] = bp;
cap[0] = bp;
- uint32 nextmatchcond = state->matchcond;
+ uint32_t nextmatchcond = state->matchcond;
for (p = bp; p < ep; p++) {
int c = bytemap[*p & 0xFF];
- uint32 matchcond = nextmatchcond;
- uint32 cond = state->action[c];
+ uint32_t matchcond = nextmatchcond;
+ uint32_t cond = state->action[c];
// Determine whether we can reach act->next.
// If so, advance state and nextmatchcond.
if ((cond & kEmptyAllFlags) == 0 || Satisfy(cond, context, p)) {
- uint32 nextindex = cond >> kIndexShift;
+ uint32_t nextindex = cond >> kIndexShift;
state = IndexToNode(nodes, statesize, nextindex);
nextmatchcond = state->matchcond;
} else {
@@ -319,7 +323,7 @@ bool Prog::SearchOnePass(const StringPiece& text,
// Look for match at end of input.
{
- uint32 matchcond = state->matchcond;
+ uint32_t matchcond = state->matchcond;
if (matchcond != kImpossible &&
((matchcond & kEmptyAllFlags) == 0 || Satisfy(matchcond, context, p))) {
if (nmatch > 1 && (matchcond & kCapMask))
@@ -335,7 +339,9 @@ done:
if (!matched)
return false;
for (int i = 0; i < nmatch; i++)
- match[i].set(matchcap[2*i], matchcap[2*i+1] - matchcap[2*i]);
+ match[i] =
+ StringPiece(matchcap[2 * i],
+ static_cast<size_t>(matchcap[2 * i + 1] - matchcap[2 * i]));
return true;
}
@@ -357,7 +363,7 @@ static bool AddQ(Instq *q, int id) {
struct InstCond {
int id;
- uint32 cond;
+ uint32_t cond;
};
// Returns whether this is a one-pass program; that is,
@@ -377,7 +383,7 @@ struct InstCond {
// Constructs and saves corresponding one-pass NFA on success.
bool Prog::IsOnePass() {
if (did_onepass_)
- return onepass_start_ != NULL;
+ return onepass_nodes_ != NULL;
did_onepass_ = true;
if (start() == 0) // no match
@@ -387,32 +393,37 @@ bool Prog::IsOnePass() {
// Willing to use at most 1/4 of the DFA budget (heuristic).
// Limit max node count to 65000 as a conservative estimate to
// avoid overflowing 16-bit node index in encoding.
- int maxnodes = 2 + byte_inst_count_;
- int statesize = sizeof(OneState) + (bytemap_range_-1)*sizeof(uint32);
+ int maxnodes = 2 + inst_count(kInstByteRange);
+ int statesize = sizeof(OneState) + bytemap_range()*sizeof(uint32_t);
if (maxnodes >= 65000 || dfa_mem_ / 4 / statesize < maxnodes)
return false;
// Flood the graph starting at the start state, and check
// that in each reachable state, each possible byte leads
// to a unique next state.
- int size = this->size();
- InstCond *stack = new InstCond[size];
+ int stacksize = inst_count(kInstCapture) +
+ inst_count(kInstEmptyWidth) +
+ inst_count(kInstNop) + 1; // + 1 for start inst
+ PODArray<InstCond> stack(stacksize);
- int* nodebyid = new int[size]; // indexed by ip
- memset(nodebyid, 0xFF, size*sizeof nodebyid[0]);
+ int size = this->size();
+ PODArray<int> nodebyid(size); // indexed by ip
+ memset(nodebyid.data(), 0xFF, size*sizeof nodebyid[0]);
- uint8* nodes = new uint8[maxnodes*statesize];
- uint8* nodep = nodes;
+ // Originally, nodes was a uint8_t[maxnodes*statesize], but that was
+ // unnecessarily optimistic: why allocate a large amount of memory
+ // upfront for a large program when it is unlikely to be one-pass?
+ std::vector<uint8_t> nodes;
Instq tovisit(size), workq(size);
AddQ(&tovisit, start());
nodebyid[start()] = 0;
- nodep += statesize;
int nalloc = 1;
+ nodes.insert(nodes.end(), statesize, 0);
for (Instq::iterator it = tovisit.begin(); it != tovisit.end(); ++it) {
int id = *it;
int nodeindex = nodebyid[id];
- OneState* node = IndexToNode(nodes, statesize, nodeindex);
+ OneState* node = IndexToNode(nodes.data(), statesize, nodeindex);
// Flood graph using manual stack, filling in actions as found.
// Default is none.
@@ -427,93 +438,108 @@ bool Prog::IsOnePass() {
stack[nstack++].cond = 0;
while (nstack > 0) {
int id = stack[--nstack].id;
+ uint32_t cond = stack[nstack].cond;
+
+ Loop:
Prog::Inst* ip = inst(id);
- uint32 cond = stack[nstack].cond;
switch (ip->opcode()) {
+ default:
+ LOG(DFATAL) << "unhandled opcode: " << ip->opcode();
+ break;
+
case kInstAltMatch:
// TODO(rsc): Ignoring kInstAltMatch optimization.
// Should implement it in this engine, but it's subtle.
- // Fall through.
- case kInstAlt:
+ DCHECK(!ip->last());
// If already on work queue, (1) is violated: bail out.
- if (!AddQ(&workq, ip->out()) || !AddQ(&workq, ip->out1()))
+ if (!AddQ(&workq, id+1))
goto fail;
- stack[nstack].id = ip->out1();
- stack[nstack++].cond = cond;
- stack[nstack].id = ip->out();
- stack[nstack++].cond = cond;
- break;
+ id = id+1;
+ goto Loop;
case kInstByteRange: {
int nextindex = nodebyid[ip->out()];
if (nextindex == -1) {
if (nalloc >= maxnodes) {
- if (Debug)
- LOG(ERROR)
- << StringPrintf("Not OnePass: hit node limit %d > %d",
- nalloc, maxnodes);
+ if (ExtraDebug)
+ LOG(ERROR) << StringPrintf(
+ "Not OnePass: hit node limit %d >= %d", nalloc, maxnodes);
goto fail;
}
nextindex = nalloc;
- nodep += statesize;
- nodebyid[ip->out()] = nextindex;
- nalloc++;
AddQ(&tovisit, ip->out());
+ nodebyid[ip->out()] = nalloc;
+ nalloc++;
+ nodes.insert(nodes.end(), statesize, 0);
+ // Update node because it might have been invalidated.
+ node = IndexToNode(nodes.data(), statesize, nodeindex);
}
- if (matched)
- cond |= kMatchWins;
for (int c = ip->lo(); c <= ip->hi(); c++) {
int b = bytemap_[c];
- c = unbytemap_[b]; // last c in byte class
- uint32 act = node->action[b];
- uint32 newact = (nextindex << kIndexShift) | cond;
+ // Skip any bytes immediately after c that are also in b.
+ while (c < 256-1 && bytemap_[c+1] == b)
+ c++;
+ uint32_t act = node->action[b];
+ uint32_t newact = (nextindex << kIndexShift) | cond;
+ if (matched)
+ newact |= kMatchWins;
if ((act & kImpossible) == kImpossible) {
node->action[b] = newact;
} else if (act != newact) {
- if (Debug) {
- LOG(ERROR)
- << StringPrintf("Not OnePass: conflict on byte "
- "%#x at state %d",
- c, *it);
- }
+ if (ExtraDebug)
+ LOG(ERROR) << StringPrintf(
+ "Not OnePass: conflict on byte %#x at state %d", c, *it);
goto fail;
}
}
if (ip->foldcase()) {
- Rune lo = max<Rune>(ip->lo(), 'a') + 'A' - 'a';
- Rune hi = min<Rune>(ip->hi(), 'z') + 'A' - 'a';
+ Rune lo = std::max<Rune>(ip->lo(), 'a') + 'A' - 'a';
+ Rune hi = std::min<Rune>(ip->hi(), 'z') + 'A' - 'a';
for (int c = lo; c <= hi; c++) {
int b = bytemap_[c];
- c = unbytemap_[b]; // last c in class
- uint32 act = node->action[b];
- uint32 newact = (nextindex << kIndexShift) | cond;
+ // Skip any bytes immediately after c that are also in b.
+ while (c < 256-1 && bytemap_[c+1] == b)
+ c++;
+ uint32_t act = node->action[b];
+ uint32_t newact = (nextindex << kIndexShift) | cond;
+ if (matched)
+ newact |= kMatchWins;
if ((act & kImpossible) == kImpossible) {
node->action[b] = newact;
} else if (act != newact) {
- if (Debug) {
- LOG(ERROR)
- << StringPrintf("Not OnePass: conflict on byte "
- "%#x at state %d",
- c, *it);
- }
+ if (ExtraDebug)
+ LOG(ERROR) << StringPrintf(
+ "Not OnePass: conflict on byte %#x at state %d", c, *it);
goto fail;
}
}
}
- break;
+
+ if (ip->last())
+ break;
+ // If already on work queue, (1) is violated: bail out.
+ if (!AddQ(&workq, id+1))
+ goto fail;
+ id = id+1;
+ goto Loop;
}
case kInstCapture:
- if (ip->cap() < kMaxCap)
- cond |= (1 << kCapShift) << ip->cap();
- goto QueueEmpty;
-
case kInstEmptyWidth:
- cond |= ip->empty();
- goto QueueEmpty;
-
case kInstNop:
- QueueEmpty:
+ if (!ip->last()) {
+ // If already on work queue, (1) is violated: bail out.
+ if (!AddQ(&workq, id+1))
+ goto fail;
+ stack[nstack].id = id+1;
+ stack[nstack++].cond = cond;
+ }
+
+ if (ip->opcode() == kInstCapture && ip->cap() < kMaxCap)
+ cond |= (1 << kCapShift) << ip->cap();
+ if (ip->opcode() == kInstEmptyWidth)
+ cond |= ip->empty();
+
// kInstCapture and kInstNop always proceed to ip->out().
// kInstEmptyWidth only sometimes proceeds to ip->out(),
// but as a conservative approximation we assume it always does.
@@ -522,29 +548,32 @@ bool Prog::IsOnePass() {
// If already on work queue, (1) is violated: bail out.
if (!AddQ(&workq, ip->out())) {
- if (Debug) {
- LOG(ERROR) << StringPrintf("Not OnePass: multiple paths"
- " %d -> %d\n",
- *it, ip->out());
- }
+ if (ExtraDebug)
+ LOG(ERROR) << StringPrintf(
+ "Not OnePass: multiple paths %d -> %d\n", *it, ip->out());
goto fail;
}
- stack[nstack].id = ip->out();
- stack[nstack++].cond = cond;
- break;
+ id = ip->out();
+ goto Loop;
case kInstMatch:
if (matched) {
// (3) is violated
- if (Debug) {
- LOG(ERROR) << StringPrintf("Not OnePass: multiple matches"
- " from %d\n", *it);
- }
+ if (ExtraDebug)
+ LOG(ERROR) << StringPrintf(
+ "Not OnePass: multiple matches from %d\n", *it);
goto fail;
}
matched = true;
node->matchcond = cond;
- break;
+
+ if (ip->last())
+ break;
+ // If already on work queue, (1) is violated: bail out.
+ if (!AddQ(&workq, id+1))
+ goto fail;
+ id = id+1;
+ goto Loop;
case kInstFail:
break;
@@ -552,29 +581,22 @@ bool Prog::IsOnePass() {
}
}
- if (Debug) { // For debugging, dump one-pass NFA to LOG(ERROR).
- string dump = "prog dump:\n" + Dump() + "node dump\n";
- map<int, int> idmap;
+ if (ExtraDebug) { // For debugging, dump one-pass NFA to LOG(ERROR).
+ LOG(ERROR) << "bytemap:\n" << DumpByteMap();
+ LOG(ERROR) << "prog:\n" << Dump();
+
+ std::map<int, int> idmap;
for (int i = 0; i < size; i++)
if (nodebyid[i] != -1)
idmap[nodebyid[i]] = i;
- StringAppendF(&dump, "byte ranges:\n");
- int i = 0;
- for (int b = 0; b < bytemap_range_; b++) {
- int lo = i;
- while (bytemap_[i] == b)
- i++;
- StringAppendF(&dump, "\t%d: %#x-%#x\n", b, lo, i - 1);
- }
-
+ string dump;
for (Instq::iterator it = tovisit.begin(); it != tovisit.end(); ++it) {
int id = *it;
int nodeindex = nodebyid[id];
if (nodeindex == -1)
- continue;
- OneState* node = IndexToNode(nodes, statesize, nodeindex);
- string s;
+ continue;
+ OneState* node = IndexToNode(nodes.data(), statesize, nodeindex);
StringAppendF(&dump, "node %d id=%d: matchcond=%#x\n",
nodeindex, id, node->matchcond);
for (int i = 0; i < bytemap_range_; i++) {
@@ -586,28 +608,15 @@ bool Prog::IsOnePass() {
idmap[node->action[i] >> kIndexShift]);
}
}
- LOG(ERROR) << dump;
+ LOG(ERROR) << "nodes:\n" << dump;
}
- // Overallocated earlier; cut down to actual size.
- nodep = new uint8[nalloc*statesize];
- memmove(nodep, nodes, nalloc*statesize);
- delete[] nodes;
- nodes = nodep;
-
- onepass_start_ = IndexToNode(nodes, statesize, nodebyid[start()]);
- onepass_nodes_ = nodes;
- onepass_statesize_ = statesize;
dfa_mem_ -= nalloc*statesize;
-
- delete[] stack;
- delete[] nodebyid;
+ onepass_nodes_ = new uint8_t[nalloc*statesize];
+ memmove(onepass_nodes_, nodes.data(), nalloc*statesize);
return true;
fail:
- delete[] stack;
- delete[] nodebyid;
- delete[] nodes;
return false;
}
diff --git a/re2/parse.cc b/re2/parse.cc
index 0cf4ab4..c8dea7e 100644
--- a/re2/parse.cc
+++ b/re2/parse.cc
@@ -16,14 +16,41 @@
// and recognizes the Perl escape sequences \d, \s, \w, \D, \S, and \W.
// See regexp.h for rationale.
+#include <ctype.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <algorithm>
+#include <map>
+#include <string>
+#include <vector>
+
#include "util/util.h"
+#include "util/logging.h"
+#include "util/pod_array.h"
+#include "util/strutil.h"
+#include "util/utf.h"
#include "re2/regexp.h"
#include "re2/stringpiece.h"
#include "re2/unicode_casefold.h"
#include "re2/unicode_groups.h"
+#include "re2/walker-inl.h"
+
+#if defined(RE2_USE_ICU)
+#include "unicode/uniset.h"
+#include "unicode/unistr.h"
+#include "unicode/utypes.h"
+#endif
namespace re2 {
+// Reduce the maximum repeat count by an order of magnitude when fuzzing.
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+static const int kMaxRepeat = 100;
+#else
+static const int kMaxRepeat = 1000;
+#endif
+
// Regular expression parse state.
// The list of parsed regexps so far is maintained as a vector of
// Regexp pointers called the stack. Left parenthesis and vertical
@@ -156,7 +183,8 @@ private:
int ncap_; // number of capturing parens seen
int rune_max_; // maximum char value for this encoding
- DISALLOW_EVIL_CONSTRUCTORS(ParseState);
+ ParseState(const ParseState&) = delete;
+ ParseState& operator=(const ParseState&) = delete;
};
// Pseudo-operators - only on parse stack.
@@ -214,7 +242,8 @@ bool Regexp::ParseState::PushRegexp(Regexp* re) {
// single characters (e.g., [.] instead of \.), and some
// analysis does better with fewer character classes.
// Similarly, [Aa] can be rewritten as a literal A with ASCII case folding.
- if (re->op_ == kRegexpCharClass) {
+ if (re->op_ == kRegexpCharClass && re->ccb_ != NULL) {
+ re->ccb_->RemoveAbove(rune_max_);
if (re->ccb_->size() == 1) {
Rune r = re->ccb_->begin()->lo;
re->Decref();
@@ -240,8 +269,8 @@ bool Regexp::ParseState::PushRegexp(Regexp* re) {
// Searches the case folding tables and returns the CaseFold* that contains r.
// If there isn't one, returns the CaseFold* with smallest f->lo bigger than r.
// If there isn't one, returns NULL.
-CaseFold* LookupCaseFold(CaseFold *f, int n, Rune r) {
- CaseFold* ef = f + n;
+const CaseFold* LookupCaseFold(const CaseFold *f, int n, Rune r) {
+ const CaseFold* ef = f + n;
// Binary search for entry containing r.
while (n > 0) {
@@ -268,7 +297,7 @@ CaseFold* LookupCaseFold(CaseFold *f, int n, Rune r) {
}
// Returns the result of applying the fold f to the rune r.
-Rune ApplyFold(CaseFold *f, Rune r) {
+Rune ApplyFold(const CaseFold *f, Rune r) {
switch (f->delta) {
default:
return r + f->delta;
@@ -276,7 +305,7 @@ Rune ApplyFold(CaseFold *f, Rune r) {
case EvenOddSkip: // even <-> odd but only applies to every other
if ((r - f->lo) % 2)
return r;
- // fall through
+ FALLTHROUGH_INTENDED;
case EvenOdd: // even <-> odd
if (r%2 == 0)
return r + 1;
@@ -285,7 +314,7 @@ Rune ApplyFold(CaseFold *f, Rune r) {
case OddEvenSkip: // odd <-> even but only applies to every other
if ((r - f->lo) % 2)
return r;
- // fall through
+ FALLTHROUGH_INTENDED;
case OddEven: // odd <-> even
if (r%2 == 1)
return r + 1;
@@ -304,7 +333,7 @@ Rune ApplyFold(CaseFold *f, Rune r) {
//
// CycleFoldRune('?') = '?'
Rune CycleFoldRune(Rune r) {
- CaseFold* f = LookupCaseFold(unicode_casefold, num_unicode_casefold, r);
+ const CaseFold* f = LookupCaseFold(unicode_casefold, num_unicode_casefold, r);
if (f == NULL || r < f->lo)
return r;
return ApplyFold(f, r);
@@ -327,7 +356,7 @@ static void AddFoldedRange(CharClassBuilder* cc, Rune lo, Rune hi, int depth) {
return;
while (lo <= hi) {
- CaseFold* f = LookupCaseFold(unicode_casefold, num_unicode_casefold, lo);
+ const CaseFold* f = LookupCaseFold(unicode_casefold, num_unicode_casefold, lo);
if (f == NULL) // lo has no fold, nor does anything above lo
break;
if (lo < f->lo) { // lo has no fold; next rune with a fold is f->lo
@@ -338,7 +367,7 @@ static void AddFoldedRange(CharClassBuilder* cc, Rune lo, Rune hi, int depth) {
// Add in the result of folding the range lo - f->hi
// and that range's fold, recursively.
Rune lo1 = lo;
- Rune hi1 = min<Rune>(hi, f->hi);
+ Rune hi1 = std::min<Rune>(hi, f->hi);
switch (f->delta) {
default:
lo1 += f->delta;
@@ -377,7 +406,6 @@ bool Regexp::ParseState::PushLiteral(Rune r) {
}
r = CycleFoldRune(r);
} while (r != r1);
- re->ccb_->RemoveAbove(rune_max_);
return PushRegexp(re);
}
@@ -454,6 +482,23 @@ bool Regexp::ParseState::PushRepeatOp(RegexpOp op, const StringPiece& s,
Regexp::ParseFlags fl = flags_;
if (nongreedy)
fl = fl ^ NonGreedy;
+
+ // Squash **, ++ and ??. Regexp::Star() et al. handle this too, but
+ // they're mostly for use during simplification, not during parsing.
+ if (op == stacktop_->op() && fl == stacktop_->parse_flags())
+ return true;
+
+ // Squash *+, *?, +*, +?, ?* and ?+. They all squash to *, so because
+ // op is a repeat, we just have to check that stacktop_->op() is too,
+ // then adjust stacktop_.
+ if ((stacktop_->op() == kRegexpStar ||
+ stacktop_->op() == kRegexpPlus ||
+ stacktop_->op() == kRegexpQuest) &&
+ fl == stacktop_->parse_flags()) {
+ stacktop_->op_ = kRegexpStar;
+ return true;
+ }
+
Regexp* re = new Regexp(op, fl);
re->AllocSub(1);
re->down_ = stacktop_->down_;
@@ -463,12 +508,66 @@ bool Regexp::ParseState::PushRepeatOp(RegexpOp op, const StringPiece& s,
return true;
}
+// RepetitionWalker reports whether the repetition regexp is valid.
+// Valid means that the combination of the top-level repetition
+// and any inner repetitions does not exceed n copies of the
+// innermost thing.
+// This rewalks the regexp tree and is called for every repetition,
+// so we have to worry about inducing quadratic behavior in the parser.
+// We avoid this by only using RepetitionWalker when min or max >= 2.
+// In that case the depth of any >= 2 nesting can only get to 9 without
+// triggering a parse error, so each subtree can only be rewalked 9 times.
+class RepetitionWalker : public Regexp::Walker<int> {
+ public:
+ RepetitionWalker() {}
+ virtual int PreVisit(Regexp* re, int parent_arg, bool* stop);
+ virtual int PostVisit(Regexp* re, int parent_arg, int pre_arg,
+ int* child_args, int nchild_args);
+ virtual int ShortVisit(Regexp* re, int parent_arg);
+
+ private:
+ RepetitionWalker(const RepetitionWalker&) = delete;
+ RepetitionWalker& operator=(const RepetitionWalker&) = delete;
+};
+
+int RepetitionWalker::PreVisit(Regexp* re, int parent_arg, bool* stop) {
+ int arg = parent_arg;
+ if (re->op() == kRegexpRepeat) {
+ int m = re->max();
+ if (m < 0) {
+ m = re->min();
+ }
+ if (m > 0) {
+ arg /= m;
+ }
+ }
+ return arg;
+}
+
+int RepetitionWalker::PostVisit(Regexp* re, int parent_arg, int pre_arg,
+ int* child_args, int nchild_args) {
+ int arg = pre_arg;
+ for (int i = 0; i < nchild_args; i++) {
+ if (child_args[i] < arg) {
+ arg = child_args[i];
+ }
+ }
+ return arg;
+}
+
+int RepetitionWalker::ShortVisit(Regexp* re, int parent_arg) {
+ // This should never be called, since we use Walk and not
+ // WalkExponential.
+ LOG(DFATAL) << "RepetitionWalker::ShortVisit called";
+ return 0;
+}
+
// Pushes a repetition regexp onto the stack.
// A valid argument for the operator must already be on the stack.
bool Regexp::ParseState::PushRepetition(int min, int max,
const StringPiece& s,
bool nongreedy) {
- if ((max != -1 && max < min) || min > 1000 || max > 1000) {
+ if ((max != -1 && max < min) || min > kMaxRepeat || max > kMaxRepeat) {
status_->set_code(kRegexpRepeatSize);
status_->set_error_arg(s);
return false;
@@ -488,8 +587,15 @@ bool Regexp::ParseState::PushRepetition(int min, int max,
re->down_ = stacktop_->down_;
re->sub()[0] = FinishRegexp(stacktop_);
re->simple_ = re->ComputeSimple();
-
stacktop_ = re;
+ if (min >= 2 || max >= 2) {
+ RepetitionWalker w;
+ if (w.Walk(stacktop_, kMaxRepeat) == 0) {
+ status_->set_code(kRegexpRepeatSize);
+ status_->set_error_arg(s);
+ return false;
+ }
+ }
return true;
}
@@ -504,7 +610,7 @@ bool Regexp::ParseState::DoLeftParen(const StringPiece& name) {
Regexp* re = new Regexp(kLeftParen, flags_);
re->cap_ = ++ncap_;
if (name.data() != NULL)
- re->name_ = new string(name.as_string());
+ re->name_ = new string(name);
return PushRegexp(re);
}
@@ -515,13 +621,6 @@ bool Regexp::ParseState::DoLeftParenNoCapture() {
return PushRegexp(re);
}
-// Adds r to cc, along with r's upper case if foldascii is set.
-static void AddLiteral(CharClassBuilder* cc, Rune r, bool foldascii) {
- cc->AddRange(r, r);
- if (foldascii && 'a' <= r && r <= 'z')
- cc->AddRange(r + 'A' - 'a', r + 'A' - 'a');
-}
-
// Processes a vertical bar in the input.
bool Regexp::ParseState::DoVerticalBar() {
MaybeConcatString(-1, NoParseFlags);
@@ -535,46 +634,34 @@ bool Regexp::ParseState::DoVerticalBar() {
Regexp* r1;
Regexp* r2;
if ((r1 = stacktop_) != NULL &&
- (r2 = stacktop_->down_) != NULL &&
+ (r2 = r1->down_) != NULL &&
r2->op() == kVerticalBar) {
- // If above and below vertical bar are literal or char class,
- // can merge into a single char class.
Regexp* r3;
- if ((r1->op() == kRegexpLiteral ||
- r1->op() == kRegexpCharClass ||
- r1->op() == kRegexpAnyChar) &&
- (r3 = r2->down_) != NULL) {
- Rune rune;
- switch (r3->op()) {
- case kRegexpLiteral: // convert to char class
- rune = r3->rune_;
- r3->op_ = kRegexpCharClass;
- r3->cc_ = NULL;
- r3->ccb_ = new CharClassBuilder;
- AddLiteral(r3->ccb_, rune, r3->parse_flags_ & Regexp::FoldCase);
- // fall through
- case kRegexpCharClass:
- if (r1->op() == kRegexpLiteral)
- AddLiteral(r3->ccb_, r1->rune_,
- r1->parse_flags_ & Regexp::FoldCase);
- else if (r1->op() == kRegexpCharClass)
- r3->ccb_->AddCharClass(r1->ccb_);
- if (r1->op() == kRegexpAnyChar || r3->ccb_->full()) {
- delete r3->ccb_;
- r3->ccb_ = NULL;
- r3->op_ = kRegexpAnyChar;
- }
- // fall through
- case kRegexpAnyChar:
- // pop r1
- stacktop_ = r2;
- r1->Decref();
- return true;
- default:
- break;
+ if ((r3 = r2->down_) != NULL &&
+ (r1->op() == kRegexpAnyChar || r3->op() == kRegexpAnyChar)) {
+ // AnyChar is above or below the vertical bar. Let it subsume
+ // the other when the other is Literal, CharClass or AnyChar.
+ if (r3->op() == kRegexpAnyChar &&
+ (r1->op() == kRegexpLiteral ||
+ r1->op() == kRegexpCharClass ||
+ r1->op() == kRegexpAnyChar)) {
+ // Discard r1.
+ stacktop_ = r2;
+ r1->Decref();
+ return true;
+ }
+ if (r1->op() == kRegexpAnyChar &&
+ (r3->op() == kRegexpLiteral ||
+ r3->op() == kRegexpCharClass ||
+ r3->op() == kRegexpAnyChar)) {
+ // Rearrange the stack and discard r3.
+ r1->down_ = r3->down_;
+ r2->down_ = r1;
+ stacktop_ = r2;
+ r3->Decref();
+ return true;
}
}
-
// Swap r1 below vertical bar (r2).
r1->down_ = r2->down_;
r2->down_ = r1;
@@ -780,59 +867,180 @@ void Regexp::RemoveLeadingString(Regexp* re, int n) {
}
}
+// In the context of factoring alternations, a Splice is: a factored prefix or
+// merged character class computed by one iteration of one round of factoring;
+// the span of subexpressions of the alternation to be "spliced" (i.e. removed
+// and replaced); and, for a factored prefix, the number of suffixes after any
+// factoring that might have subsequently been performed on them. For a merged
+// character class, there are no suffixes, of course, so the field is ignored.
+struct Splice {
+ Splice(Regexp* prefix, Regexp** sub, int nsub)
+ : prefix(prefix),
+ sub(sub),
+ nsub(nsub),
+ nsuffix(-1) {}
+
+ Regexp* prefix;
+ Regexp** sub;
+ int nsub;
+ int nsuffix;
+};
+
+// Named so because it is used to implement an explicit stack, a Frame is: the
+// span of subexpressions of the alternation to be factored; the current round
+// of factoring; any Splices computed; and, for a factored prefix, an iterator
+// to the next Splice to be factored (i.e. in another Frame) because suffixes.
+struct Frame {
+ Frame(Regexp** sub, int nsub)
+ : sub(sub),
+ nsub(nsub),
+ round(0) {}
+
+ Regexp** sub;
+ int nsub;
+ int round;
+ std::vector<Splice> splices;
+ int spliceidx;
+};
+
+// Bundled into a class for friend access to Regexp without needing to declare
+// (or define) Splice in regexp.h.
+class FactorAlternationImpl {
+ public:
+ static void Round1(Regexp** sub, int nsub,
+ Regexp::ParseFlags flags,
+ std::vector<Splice>* splices);
+ static void Round2(Regexp** sub, int nsub,
+ Regexp::ParseFlags flags,
+ std::vector<Splice>* splices);
+ static void Round3(Regexp** sub, int nsub,
+ Regexp::ParseFlags flags,
+ std::vector<Splice>* splices);
+};
+
// Factors common prefixes from alternation.
// For example,
// ABC|ABD|AEF|BCX|BCY
// simplifies to
// A(B(C|D)|EF)|BC(X|Y)
-// which the normal parse state routines will further simplify to
+// and thence to
// A(B[CD]|EF)|BC[XY]
//
// Rewrites sub to contain simplified list to alternate and returns
// the new length of sub. Adjusts reference counts accordingly
// (incoming sub[i] decremented, outgoing sub[i] incremented).
+int Regexp::FactorAlternation(Regexp** sub, int nsub, ParseFlags flags) {
+ std::vector<Frame> stk;
+ stk.emplace_back(sub, nsub);
+
+ for (;;) {
+ auto& sub = stk.back().sub;
+ auto& nsub = stk.back().nsub;
+ auto& round = stk.back().round;
+ auto& splices = stk.back().splices;
+ auto& spliceidx = stk.back().spliceidx;
+
+ if (splices.empty()) {
+ // Advance to the next round of factoring. Note that this covers
+ // the initialised state: when splices is empty and round is 0.
+ round++;
+ } else if (spliceidx < static_cast<int>(splices.size())) {
+ // We have at least one more Splice to factor. Recurse logically.
+ stk.emplace_back(splices[spliceidx].sub, splices[spliceidx].nsub);
+ continue;
+ } else {
+ // We have no more Splices to factor. Apply them.
+ auto iter = splices.begin();
+ int out = 0;
+ for (int i = 0; i < nsub; ) {
+ // Copy until we reach where the next Splice begins.
+ while (sub + i < iter->sub)
+ sub[out++] = sub[i++];
+ switch (round) {
+ case 1:
+ case 2: {
+ // Assemble the Splice prefix and the suffixes.
+ Regexp* re[2];
+ re[0] = iter->prefix;
+ re[1] = Regexp::AlternateNoFactor(iter->sub, iter->nsuffix, flags);
+ sub[out++] = Regexp::Concat(re, 2, flags);
+ i += iter->nsub;
+ break;
+ }
+ case 3:
+ // Just use the Splice prefix.
+ sub[out++] = iter->prefix;
+ i += iter->nsub;
+ break;
+ default:
+ LOG(DFATAL) << "unknown round: " << round;
+ break;
+ }
+ // If we are done, copy until the end of sub.
+ if (++iter == splices.end()) {
+ while (i < nsub)
+ sub[out++] = sub[i++];
+ }
+ }
+ splices.clear();
+ nsub = out;
+ // Advance to the next round of factoring.
+ round++;
+ }
-// It's too much of a pain to write this code with an explicit stack,
-// so instead we let the caller specify a maximum depth and
-// don't simplify beyond that. There are around 15 words of local
-// variables and parameters in the frame, so allowing 8 levels
-// on a 64-bit machine is still less than a kilobyte of stack and
-// probably enough benefit for practical uses.
-const int kFactorAlternationMaxDepth = 8;
-
-int Regexp::FactorAlternation(
- Regexp** sub, int n,
- Regexp::ParseFlags altflags) {
- return FactorAlternationRecursive(sub, n, altflags,
- kFactorAlternationMaxDepth);
-}
-
-int Regexp::FactorAlternationRecursive(
- Regexp** sub, int n,
- Regexp::ParseFlags altflags,
- int maxdepth) {
+ switch (round) {
+ case 1:
+ FactorAlternationImpl::Round1(sub, nsub, flags, &splices);
+ break;
+ case 2:
+ FactorAlternationImpl::Round2(sub, nsub, flags, &splices);
+ break;
+ case 3:
+ FactorAlternationImpl::Round3(sub, nsub, flags, &splices);
+ break;
+ case 4:
+ if (stk.size() == 1) {
+ // We are at the top of the stack. Just return.
+ return nsub;
+ } else {
+ // Pop the stack and set the number of suffixes.
+ // (Note that references will be invalidated!)
+ int nsuffix = nsub;
+ stk.pop_back();
+ stk.back().splices[stk.back().spliceidx].nsuffix = nsuffix;
+ ++stk.back().spliceidx;
+ continue;
+ }
+ default:
+ LOG(DFATAL) << "unknown round: " << round;
+ break;
+ }
- if (maxdepth <= 0)
- return n;
+ // Set spliceidx depending on whether we have Splices to factor.
+ if (splices.empty() || round == 3) {
+ spliceidx = static_cast<int>(splices.size());
+ } else {
+ spliceidx = 0;
+ }
+ }
+}
+void FactorAlternationImpl::Round1(Regexp** sub, int nsub,
+ Regexp::ParseFlags flags,
+ std::vector<Splice>* splices) {
// Round 1: Factor out common literal prefixes.
- Rune *rune = NULL;
+ int start = 0;
+ Rune* rune = NULL;
int nrune = 0;
Regexp::ParseFlags runeflags = Regexp::NoParseFlags;
- int start = 0;
- int out = 0;
- for (int i = 0; i <= n; i++) {
- // Invariant: what was in sub[0:start] has been Decref'ed
- // and that space has been reused for sub[0:out] (out <= start).
- //
- // Invariant: sub[start:i] consists of regexps that all begin
- // with the string rune[0:nrune].
-
+ for (int i = 0; i <= nsub; i++) {
+ // Invariant: sub[start:i] consists of regexps that all
+ // begin with rune[0:nrune].
Rune* rune_i = NULL;
int nrune_i = 0;
Regexp::ParseFlags runeflags_i = Regexp::NoParseFlags;
- if (i < n) {
- rune_i = LeadingString(sub[i], &nrune_i, &runeflags_i);
+ if (i < nsub) {
+ rune_i = Regexp::LeadingString(sub[i], &nrune_i, &runeflags_i);
if (runeflags_i == runeflags) {
int same = 0;
while (same < nrune && same < nrune_i && rune[same] == rune_i[same])
@@ -846,109 +1054,121 @@ int Regexp::FactorAlternationRecursive(
}
// Found end of a run with common leading literal string:
- // sub[start:i] all begin with rune[0:nrune] but sub[i]
- // does not even begin with rune[0].
- //
- // Factor out common string and append factored expression to sub[0:out].
+ // sub[start:i] all begin with rune[0:nrune],
+ // but sub[i] does not even begin with rune[0].
if (i == start) {
// Nothing to do - first iteration.
} else if (i == start+1) {
// Just one: don't bother factoring.
- sub[out++] = sub[start];
} else {
- // Construct factored form: prefix(suffix1|suffix2|...)
- Regexp* x[2]; // x[0] = prefix, x[1] = suffix1|suffix2|...
- x[0] = LiteralString(rune, nrune, runeflags);
+ Regexp* prefix = Regexp::LiteralString(rune, nrune, runeflags);
for (int j = start; j < i; j++)
- RemoveLeadingString(sub[j], nrune);
- int nn = FactorAlternationRecursive(sub + start, i - start, altflags,
- maxdepth - 1);
- x[1] = AlternateNoFactor(sub + start, nn, altflags);
- sub[out++] = Concat(x, 2, altflags);
+ Regexp::RemoveLeadingString(sub[j], nrune);
+ splices->emplace_back(prefix, sub + start, i - start);
}
- // Prepare for next round (if there is one).
- if (i < n) {
+ // Prepare for next iteration (if there is one).
+ if (i < nsub) {
start = i;
rune = rune_i;
nrune = nrune_i;
runeflags = runeflags_i;
}
}
- n = out;
+}
- // Round 2: Factor out common complex prefixes,
- // just the first piece of each concatenation,
- // whatever it is. This is good enough a lot of the time.
- start = 0;
- out = 0;
+void FactorAlternationImpl::Round2(Regexp** sub, int nsub,
+ Regexp::ParseFlags flags,
+ std::vector<Splice>* splices) {
+ // Round 2: Factor out common simple prefixes,
+ // just the first piece of each concatenation.
+ // This will be good enough a lot of the time.
+ //
+ // Complex subexpressions (e.g. involving quantifiers)
+ // are not safe to factor because that collapses their
+ // distinct paths through the automaton, which affects
+ // correctness in some cases.
+ int start = 0;
Regexp* first = NULL;
- for (int i = 0; i <= n; i++) {
- // Invariant: what was in sub[0:start] has been Decref'ed
- // and that space has been reused for sub[0:out] (out <= start).
- //
- // Invariant: sub[start:i] consists of regexps that all begin with first.
-
+ for (int i = 0; i <= nsub; i++) {
+ // Invariant: sub[start:i] consists of regexps that all
+ // begin with first.
Regexp* first_i = NULL;
- if (i < n) {
- first_i = LeadingRegexp(sub[i]);
- if (first != NULL && Regexp::Equal(first, first_i)) {
+ if (i < nsub) {
+ first_i = Regexp::LeadingRegexp(sub[i]);
+ if (first != NULL &&
+ // first must be an empty-width op
+ // OR a char class, any char or any byte
+ // OR a fixed repeat of a literal, char class, any char or any byte.
+ (first->op() == kRegexpBeginLine ||
+ first->op() == kRegexpEndLine ||
+ first->op() == kRegexpWordBoundary ||
+ first->op() == kRegexpNoWordBoundary ||
+ first->op() == kRegexpBeginText ||
+ first->op() == kRegexpEndText ||
+ first->op() == kRegexpCharClass ||
+ first->op() == kRegexpAnyChar ||
+ first->op() == kRegexpAnyByte ||
+ (first->op() == kRegexpRepeat &&
+ first->min() == first->max() &&
+ (first->sub()[0]->op() == kRegexpLiteral ||
+ first->sub()[0]->op() == kRegexpCharClass ||
+ first->sub()[0]->op() == kRegexpAnyChar ||
+ first->sub()[0]->op() == kRegexpAnyByte))) &&
+ Regexp::Equal(first, first_i))
continue;
- }
}
// Found end of a run with common leading regexp:
- // sub[start:i] all begin with first but sub[i] does not.
- //
- // Factor out common regexp and append factored expression to sub[0:out].
+ // sub[start:i] all begin with first,
+ // but sub[i] does not.
if (i == start) {
// Nothing to do - first iteration.
} else if (i == start+1) {
// Just one: don't bother factoring.
- sub[out++] = sub[start];
} else {
- // Construct factored form: prefix(suffix1|suffix2|...)
- Regexp* x[2]; // x[0] = prefix, x[1] = suffix1|suffix2|...
- x[0] = first->Incref();
+ Regexp* prefix = first->Incref();
for (int j = start; j < i; j++)
- sub[j] = RemoveLeadingRegexp(sub[j]);
- int nn = FactorAlternationRecursive(sub + start, i - start, altflags,
- maxdepth - 1);
- x[1] = AlternateNoFactor(sub + start, nn, altflags);
- sub[out++] = Concat(x, 2, altflags);
+ sub[j] = Regexp::RemoveLeadingRegexp(sub[j]);
+ splices->emplace_back(prefix, sub + start, i - start);
}
- // Prepare for next round (if there is one).
- if (i < n) {
+ // Prepare for next iteration (if there is one).
+ if (i < nsub) {
start = i;
first = first_i;
}
}
- n = out;
-
- // Round 3: Collapse runs of single literals into character classes.
- start = 0;
- out = 0;
- for (int i = 0; i <= n; i++) {
- // Invariant: what was in sub[0:start] has been Decref'ed
- // and that space has been reused for sub[0:out] (out <= start).
- //
- // Invariant: sub[start:i] consists of regexps that are either
- // literal runes or character classes.
+}
- if (i < n &&
- (sub[i]->op() == kRegexpLiteral ||
- sub[i]->op() == kRegexpCharClass))
- continue;
+void FactorAlternationImpl::Round3(Regexp** sub, int nsub,
+ Regexp::ParseFlags flags,
+ std::vector<Splice>* splices) {
+ // Round 3: Merge runs of literals and/or character classes.
+ int start = 0;
+ Regexp* first = NULL;
+ for (int i = 0; i <= nsub; i++) {
+ // Invariant: sub[start:i] consists of regexps that all
+ // are either literals (i.e. runes) or character classes.
+ Regexp* first_i = NULL;
+ if (i < nsub) {
+ first_i = sub[i];
+ if (first != NULL &&
+ (first->op() == kRegexpLiteral ||
+ first->op() == kRegexpCharClass) &&
+ (first_i->op() == kRegexpLiteral ||
+ first_i->op() == kRegexpCharClass))
+ continue;
+ }
- // sub[i] is not a char or char class;
- // emit char class for sub[start:i]...
+ // Found end of a run of Literal/CharClass:
+ // sub[start:i] all are either one or the other,
+ // but sub[i] is not.
if (i == start) {
- // Nothing to do.
+ // Nothing to do - first iteration.
} else if (i == start+1) {
- sub[out++] = sub[start];
+ // Just one: don't bother factoring.
} else {
- // Make new char class.
CharClassBuilder ccb;
for (int j = start; j < i; j++) {
Regexp* re = sub[j];
@@ -964,31 +1184,16 @@ int Regexp::FactorAlternationRecursive(
}
re->Decref();
}
- sub[out++] = NewCharClass(ccb.GetCharClass(), altflags);
+ Regexp* re = Regexp::NewCharClass(ccb.GetCharClass(), flags);
+ splices->emplace_back(re, sub + start, i - start);
}
- // ... and then emit sub[i].
- if (i < n)
- sub[out++] = sub[i];
- start = i+1;
- }
- n = out;
-
- // Round 4: Collapse runs of empty matches into single empty match.
- start = 0;
- out = 0;
- for (int i = 0; i < n; i++) {
- if (i + 1 < n &&
- sub[i]->op() == kRegexpEmptyMatch &&
- sub[i+1]->op() == kRegexpEmptyMatch) {
- sub[i]->Decref();
- continue;
+ // Prepare for next iteration (if there is one).
+ if (i < nsub) {
+ start = i;
+ first = first_i;
}
- sub[out++] = sub[i];
}
- n = out;
-
- return n;
}
// Collapse the regexps on top of the stack, down to the
@@ -1013,7 +1218,7 @@ void Regexp::ParseState::DoCollapse(RegexpOp op) {
return;
// Construct op (alternation or concatenation), flattening op of op.
- Regexp** subs = new Regexp*[n];
+ PODArray<Regexp*> subs(n);
next = NULL;
int i = n;
for (sub = stacktop_; sub != NULL && !IsMarker(sub->op()); sub = next) {
@@ -1028,8 +1233,7 @@ void Regexp::ParseState::DoCollapse(RegexpOp op) {
}
}
- Regexp* re = ConcatOrAlternate(op, subs, n, flags_, true);
- delete[] subs;
+ Regexp* re = ConcatOrAlternate(op, subs.data(), n, flags_, true);
re->simple_ = re->ComputeSimple();
re->down_ = next;
stacktop_ = re;
@@ -1105,7 +1309,7 @@ bool Regexp::ParseState::MaybeConcatString(int r, ParseFlags flags) {
if (r >= 0) {
re1->op_ = kRegexpLiteral;
re1->rune_ = r;
- re1->parse_flags_ = flags;
+ re1->parse_flags_ = static_cast<uint16_t>(flags);
return true;
}
@@ -1116,9 +1320,8 @@ bool Regexp::ParseState::MaybeConcatString(int r, ParseFlags flags) {
// Lexing routines.
-// Parses a decimal integer, storing it in *n.
+// Parses a decimal integer, storing it in *np.
// Sets *s to span the remainder of the string.
-// Sets *out_re to the regexp for the class.
static bool ParseInteger(StringPiece* s, int* np) {
if (s->size() == 0 || !isdigit((*s)[0] & 0xFF))
return false;
@@ -1185,9 +1388,18 @@ static bool MaybeParseRepetition(StringPiece* sp, int* lo, int* hi) {
// Argument order is backwards from usual Google style
// but consistent with chartorune.
static int StringPieceToRune(Rune *r, StringPiece *sp, RegexpStatus* status) {
- int n;
- if (fullrune(sp->data(), sp->size())) {
- n = chartorune(r, sp->data());
+ // fullrune() takes int, not size_t. However, it just looks
+ // at the leading byte and treats any length >= 4 the same.
+ if (fullrune(sp->data(), static_cast<int>(std::min(size_t{4}, sp->size())))) {
+ int n = chartorune(r, sp->data());
+ // Some copies of chartorune have a bug that accepts
+ // encodings of values in (10FFFF, 1FFFFF] as valid.
+ // Those values break the character class algorithm,
+ // which assumes Runemax is the largest rune.
+ if (*r > Runemax) {
+ n = 1;
+ *r = Runeerror;
+ }
if (!(n == 1 && *r == Runeerror)) { // no decoding error
sp->remove_prefix(n);
return n;
@@ -1195,7 +1407,7 @@ static int StringPieceToRune(Rune *r, StringPiece *sp, RegexpStatus* status) {
}
status->set_code(kRegexpBadUTF8);
- status->set_error_arg(NULL);
+ status->set_error_arg(StringPiece());
return -1;
}
@@ -1239,12 +1451,12 @@ static bool ParseEscape(StringPiece* s, Rune* rp,
if (s->size() < 1 || (*s)[0] != '\\') {
// Should not happen - caller always checks.
status->set_code(kRegexpInternalError);
- status->set_error_arg(NULL);
+ status->set_error_arg(StringPiece());
return false;
}
if (s->size() < 2) {
status->set_code(kRegexpTrailingBackslash);
- status->set_error_arg(NULL);
+ status->set_error_arg(StringPiece());
return false;
}
Rune c, c1;
@@ -1275,7 +1487,7 @@ static bool ParseEscape(StringPiece* s, Rune* rp,
// Single non-zero octal digit is a backreference; not supported.
if (s->size() == 0 || (*s)[0] < '0' || (*s)[0] > '7')
goto BadEscape;
- // fall through
+ FALLTHROUGH_INTENDED;
case '0':
// consume up to three octal digits; already have one.
code = c - '0';
@@ -1290,6 +1502,8 @@ static bool ParseEscape(StringPiece* s, Rune* rp,
}
}
}
+ if (code > rune_max)
+ goto BadEscape;
*rp = code;
return true;
@@ -1375,7 +1589,8 @@ static bool ParseEscape(StringPiece* s, Rune* rp,
BadEscape:
// Unrecognized escape sequence.
status->set_code(kRegexpBadEscape);
- status->set_error_arg(StringPiece(begin, s->data() - begin));
+ status->set_error_arg(
+ StringPiece(begin, static_cast<size_t>(s->begin() - begin)));
return false;
}
@@ -1403,8 +1618,8 @@ void CharClassBuilder::AddRangeFlags(
}
// Look for a group with the given name.
-static UGroup* LookupGroup(const StringPiece& name,
- UGroup *groups, int ngroups) {
+static const UGroup* LookupGroup(const StringPiece& name,
+ const UGroup *groups, int ngroups) {
// Simple name lookup.
for (int i = 0; i < ngroups; i++)
if (StringPiece(groups[i].name) == name)
@@ -1412,30 +1627,32 @@ static UGroup* LookupGroup(const StringPiece& name,
return NULL;
}
-// Fake UGroup containing all Runes
-static URange16 any16[] = { { 0, 65535 } };
-static URange32 any32[] = { { 65536, Runemax } };
-static UGroup anygroup = { "Any", +1, any16, 1, any32, 1 };
-
// Look for a POSIX group with the given name (e.g., "[:^alpha:]")
-static UGroup* LookupPosixGroup(const StringPiece& name) {
+static const UGroup* LookupPosixGroup(const StringPiece& name) {
return LookupGroup(name, posix_groups, num_posix_groups);
}
-static UGroup* LookupPerlGroup(const StringPiece& name) {
+static const UGroup* LookupPerlGroup(const StringPiece& name) {
return LookupGroup(name, perl_groups, num_perl_groups);
}
+#if !defined(RE2_USE_ICU)
+// Fake UGroup containing all Runes
+static URange16 any16[] = { { 0, 65535 } };
+static URange32 any32[] = { { 65536, Runemax } };
+static UGroup anygroup = { "Any", +1, any16, 1, any32, 1 };
+
// Look for a Unicode group with the given name (e.g., "Han")
-static UGroup* LookupUnicodeGroup(const StringPiece& name) {
+static const UGroup* LookupUnicodeGroup(const StringPiece& name) {
// Special case: "Any" means any.
if (name == StringPiece("Any"))
return &anygroup;
return LookupGroup(name, unicode_groups, num_unicode_groups);
}
+#endif
// Add a UGroup or its negation to the character class.
-static void AddUGroup(CharClassBuilder *cc, UGroup *g, int sign,
+static void AddUGroup(CharClassBuilder *cc, const UGroup *g, int sign,
Regexp::ParseFlags parse_flags) {
if (sign == +1) {
for (int i = 0; i < g->nr16; i++) {
@@ -1486,7 +1703,7 @@ static void AddUGroup(CharClassBuilder *cc, UGroup *g, int sign,
// On success, sets *s to span the remainder of the string
// and returns the corresponding UGroup.
// The StringPiece must *NOT* be edited unless the call succeeds.
-UGroup* MaybeParsePerlCCEscape(StringPiece* s, Regexp::ParseFlags parse_flags) {
+const UGroup* MaybeParsePerlCCEscape(StringPiece* s, Regexp::ParseFlags parse_flags) {
if (!(parse_flags & Regexp::PerlClasses))
return NULL;
if (s->size() < 2 || (*s)[0] != '\\')
@@ -1494,7 +1711,7 @@ UGroup* MaybeParsePerlCCEscape(StringPiece* s, Regexp::ParseFlags parse_flags) {
// Could use StringPieceToRune, but there aren't
// any non-ASCII Perl group names.
StringPiece name(s->begin(), 2);
- UGroup *g = LookupPerlGroup(name);
+ const UGroup *g = LookupPerlGroup(name);
if (g == NULL)
return NULL;
s->remove_prefix(name.size());
@@ -1524,7 +1741,7 @@ ParseStatus ParseUnicodeGroup(StringPiece* s, Regexp::ParseFlags parse_flags,
// Committed to parse. Results:
int sign = +1; // -1 = negated char class
if (c == 'P')
- sign = -1;
+ sign = -sign;
StringPiece seq = *s; // \p{Han} or \pL
StringPiece name; // Han or L
s->remove_prefix(2); // '\\', 'p'
@@ -1534,11 +1751,11 @@ ParseStatus ParseUnicodeGroup(StringPiece* s, Regexp::ParseFlags parse_flags,
if (c != '{') {
// Name is the bit of string we just skipped over for c.
const char* p = seq.begin() + 2;
- name = StringPiece(p, s->begin() - p);
+ name = StringPiece(p, static_cast<size_t>(s->begin() - p));
} else {
// Name is in braces. Look for closing }
- int end = s->find('}', 0);
- if (end == s->npos) {
+ size_t end = s->find('}', 0);
+ if (end == StringPiece::npos) {
if (!IsValidUTF8(seq, status))
return kParseError;
status->set_code(kRegexpBadCharRange);
@@ -1552,14 +1769,16 @@ ParseStatus ParseUnicodeGroup(StringPiece* s, Regexp::ParseFlags parse_flags,
}
// Chop seq where s now begins.
- seq = StringPiece(seq.begin(), s->begin() - seq.begin());
+ seq = StringPiece(seq.begin(), static_cast<size_t>(s->begin() - seq.begin()));
- // Look up group
if (name.size() > 0 && name[0] == '^') {
sign = -sign;
name.remove_prefix(1); // '^'
}
- UGroup *g = LookupUnicodeGroup(name);
+
+#if !defined(RE2_USE_ICU)
+ // Look up the group in the RE2 Unicode data.
+ const UGroup *g = LookupUnicodeGroup(name);
if (g == NULL) {
status->set_code(kRegexpBadCharRange);
status->set_error_arg(seq);
@@ -1567,6 +1786,31 @@ ParseStatus ParseUnicodeGroup(StringPiece* s, Regexp::ParseFlags parse_flags,
}
AddUGroup(cc, g, sign, parse_flags);
+#else
+ // Look up the group in the ICU Unicode data. Because ICU provides full
+ // Unicode properties support, this could be more than a lookup by name.
+ ::icu::UnicodeString ustr = ::icu::UnicodeString::fromUTF8(
+ string("\\p{") + string(name) + string("}"));
+ UErrorCode uerr = U_ZERO_ERROR;
+ ::icu::UnicodeSet uset(ustr, uerr);
+ if (U_FAILURE(uerr)) {
+ status->set_code(kRegexpBadCharRange);
+ status->set_error_arg(seq);
+ return kParseError;
+ }
+
+ // Convert the UnicodeSet to a URange32 and UGroup that we can add.
+ int nr = uset.getRangeCount();
+ URange32* r = new URange32[nr];
+ for (int i = 0; i < nr; i++) {
+ r[i].lo = uset.getRangeStart(i);
+ r[i].hi = uset.getRangeEnd(i);
+ }
+ UGroup g = {"", +1, 0, 0, r, nr};
+ AddUGroup(cc, &g, sign, parse_flags);
+ delete[] r;
+#endif
+
return kParseOk;
}
@@ -1593,9 +1837,9 @@ static ParseStatus ParseCCName(StringPiece* s, Regexp::ParseFlags parse_flags,
// Got it. Check that it's valid.
q += 2;
- StringPiece name(p, q-p);
+ StringPiece name(p, static_cast<size_t>(q - p));
- UGroup *g = LookupPosixGroup(name);
+ const UGroup *g = LookupPosixGroup(name);
if (g == NULL) {
status->set_code(kRegexpBadCharRange);
status->set_error_arg(name);
@@ -1647,7 +1891,8 @@ bool Regexp::ParseState::ParseCCRange(StringPiece* s, RuneRange* rr,
return false;
if (rr->hi < rr->lo) {
status->set_code(kRegexpBadCharRange);
- status->set_error_arg(StringPiece(os.data(), s->data() - os.data()));
+ status->set_error_arg(
+ StringPiece(os.data(), static_cast<size_t>(s->data() - os.data())));
return false;
}
} else {
@@ -1666,7 +1911,7 @@ bool Regexp::ParseState::ParseCharClass(StringPiece* s,
if (s->size() == 0 || (*s)[0] != '[') {
// Caller checked this.
status->set_code(kRegexpInternalError);
- status->set_error_arg(NULL);
+ status->set_error_arg(StringPiece());
return false;
}
bool negated = false;
@@ -1732,7 +1977,7 @@ bool Regexp::ParseState::ParseCharClass(StringPiece* s,
}
// Look for Perl character class symbols (extension).
- UGroup *g = MaybeParsePerlCCEscape(s, flags_);
+ const UGroup *g = MaybeParsePerlCCEscape(s, flags_);
if (g != NULL) {
AddUGroup(re->ccb_, g, g->sign, flags_);
continue;
@@ -1761,7 +2006,6 @@ bool Regexp::ParseState::ParseCharClass(StringPiece* s,
if (negated)
re->ccb_->Negate();
- re->ccb_->RemoveAbove(rune_max_);
*out_re = re;
return true;
@@ -1774,7 +2018,7 @@ bool Regexp::ParseState::ParseCharClass(StringPiece* s,
static bool IsValidCaptureName(const StringPiece& name) {
if (name.size() == 0)
return false;
- for (int i = 0; i < name.size(); i++) {
+ for (size_t i = 0; i < name.size(); i++) {
int c = name[i];
if (('0' <= c && c <= '9') ||
('a' <= c && c <= 'z') ||
@@ -1820,8 +2064,8 @@ bool Regexp::ParseState::ParsePerlFlags(StringPiece* s) {
// so that's the one we implement. One is enough.
if (t.size() > 2 && t[0] == 'P' && t[1] == '<') {
// Pull out name.
- int end = t.find('>', 2);
- if (end == t.npos) {
+ size_t end = t.find('>', 2);
+ if (end == StringPiece::npos) {
if (!IsValidUTF8(*s, status_))
return false;
status_->set_code(kRegexpBadNamedCapture);
@@ -1845,7 +2089,7 @@ bool Regexp::ParseState::ParsePerlFlags(StringPiece* s) {
return false;
}
- s->remove_prefix(capture.end() - s->begin());
+ s->remove_prefix(static_cast<size_t>(capture.end() - s->begin()));
return true;
}
@@ -1928,7 +2172,8 @@ bool Regexp::ParseState::ParsePerlFlags(StringPiece* s) {
BadPerlOp:
status_->set_code(kRegexpBadPerlOp);
- status_->set_error_arg(StringPiece(s->begin(), t.begin() - s->begin()));
+ status_->set_error_arg(
+ StringPiece(s->begin(), static_cast<size_t>(t.begin() - s->begin())));
return false;
}
@@ -1940,7 +2185,7 @@ void ConvertLatin1ToUTF8(const StringPiece& latin1, string* utf) {
char buf[UTFmax];
utf->clear();
- for (int i = 0; i < latin1.size(); i++) {
+ for (size_t i = 0; i < latin1.size(); i++) {
Rune r = latin1[i] & 0xFF;
int n = runetochar(buf, &r);
utf->append(buf, n);
@@ -1981,9 +2226,9 @@ Regexp* Regexp::Parse(const StringPiece& s, ParseFlags global_flags,
return ps.DoFinish();
}
- StringPiece lastunary = NULL;
+ StringPiece lastunary = StringPiece();
while (t.size() > 0) {
- StringPiece isunary = NULL;
+ StringPiece isunary = StringPiece();
switch (t[0]) {
default: {
Rune r;
@@ -2006,7 +2251,7 @@ Regexp* Regexp::Parse(const StringPiece& s, ParseFlags global_flags,
if (!ps.DoLeftParenNoCapture())
return NULL;
} else {
- if (!ps.DoLeftParen(NULL))
+ if (!ps.DoLeftParen(StringPiece()))
return NULL;
}
t.remove_prefix(1); // '('
@@ -2075,12 +2320,14 @@ Regexp* Regexp::Parse(const StringPiece& s, ParseFlags global_flags,
// a** is a syntax error, not a double-star.
// (and a++ means something else entirely, which we don't support!)
status->set_code(kRegexpRepeatOp);
- status->set_error_arg(StringPiece(lastunary.begin(),
- t.begin() - lastunary.begin()));
+ status->set_error_arg(StringPiece(
+ lastunary.begin(),
+ static_cast<size_t>(t.begin() - lastunary.begin())));
return NULL;
}
}
- opstr.set(opstr.data(), t.data() - opstr.data());
+ opstr = StringPiece(opstr.data(),
+ static_cast<size_t>(t.data() - opstr.data()));
if (!ps.PushRepeatOp(op, opstr, nongreedy))
return NULL;
isunary = opstr;
@@ -2106,12 +2353,14 @@ Regexp* Regexp::Parse(const StringPiece& s, ParseFlags global_flags,
if (lastunary.size() > 0) {
// Not allowed to stack repetition operators.
status->set_code(kRegexpRepeatOp);
- status->set_error_arg(StringPiece(lastunary.begin(),
- t.begin() - lastunary.begin()));
+ status->set_error_arg(StringPiece(
+ lastunary.begin(),
+ static_cast<size_t>(t.begin() - lastunary.begin())));
return NULL;
}
}
- opstr.set(opstr.data(), t.data() - opstr.data());
+ opstr = StringPiece(opstr.data(),
+ static_cast<size_t>(t.data() - opstr.data()));
if (!ps.PushRepetition(lo, hi, opstr, nongreedy))
return NULL;
isunary = opstr;
@@ -2187,7 +2436,7 @@ Regexp* Regexp::Parse(const StringPiece& s, ParseFlags global_flags,
}
}
- UGroup *g = MaybeParsePerlCCEscape(&t, ps.flags());
+ const UGroup *g = MaybeParsePerlCCEscape(&t, ps.flags());
if (g != NULL) {
Regexp* re = new Regexp(kRegexpCharClass, ps.flags() & ~FoldCase);
re->ccb_ = new CharClassBuilder;
diff --git a/re2/perl_groups.cc b/re2/perl_groups.cc
index 1af5b43..422b388 100644
--- a/re2/perl_groups.cc
+++ b/re2/perl_groups.cc
@@ -5,21 +5,21 @@
namespace re2 {
-static URange16 code1[] = { /* \d */
+static const URange16 code1[] = { /* \d */
{ 0x30, 0x39 },
};
-static URange16 code2[] = { /* \s */
+static const URange16 code2[] = { /* \s */
{ 0x9, 0xa },
{ 0xc, 0xd },
{ 0x20, 0x20 },
};
-static URange16 code3[] = { /* \w */
+static const URange16 code3[] = { /* \w */
{ 0x30, 0x39 },
{ 0x41, 0x5a },
{ 0x5f, 0x5f },
{ 0x61, 0x7a },
};
-UGroup perl_groups[] = {
+const UGroup perl_groups[] = {
{ "\\d", +1, code1, 1 },
{ "\\D", -1, code1, 1 },
{ "\\s", +1, code2, 3 },
@@ -27,64 +27,64 @@ UGroup perl_groups[] = {
{ "\\w", +1, code3, 4 },
{ "\\W", -1, code3, 4 },
};
-int num_perl_groups = 6;
-static URange16 code4[] = { /* [:alnum:] */
+const int num_perl_groups = 6;
+static const URange16 code4[] = { /* [:alnum:] */
{ 0x30, 0x39 },
{ 0x41, 0x5a },
{ 0x61, 0x7a },
};
-static URange16 code5[] = { /* [:alpha:] */
+static const URange16 code5[] = { /* [:alpha:] */
{ 0x41, 0x5a },
{ 0x61, 0x7a },
};
-static URange16 code6[] = { /* [:ascii:] */
+static const URange16 code6[] = { /* [:ascii:] */
{ 0x0, 0x7f },
};
-static URange16 code7[] = { /* [:blank:] */
+static const URange16 code7[] = { /* [:blank:] */
{ 0x9, 0x9 },
{ 0x20, 0x20 },
};
-static URange16 code8[] = { /* [:cntrl:] */
+static const URange16 code8[] = { /* [:cntrl:] */
{ 0x0, 0x1f },
{ 0x7f, 0x7f },
};
-static URange16 code9[] = { /* [:digit:] */
+static const URange16 code9[] = { /* [:digit:] */
{ 0x30, 0x39 },
};
-static URange16 code10[] = { /* [:graph:] */
+static const URange16 code10[] = { /* [:graph:] */
{ 0x21, 0x7e },
};
-static URange16 code11[] = { /* [:lower:] */
+static const URange16 code11[] = { /* [:lower:] */
{ 0x61, 0x7a },
};
-static URange16 code12[] = { /* [:print:] */
+static const URange16 code12[] = { /* [:print:] */
{ 0x20, 0x7e },
};
-static URange16 code13[] = { /* [:punct:] */
+static const URange16 code13[] = { /* [:punct:] */
{ 0x21, 0x2f },
{ 0x3a, 0x40 },
{ 0x5b, 0x60 },
{ 0x7b, 0x7e },
};
-static URange16 code14[] = { /* [:space:] */
+static const URange16 code14[] = { /* [:space:] */
{ 0x9, 0xd },
{ 0x20, 0x20 },
};
-static URange16 code15[] = { /* [:upper:] */
+static const URange16 code15[] = { /* [:upper:] */
{ 0x41, 0x5a },
};
-static URange16 code16[] = { /* [:word:] */
+static const URange16 code16[] = { /* [:word:] */
{ 0x30, 0x39 },
{ 0x41, 0x5a },
{ 0x5f, 0x5f },
{ 0x61, 0x7a },
};
-static URange16 code17[] = { /* [:xdigit:] */
+static const URange16 code17[] = { /* [:xdigit:] */
{ 0x30, 0x39 },
{ 0x41, 0x46 },
{ 0x61, 0x66 },
};
-UGroup posix_groups[] = {
+const UGroup posix_groups[] = {
{ "[:alnum:]", +1, code4, 3 },
{ "[:^alnum:]", -1, code4, 3 },
{ "[:alpha:]", +1, code5, 2 },
@@ -114,6 +114,6 @@ UGroup posix_groups[] = {
{ "[:xdigit:]", +1, code17, 3 },
{ "[:^xdigit:]", -1, code17, 3 },
};
-int num_posix_groups = 28;
+const int num_posix_groups = 28;
} // namespace re2
diff --git a/re2/prefilter.cc b/re2/prefilter.cc
index 4b9c35d..b657357 100644
--- a/re2/prefilter.cc
+++ b/re2/prefilter.cc
@@ -2,36 +2,40 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "util/util.h"
#include "re2/prefilter.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+#include "util/util.h"
+#include "util/logging.h"
+#include "util/strutil.h"
+#include "util/utf.h"
#include "re2/re2.h"
#include "re2/unicode_casefold.h"
#include "re2/walker-inl.h"
namespace re2 {
-static const int Trace = false;
+static const bool ExtraDebug = false;
-typedef set<string>::iterator SSIter;
-typedef set<string>::const_iterator ConstSSIter;
+typedef std::set<string>::iterator SSIter;
+typedef std::set<string>::const_iterator ConstSSIter;
-static int alloc_id = 100000; // Used for debugging.
// Initializes a Prefilter, allocating subs_ as necessary.
Prefilter::Prefilter(Op op) {
op_ = op;
subs_ = NULL;
if (op_ == AND || op_ == OR)
- subs_ = new vector<Prefilter*>;
-
- alloc_id_ = alloc_id++;
- VLOG(10) << "alloc_id: " << alloc_id_;
+ subs_ = new std::vector<Prefilter*>;
}
// Destroys a Prefilter.
Prefilter::~Prefilter() {
- VLOG(10) << "Deleted: " << alloc_id_;
if (subs_) {
- for (int i = 0; i < subs_->size(); i++)
+ for (size_t i = 0; i < subs_->size(); i++)
delete (*subs_)[i];
delete subs_;
subs_ = NULL;
@@ -45,7 +49,7 @@ Prefilter* Prefilter::Simplify() {
}
// Nothing left in the AND/OR.
- if (subs_->size() == 0) {
+ if (subs_->empty()) {
if (op_ == AND)
op_ = ALL; // AND of nothing is true
else
@@ -100,7 +104,7 @@ Prefilter* Prefilter::AndOr(Op op, Prefilter* a, Prefilter* b) {
// If a and b match op, merge their contents.
if (a->op() == op && b->op() == op) {
- for (int i = 0; i < b->subs()->size(); i++) {
+ for (size_t i = 0; i < b->subs()->size(); i++) {
Prefilter* bb = (*b->subs())[i];
a->subs()->push_back(bb);
}
@@ -136,7 +140,7 @@ Prefilter* Prefilter::Or(Prefilter* a, Prefilter* b) {
return AndOr(OR, a, b);
}
-static void SimplifyStringSet(set<string> *ss) {
+static void SimplifyStringSet(std::set<string> *ss) {
// Now make sure that the strings aren't redundant. For example, if
// we know "ab" is a required string, then it doesn't help at all to
// know that "abc" is also a required string, so delete "abc". This
@@ -157,7 +161,7 @@ static void SimplifyStringSet(set<string> *ss) {
}
}
-Prefilter* Prefilter::OrStrings(set<string>* ss) {
+Prefilter* Prefilter::OrStrings(std::set<string>* ss) {
SimplifyStringSet(ss);
Prefilter* or_prefilter = NULL;
if (!ss->empty()) {
@@ -175,7 +179,7 @@ static Rune ToLowerRune(Rune r) {
return r;
}
- CaseFold *f = LookupCaseFold(unicode_tolower, num_unicode_tolower, r);
+ const CaseFold *f = LookupCaseFold(unicode_tolower, num_unicode_tolower, r);
if (f == NULL || r < f->lo)
return r;
return ApplyFold(f, r);
@@ -210,7 +214,7 @@ class Prefilter::Info {
static Info* Quest(Info* a);
static Info* EmptyString();
static Info* NoMatch();
- static Info* AnyChar();
+ static Info* AnyCharOrAnyByte();
static Info* CClass(CharClass* cc, bool latin1);
static Info* Literal(Rune r);
static Info* LiteralLatin1(Rune r);
@@ -222,14 +226,14 @@ class Prefilter::Info {
// Caller takes ownership of the Prefilter.
Prefilter* TakeMatch();
- set<string>& exact() { return exact_; }
+ std::set<string>& exact() { return exact_; }
bool is_exact() const { return is_exact_; }
class Walker;
private:
- set<string> exact_;
+ std::set<string> exact_;
// When is_exact_ is true, the strings that match
// are placed in exact_. When it is no longer an exact
@@ -265,18 +269,12 @@ Prefilter* Prefilter::Info::TakeMatch() {
// Format a Info in string form.
string Prefilter::Info::ToString() {
- if (this == NULL) {
- // Sometimes when iterating on children of a node,
- // some children might have NULL Info. Adding
- // the check here for NULL to take care of cases where
- // the caller is not checking.
- return "";
- }
-
if (is_exact_) {
int n = 0;
string s;
- for (set<string>::iterator i = exact_.begin(); i != exact_.end(); ++i) {
+ for (std::set<string>::iterator i = exact_.begin();
+ i != exact_.end();
+ ++i) {
if (n++ > 0)
s += ",";
s += *i;
@@ -291,16 +289,17 @@ string Prefilter::Info::ToString() {
}
// Add the strings from src to dst.
-static void CopyIn(const set<string>& src, set<string>* dst) {
+static void CopyIn(const std::set<string>& src,
+ std::set<string>* dst) {
for (ConstSSIter i = src.begin(); i != src.end(); ++i)
dst->insert(*i);
}
// Add the cross-product of a and b to dst.
// (For each string i in a and j in b, add i+j.)
-static void CrossProduct(const set<string>& a,
- const set<string>& b,
- set<string>* dst) {
+static void CrossProduct(const std::set<string>& a,
+ const std::set<string>& b,
+ std::set<string>* dst) {
for (ConstSSIter i = a.begin(); i != a.end(); ++i)
for (ConstSSIter j = b.begin(); j != b.end(); ++j)
dst->insert(*i + *j);
@@ -418,8 +417,8 @@ Prefilter::Info* Prefilter::Info::LiteralLatin1(Rune r) {
return info;
}
-// Constructs Info for dot (any character).
-Prefilter::Info* Prefilter::Info::AnyChar() {
+// Constructs Info for dot (any character) or \C (any byte).
+Prefilter::Info* Prefilter::Info::AnyCharOrAnyByte() {
Prefilter::Info* info = new Prefilter::Info();
info->match_ = new Prefilter(ALL);
return info;
@@ -454,15 +453,15 @@ Prefilter::Info* Prefilter::Info::EmptyString() {
typedef CharClass::iterator CCIter;
Prefilter::Info* Prefilter::Info::CClass(CharClass *cc,
bool latin1) {
- if (Trace) {
- VLOG(0) << "CharClassInfo:";
+ if (ExtraDebug) {
+ LOG(ERROR) << "CharClassInfo:";
for (CCIter i = cc->begin(); i != cc->end(); ++i)
- VLOG(0) << " " << i->lo << "-" << i->hi;
+ LOG(ERROR) << " " << i->lo << "-" << i->hi;
}
// If the class is too large, it's okay to overestimate.
if (cc->size() > 10)
- return AnyChar();
+ return AnyCharOrAnyByte();
Prefilter::Info *a = new Prefilter::Info();
for (CCIter i = cc->begin(); i != cc->end(); ++i)
@@ -477,9 +476,8 @@ Prefilter::Info* Prefilter::Info::CClass(CharClass *cc,
a->is_exact_ = true;
- if (Trace) {
- VLOG(0) << " = " << a->ToString();
- }
+ if (ExtraDebug)
+ LOG(ERROR) << " = " << a->ToString();
return a;
}
@@ -500,15 +498,16 @@ class Prefilter::Info::Walker : public Regexp::Walker<Prefilter::Info*> {
bool latin1() { return latin1_; }
private:
bool latin1_;
- DISALLOW_EVIL_CONSTRUCTORS(Walker);
+
+ Walker(const Walker&) = delete;
+ Walker& operator=(const Walker&) = delete;
};
Prefilter::Info* Prefilter::BuildInfo(Regexp* re) {
- if (Trace) {
- LOG(INFO) << "BuildPrefilter::Info: " << re->ToString();
- }
+ if (ExtraDebug)
+ LOG(ERROR) << "BuildPrefilter::Info: " << re->ToString();
- bool latin1 = re->parse_flags() & Regexp::Latin1;
+ bool latin1 = (re->parse_flags() & Regexp::Latin1) != 0;
Prefilter::Info::Walker w(latin1);
Prefilter::Info* info = w.WalkExponential(re, NULL, 100000);
@@ -608,7 +607,6 @@ Prefilter::Info* Prefilter::Info::Walker::PostVisit(
info = child_args[0];
for (int i = 1; i < nchild_args; i++)
info = Alt(info, child_args[i]);
- VLOG(10) << "Alt: " << info->ToString();
break;
case kRegexpStar:
@@ -624,8 +622,9 @@ Prefilter::Info* Prefilter::Info::Walker::PostVisit(
break;
case kRegexpAnyChar:
+ case kRegexpAnyByte:
// Claim nothing, except that it's not empty.
- info = AnyChar();
+ info = AnyCharOrAnyByte();
break;
case kRegexpCharClass:
@@ -638,10 +637,9 @@ Prefilter::Info* Prefilter::Info::Walker::PostVisit(
break;
}
- if (Trace) {
- VLOG(0) << "BuildInfo " << re->ToString()
- << ": " << info->ToString();
- }
+ if (ExtraDebug)
+ LOG(ERROR) << "BuildInfo " << re->ToString()
+ << ": " << (info ? info->ToString() : "");
return info;
}
@@ -665,9 +663,6 @@ Prefilter* Prefilter::FromRegexp(Regexp* re) {
}
string Prefilter::DebugString() const {
- if (this == NULL)
- return "<nil>";
-
switch (op_) {
default:
LOG(DFATAL) << "Bad op in Prefilter::DebugString: " << op_;
@@ -680,19 +675,21 @@ string Prefilter::DebugString() const {
return "";
case AND: {
string s = "";
- for (int i = 0; i < subs_->size(); i++) {
+ for (size_t i = 0; i < subs_->size(); i++) {
if (i > 0)
s += " ";
- s += (*subs_)[i]->DebugString();
+ Prefilter* sub = (*subs_)[i];
+ s += sub ? sub->DebugString() : "<nil>";
}
return s;
}
case OR: {
string s = "(";
- for (int i = 0; i < subs_->size(); i++) {
+ for (size_t i = 0; i < subs_->size(); i++) {
if (i > 0)
s += "|";
- s += (*subs_)[i]->DebugString();
+ Prefilter* sub = (*subs_)[i];
+ s += sub ? sub->DebugString() : "<nil>";
}
s += ")";
return s;
diff --git a/re2/prefilter.h b/re2/prefilter.h
index c2f9ddd..ead09e1 100644
--- a/re2/prefilter.h
+++ b/re2/prefilter.h
@@ -2,14 +2,19 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_PREFILTER_H_
+#define RE2_PREFILTER_H_
+
// Prefilter is the class used to extract string guards from regexps.
// Rather than using Prefilter class directly, use FilteredRE2.
// See filtered_re2.h
-#ifndef RE2_PREFILTER_H_
-#define RE2_PREFILTER_H_
+#include <set>
+#include <string>
+#include <vector>
#include "util/util.h"
+#include "util/logging.h"
namespace re2 {
@@ -37,14 +42,14 @@ class Prefilter {
int unique_id() const { return unique_id_; }
// The children of the Prefilter node.
- vector<Prefilter*>* subs() {
- CHECK(op_ == AND || op_ == OR);
+ std::vector<Prefilter*>* subs() {
+ DCHECK(op_ == AND || op_ == OR);
return subs_;
}
// Set the children vector. Prefilter takes ownership of subs and
// subs_ will be deleted when Prefilter is deleted.
- void set_subs(vector<Prefilter*>* subs) { subs_ = subs; }
+ void set_subs(std::vector<Prefilter*>* subs) { subs_ = subs; }
// Given a RE2, return a Prefilter. The caller takes ownership of
// the Prefilter and should deallocate it. Returns NULL if Prefilter
@@ -72,7 +77,7 @@ class Prefilter {
static Prefilter* FromString(const string& str);
- static Prefilter* OrStrings(set<string>* ss);
+ static Prefilter* OrStrings(std::set<string>* ss);
static Info* BuildInfo(Regexp* re);
@@ -82,7 +87,7 @@ class Prefilter {
Op op_;
// Sub-matches for AND or OR Prefilter.
- vector<Prefilter*>* subs_;
+ std::vector<Prefilter*>* subs_;
// Actual string to match in leaf node.
string atom_;
@@ -94,10 +99,8 @@ class Prefilter {
// and -1 for duplicate nodes.
int unique_id_;
- // Used for debugging, helps in tracking memory leaks.
- int alloc_id_;
-
- DISALLOW_EVIL_CONSTRUCTORS(Prefilter);
+ Prefilter(const Prefilter&) = delete;
+ Prefilter& operator=(const Prefilter&) = delete;
};
} // namespace re2
diff --git a/re2/prefilter_tree.cc b/re2/prefilter_tree.cc
index d8bc37a..a07de40 100644
--- a/re2/prefilter_tree.cc
+++ b/re2/prefilter_tree.cc
@@ -2,98 +2,74 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "re2/prefilter_tree.h"
+
+#include <stddef.h>
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
#include "util/util.h"
-#include "util/flags.h"
+#include "util/logging.h"
+#include "util/strutil.h"
#include "re2/prefilter.h"
-#include "re2/prefilter_tree.h"
#include "re2/re2.h"
-DEFINE_int32(filtered_re2_min_atom_len,
- 3,
- "Strings less than this length are not stored as atoms");
-
namespace re2 {
+static const bool ExtraDebug = false;
+
PrefilterTree::PrefilterTree()
- : compiled_(false) {
+ : compiled_(false),
+ min_atom_len_(3) {
+}
+
+PrefilterTree::PrefilterTree(int min_atom_len)
+ : compiled_(false),
+ min_atom_len_(min_atom_len) {
}
PrefilterTree::~PrefilterTree() {
- for (int i = 0; i < prefilter_vec_.size(); i++)
+ for (size_t i = 0; i < prefilter_vec_.size(); i++)
delete prefilter_vec_[i];
- for (int i = 0; i < entries_.size(); i++)
+ for (size_t i = 0; i < entries_.size(); i++)
delete entries_[i].parents;
}
-// Functions used for adding and Compiling prefilters to the
-// PrefilterTree.
-static bool KeepPart(Prefilter* prefilter, int level) {
- if (prefilter == NULL)
- return false;
-
- switch (prefilter->op()) {
- default:
- LOG(DFATAL) << "Unexpected op in KeepPart: "
- << prefilter->op();
- return false;
-
- case Prefilter::ALL:
- return false;
-
- case Prefilter::ATOM:
- return prefilter->atom().size() >=
- FLAGS_filtered_re2_min_atom_len;
-
- case Prefilter::AND: {
- int j = 0;
- vector<Prefilter*>* subs = prefilter->subs();
- for (int i = 0; i < subs->size(); i++)
- if (KeepPart((*subs)[i], level + 1))
- (*subs)[j++] = (*subs)[i];
- else
- delete (*subs)[i];
-
- subs->resize(j);
- return j > 0;
- }
-
- case Prefilter::OR:
- for (int i = 0; i < prefilter->subs()->size(); i++)
- if (!KeepPart((*prefilter->subs())[i], level + 1))
- return false;
- return true;
- }
-}
-
-void PrefilterTree::Add(Prefilter *f) {
+void PrefilterTree::Add(Prefilter* prefilter) {
if (compiled_) {
- LOG(DFATAL) << "Add after Compile.";
+ LOG(DFATAL) << "Add called after Compile.";
return;
}
- if (f != NULL && !KeepPart(f, 0)) {
- delete f;
- f = NULL;
+ if (prefilter != NULL && !KeepNode(prefilter)) {
+ delete prefilter;
+ prefilter = NULL;
}
- prefilter_vec_.push_back(f);
+ prefilter_vec_.push_back(prefilter);
}
-void PrefilterTree::Compile(vector<string>* atom_vec) {
+void PrefilterTree::Compile(std::vector<string>* atom_vec) {
if (compiled_) {
- LOG(DFATAL) << "Compile after Compile.";
+ LOG(DFATAL) << "Compile called already.";
return;
}
- // We do this check to support some legacy uses of
- // PrefilterTree that call Compile before adding any regexps,
- // and expect Compile not to have effect.
+ // Some legacy users of PrefilterTree call Compile() before
+ // adding any regexps and expect Compile() to have no effect.
if (prefilter_vec_.empty())
return;
compiled_ = true;
- AssignUniqueIds(atom_vec);
+ // TODO(junyer): Use std::unordered_set<Prefilter*> instead?
+ NodeMap nodes;
+ AssignUniqueIds(&nodes, atom_vec);
// Identify nodes that are too common among prefilters and are
// triggering too many parents. Then get rid of them if possible.
@@ -101,72 +77,107 @@ void PrefilterTree::Compile(vector<string>* atom_vec) {
// no longer necessary for their parent to trigger; that is, we do
// not miss out on any regexps triggering by getting rid of a
// prefilter node.
- for (int i = 0; i < entries_.size(); i++) {
- IntMap* parents = entries_[i].parents;
+ for (size_t i = 0; i < entries_.size(); i++) {
+ StdIntMap* parents = entries_[i].parents;
if (parents->size() > 8) {
// This one triggers too many things. If all the parents are AND
// nodes and have other things guarding them, then get rid of
// this trigger. TODO(vsri): Adjust the threshold appropriately,
// make it a function of total number of nodes?
bool have_other_guard = true;
- for (IntMap::iterator it = parents->begin(); it != parents->end(); ++it)
+ for (StdIntMap::iterator it = parents->begin();
+ it != parents->end(); ++it) {
have_other_guard = have_other_guard &&
- (entries_[it->index()].propagate_up_at_count > 1);
+ (entries_[it->first].propagate_up_at_count > 1);
+ }
if (have_other_guard) {
- for (IntMap::iterator it = parents->begin();
+ for (StdIntMap::iterator it = parents->begin();
it != parents->end(); ++it)
- entries_[it->index()].propagate_up_at_count -= 1;
+ entries_[it->first].propagate_up_at_count -= 1;
parents->clear(); // Forget the parents
}
}
}
- PrintDebugInfo();
+ if (ExtraDebug)
+ PrintDebugInfo(&nodes);
}
-Prefilter* PrefilterTree::CanonicalNode(Prefilter* node) {
+Prefilter* PrefilterTree::CanonicalNode(NodeMap* nodes, Prefilter* node) {
string node_string = NodeString(node);
- map<string, Prefilter*>::iterator iter = node_map_.find(node_string);
- if (iter == node_map_.end())
+ std::map<string, Prefilter*>::iterator iter = nodes->find(node_string);
+ if (iter == nodes->end())
return NULL;
return (*iter).second;
}
-static string Itoa(int n) {
- char buf[100];
- snprintf(buf, sizeof buf, "%d", n);
- return string(buf);
-}
-
string PrefilterTree::NodeString(Prefilter* node) const {
// Adding the operation disambiguates AND/OR/atom nodes.
- string s = Itoa(node->op()) + ":";
+ string s = StringPrintf("%d", node->op()) + ":";
if (node->op() == Prefilter::ATOM) {
s += node->atom();
} else {
- for (int i = 0; i < node->subs()->size() ; i++) {
+ for (size_t i = 0; i < node->subs()->size(); i++) {
if (i > 0)
s += ',';
- s += Itoa((*node->subs())[i]->unique_id());
+ s += StringPrintf("%d", (*node->subs())[i]->unique_id());
}
}
return s;
}
-void PrefilterTree::AssignUniqueIds(vector<string>* atom_vec) {
+bool PrefilterTree::KeepNode(Prefilter* node) const {
+ if (node == NULL)
+ return false;
+
+ switch (node->op()) {
+ default:
+ LOG(DFATAL) << "Unexpected op in KeepNode: " << node->op();
+ return false;
+
+ case Prefilter::ALL:
+ case Prefilter::NONE:
+ return false;
+
+ case Prefilter::ATOM:
+ return node->atom().size() >= static_cast<size_t>(min_atom_len_);
+
+ case Prefilter::AND: {
+ int j = 0;
+ std::vector<Prefilter*>* subs = node->subs();
+ for (size_t i = 0; i < subs->size(); i++)
+ if (KeepNode((*subs)[i]))
+ (*subs)[j++] = (*subs)[i];
+ else
+ delete (*subs)[i];
+
+ subs->resize(j);
+ return j > 0;
+ }
+
+ case Prefilter::OR:
+ for (size_t i = 0; i < node->subs()->size(); i++)
+ if (!KeepNode((*node->subs())[i]))
+ return false;
+ return true;
+ }
+}
+
+void PrefilterTree::AssignUniqueIds(NodeMap* nodes,
+ std::vector<string>* atom_vec) {
atom_vec->clear();
// Build vector of all filter nodes, sorted topologically
// from top to bottom in v.
- vector<Prefilter*> v;
+ std::vector<Prefilter*> v;
// Add the top level nodes of each regexp prefilter.
- for (int i = 0; i < prefilter_vec_.size(); i++) {
+ for (size_t i = 0; i < prefilter_vec_.size(); i++) {
Prefilter* f = prefilter_vec_[i];
if (f == NULL)
- unfiltered_.push_back(i);
+ unfiltered_.push_back(static_cast<int>(i));
// We push NULL also on to v, so that we maintain the
// mapping of index==regexpid for level=0 prefilter nodes.
@@ -174,29 +185,29 @@ void PrefilterTree::AssignUniqueIds(vector<string>* atom_vec) {
}
// Now add all the descendant nodes.
- for (int i = 0; i < v.size(); i++) {
+ for (size_t i = 0; i < v.size(); i++) {
Prefilter* f = v[i];
if (f == NULL)
continue;
if (f->op() == Prefilter::AND || f->op() == Prefilter::OR) {
- const vector<Prefilter*>& subs = *f->subs();
- for (int j = 0; j < subs.size(); j++)
+ const std::vector<Prefilter*>& subs = *f->subs();
+ for (size_t j = 0; j < subs.size(); j++)
v.push_back(subs[j]);
}
}
// Identify unique nodes.
int unique_id = 0;
- for (int i = v.size() - 1; i >= 0; i--) {
+ for (int i = static_cast<int>(v.size()) - 1; i >= 0; i--) {
Prefilter *node = v[i];
if (node == NULL)
continue;
node->set_unique_id(-1);
- Prefilter* canonical = CanonicalNode(node);
+ Prefilter* canonical = CanonicalNode(nodes, node);
if (canonical == NULL) {
// Any further nodes that have the same node string
// will find this node as the canonical node.
- node_map_[NodeString(node)] = node;
+ nodes->emplace(NodeString(node), node);
if (node->op() == Prefilter::ATOM) {
atom_vec->push_back(node->atom());
atom_index_to_id_.push_back(unique_id);
@@ -206,28 +217,28 @@ void PrefilterTree::AssignUniqueIds(vector<string>* atom_vec) {
node->set_unique_id(canonical->unique_id());
}
}
- entries_.resize(node_map_.size());
+ entries_.resize(nodes->size());
- // Create parent IntMap for the entries.
- for (int i = v.size() - 1; i >= 0; i--) {
+ // Create parent StdIntMap for the entries.
+ for (int i = static_cast<int>(v.size()) - 1; i >= 0; i--) {
Prefilter* prefilter = v[i];
if (prefilter == NULL)
continue;
- if (CanonicalNode(prefilter) != prefilter)
+ if (CanonicalNode(nodes, prefilter) != prefilter)
continue;
Entry* entry = &entries_[prefilter->unique_id()];
- entry->parents = new IntMap(node_map_.size());
+ entry->parents = new StdIntMap();
}
// Fill the entries.
- for (int i = v.size() - 1; i >= 0; i--) {
+ for (int i = static_cast<int>(v.size()) - 1; i >= 0; i--) {
Prefilter* prefilter = v[i];
if (prefilter == NULL)
continue;
- if (CanonicalNode(prefilter) != prefilter)
+ if (CanonicalNode(nodes, prefilter) != prefilter)
continue;
Entry* entry = &entries_[prefilter->unique_id()];
@@ -244,24 +255,26 @@ void PrefilterTree::AssignUniqueIds(vector<string>* atom_vec) {
case Prefilter::OR:
case Prefilter::AND: {
- IntMap uniq_child(node_map_.size());
- for (int j = 0; j < prefilter->subs()->size() ; j++) {
+ std::set<int> uniq_child;
+ for (size_t j = 0; j < prefilter->subs()->size(); j++) {
Prefilter* child = (*prefilter->subs())[j];
- Prefilter* canonical = CanonicalNode(child);
+ Prefilter* canonical = CanonicalNode(nodes, child);
if (canonical == NULL) {
LOG(DFATAL) << "Null canonical node";
return;
}
int child_id = canonical->unique_id();
- if (!uniq_child.has_index(child_id))
- uniq_child.set_new(child_id, 1);
+ uniq_child.insert(child_id);
// To the child, we want to add to parent indices.
Entry* child_entry = &entries_[child_id];
- if (!child_entry->parents->has_index(prefilter->unique_id()))
- child_entry->parents->set_new(prefilter->unique_id(), 1);
+ if (child_entry->parents->find(prefilter->unique_id()) ==
+ child_entry->parents->end()) {
+ (*child_entry->parents)[prefilter->unique_id()] = 1;
+ }
}
- entry->propagate_up_at_count =
- prefilter->op() == Prefilter::AND ? uniq_child.size() : 1;
+ entry->propagate_up_at_count = prefilter->op() == Prefilter::AND
+ ? static_cast<int>(uniq_child.size())
+ : 1;
break;
}
@@ -269,67 +282,65 @@ void PrefilterTree::AssignUniqueIds(vector<string>* atom_vec) {
}
// For top level nodes, populate regexp id.
- for (int i = 0; i < prefilter_vec_.size(); i++) {
+ for (size_t i = 0; i < prefilter_vec_.size(); i++) {
if (prefilter_vec_[i] == NULL)
continue;
- int id = CanonicalNode(prefilter_vec_[i])->unique_id();
+ int id = CanonicalNode(nodes, prefilter_vec_[i])->unique_id();
DCHECK_LE(0, id);
Entry* entry = &entries_[id];
- entry->regexps.push_back(i);
+ entry->regexps.push_back(static_cast<int>(i));
}
}
// Functions for triggering during search.
void PrefilterTree::RegexpsGivenStrings(
- const vector<int>& matched_atoms,
- vector<int>* regexps) const {
+ const std::vector<int>& matched_atoms,
+ std::vector<int>* regexps) const {
regexps->clear();
if (!compiled_) {
- LOG(WARNING) << "Compile() not called";
- for (int i = 0; i < prefilter_vec_.size(); ++i)
- regexps->push_back(i);
+ // Some legacy users of PrefilterTree call Compile() before
+ // adding any regexps and expect Compile() to have no effect.
+ // This kludge is a counterpart to that kludge.
+ if (prefilter_vec_.empty())
+ return;
+
+ LOG(ERROR) << "RegexpsGivenStrings called before Compile.";
+ for (size_t i = 0; i < prefilter_vec_.size(); i++)
+ regexps->push_back(static_cast<int>(i));
} else {
- if (!prefilter_vec_.empty()) {
- IntMap regexps_map(prefilter_vec_.size());
- vector<int> matched_atom_ids;
- for (int j = 0; j < matched_atoms.size(); j++) {
- matched_atom_ids.push_back(atom_index_to_id_[matched_atoms[j]]);
- VLOG(10) << "Atom id:" << atom_index_to_id_[matched_atoms[j]];
- }
- PropagateMatch(matched_atom_ids, &regexps_map);
- for (IntMap::iterator it = regexps_map.begin();
- it != regexps_map.end();
- ++it)
- regexps->push_back(it->index());
-
- regexps->insert(regexps->end(), unfiltered_.begin(), unfiltered_.end());
- }
+ IntMap regexps_map(static_cast<int>(prefilter_vec_.size()));
+ std::vector<int> matched_atom_ids;
+ for (size_t j = 0; j < matched_atoms.size(); j++)
+ matched_atom_ids.push_back(atom_index_to_id_[matched_atoms[j]]);
+ PropagateMatch(matched_atom_ids, &regexps_map);
+ for (IntMap::iterator it = regexps_map.begin();
+ it != regexps_map.end();
+ ++it)
+ regexps->push_back(it->index());
+
+ regexps->insert(regexps->end(), unfiltered_.begin(), unfiltered_.end());
}
- sort(regexps->begin(), regexps->end());
+ std::sort(regexps->begin(), regexps->end());
}
-void PrefilterTree::PropagateMatch(const vector<int>& atom_ids,
+void PrefilterTree::PropagateMatch(const std::vector<int>& atom_ids,
IntMap* regexps) const {
- IntMap count(entries_.size());
- IntMap work(entries_.size());
- for (int i = 0; i < atom_ids.size(); i++)
+ IntMap count(static_cast<int>(entries_.size()));
+ IntMap work(static_cast<int>(entries_.size()));
+ for (size_t i = 0; i < atom_ids.size(); i++)
work.set(atom_ids[i], 1);
for (IntMap::iterator it = work.begin(); it != work.end(); ++it) {
const Entry& entry = entries_[it->index()];
- VLOG(10) << "Processing: " << it->index();
// Record regexps triggered.
- for (int i = 0; i < entry.regexps.size(); i++) {
- VLOG(10) << "Regexp triggered: " << entry.regexps[i];
+ for (size_t i = 0; i < entry.regexps.size(); i++)
regexps->set(entry.regexps[i], 1);
- }
int c;
// Pass trigger up to parents.
- for (IntMap::iterator it = entry.parents->begin();
+ for (StdIntMap::iterator it = entry.parents->begin();
it != entry.parents->end();
++it) {
- int j = it->index();
+ int j = it->first;
const Entry& parent = entries_[j];
- VLOG(10) << " parent= " << j << " trig= " << parent.propagate_up_at_count;
// Delay until all the children have succeeded.
if (parent.propagate_up_at_count > 1) {
if (count.has_index(j)) {
@@ -342,7 +353,6 @@ void PrefilterTree::PropagateMatch(const vector<int>& atom_ids,
if (c < parent.propagate_up_at_count)
continue;
}
- VLOG(10) << "Triggering: " << j;
// Trigger the parent.
work.set(j, 1);
}
@@ -351,26 +361,26 @@ void PrefilterTree::PropagateMatch(const vector<int>& atom_ids,
// Debugging help.
void PrefilterTree::PrintPrefilter(int regexpid) {
- LOG(INFO) << DebugNodeString(prefilter_vec_[regexpid]);
+ LOG(ERROR) << DebugNodeString(prefilter_vec_[regexpid]);
}
-void PrefilterTree::PrintDebugInfo() {
- VLOG(10) << "#Unique Atoms: " << atom_index_to_id_.size();
- VLOG(10) << "#Unique Nodes: " << entries_.size();
-
- for (int i = 0; i < entries_.size(); ++i) {
- IntMap* parents = entries_[i].parents;
- const vector<int>& regexps = entries_[i].regexps;
- VLOG(10) << "EntryId: " << i
- << " N: " << parents->size() << " R: " << regexps.size();
- for (IntMap::iterator it = parents->begin(); it != parents->end(); ++it)
- VLOG(10) << it->index();
+void PrefilterTree::PrintDebugInfo(NodeMap* nodes) {
+ LOG(ERROR) << "#Unique Atoms: " << atom_index_to_id_.size();
+ LOG(ERROR) << "#Unique Nodes: " << entries_.size();
+
+ for (size_t i = 0; i < entries_.size(); i++) {
+ StdIntMap* parents = entries_[i].parents;
+ const std::vector<int>& regexps = entries_[i].regexps;
+ LOG(ERROR) << "EntryId: " << i
+ << " N: " << parents->size() << " R: " << regexps.size();
+ for (StdIntMap::iterator it = parents->begin(); it != parents->end(); ++it)
+ LOG(ERROR) << it->first;
}
- VLOG(10) << "Map:";
- for (map<string, Prefilter*>::const_iterator iter = node_map_.begin();
- iter != node_map_.end(); ++iter)
- VLOG(10) << "NodeId: " << (*iter).second->unique_id()
- << " Str: " << (*iter).first;
+ LOG(ERROR) << "Map:";
+ for (std::map<string, Prefilter*>::const_iterator iter = nodes->begin();
+ iter != nodes->end(); ++iter)
+ LOG(ERROR) << "NodeId: " << (*iter).second->unique_id()
+ << " Str: " << (*iter).first;
}
string PrefilterTree::DebugNodeString(Prefilter* node) const {
@@ -383,10 +393,10 @@ string PrefilterTree::DebugNodeString(Prefilter* node) const {
// Adding the operation disambiguates AND and OR nodes.
node_string += node->op() == Prefilter::AND ? "AND" : "OR";
node_string += "(";
- for (int i = 0; i < node->subs()->size() ; i++) {
+ for (size_t i = 0; i < node->subs()->size(); i++) {
if (i > 0)
node_string += ',';
- node_string += Itoa((*node->subs())[i]->unique_id());
+ node_string += StringPrintf("%d", (*node->subs())[i]->unique_id());
node_string += ":";
node_string += DebugNodeString((*node->subs())[i]);
}
diff --git a/re2/prefilter_tree.h b/re2/prefilter_tree.h
index 596b734..f81e134 100644
--- a/re2/prefilter_tree.h
+++ b/re2/prefilter_tree.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_PREFILTER_TREE_H_
+#define RE2_PREFILTER_TREE_H_
+
// The PrefilterTree class is used to form an AND-OR tree of strings
// that would trigger each regexp. The 'prefilter' of each regexp is
// added tp PrefilterTree, and then PrefilterTree is used to find all
@@ -12,22 +15,21 @@
// favorite engine. PrefilterTree provides a set of strings (called
// atoms) that the user of this class should use to do the string
// matching.
-//
-#ifndef RE2_PREFILTER_TREE_H_
-#define RE2_PREFILTER_TREE_H_
+
+#include <map>
+#include <string>
+#include <vector>
#include "util/util.h"
#include "util/sparse_array.h"
+#include "re2/prefilter.h"
namespace re2 {
-typedef SparseArray<int> IntMap;
-
-class Prefilter;
-
class PrefilterTree {
public:
PrefilterTree();
+ explicit PrefilterTree(int min_atom_len);
~PrefilterTree();
// Adds the prefilter for the next regexp. Note that we assume that
@@ -41,20 +43,24 @@ class PrefilterTree {
// The caller should use the returned set of strings to do string matching.
// Each time a string matches, the corresponding index then has to be
// and passed to RegexpsGivenStrings below.
- void Compile(vector<string>* atom_vec);
+ void Compile(std::vector<string>* atom_vec);
// Given the indices of the atoms that matched, returns the indexes
// of regexps that should be searched. The matched_atoms should
// contain all the ids of string atoms that were found to match the
// content. The caller can use any string match engine to perform
// this function. This function is thread safe.
- void RegexpsGivenStrings(const vector<int>& matched_atoms,
- vector<int>* regexps) const;
+ void RegexpsGivenStrings(const std::vector<int>& matched_atoms,
+ std::vector<int>* regexps) const;
// Print debug prefilter. Also prints unique ids associated with
// nodes of the prefilter of the regexp.
void PrintPrefilter(int regexpid);
+ private:
+ typedef SparseArray<int> IntMap;
+ typedef std::map<int, int> StdIntMap;
+ typedef std::map<string, Prefilter*> NodeMap;
// Each unique node has a corresponding Entry that helps in
// passing the matching trigger information along the tree.
@@ -71,26 +77,28 @@ class PrefilterTree {
// are two different nodes, but they share the atom 'def'. So when
// 'def' matches, it triggers two parents, corresponding to the two
// different OR nodes.
- IntMap* parents;
+ StdIntMap* parents;
// When this node is ready to trigger the parent, what are the
// regexps that are triggered.
- vector<int> regexps;
+ std::vector<int> regexps;
};
- private:
+ // Returns true if the prefilter node should be kept.
+ bool KeepNode(Prefilter* node) const;
+
// This function assigns unique ids to various parts of the
// prefilter, by looking at if these nodes are already in the
// PrefilterTree.
- void AssignUniqueIds(vector<string>* atom_vec);
+ void AssignUniqueIds(NodeMap* nodes, std::vector<string>* atom_vec);
// Given the matching atoms, find the regexps to be triggered.
- void PropagateMatch(const vector<int>& atom_ids,
+ void PropagateMatch(const std::vector<int>& atom_ids,
IntMap* regexps) const;
// Returns the prefilter node that has the same NodeString as this
// node. For the canonical node, returns node.
- Prefilter* CanonicalNode(Prefilter* node);
+ Prefilter* CanonicalNode(NodeMap* nodes, Prefilter* node);
// A string that uniquely identifies the node. Assumes that the
// children of node has already been assigned unique ids.
@@ -100,29 +108,30 @@ class PrefilterTree {
string DebugNodeString(Prefilter* node) const;
// Used for debugging.
- void PrintDebugInfo();
+ void PrintDebugInfo(NodeMap* nodes);
// These are all the nodes formed by Compile. Essentially, there is
// one node for each unique atom and each unique AND/OR node.
- vector<Entry> entries_;
-
- // Map node string to canonical Prefilter node.
- map<string, Prefilter*> node_map_;
+ std::vector<Entry> entries_;
// indices of regexps that always pass through the filter (since we
// found no required literals in these regexps).
- vector<int> unfiltered_;
+ std::vector<int> unfiltered_;
// vector of Prefilter for all regexps.
- vector<Prefilter*> prefilter_vec_;
+ std::vector<Prefilter*> prefilter_vec_;
// Atom index in returned strings to entry id mapping.
- vector<int> atom_index_to_id_;
+ std::vector<int> atom_index_to_id_;
// Has the prefilter tree been compiled.
bool compiled_;
- DISALLOW_EVIL_CONSTRUCTORS(PrefilterTree);
+ // Strings less than this length are not stored as atoms.
+ const int min_atom_len_;
+
+ PrefilterTree(const PrefilterTree&) = delete;
+ PrefilterTree& operator=(const PrefilterTree&) = delete;
};
} // namespace
diff --git a/re2/prog.cc b/re2/prog.cc
index ef9ef23..fa03af9 100644
--- a/re2/prog.cc
+++ b/re2/prog.cc
@@ -5,48 +5,57 @@
// Compiled regular expression representation.
// Tested by compile_test.cc
-#include "util/util.h"
-#include "util/sparse_set.h"
#include "re2/prog.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "util/util.h"
+#include "util/logging.h"
+#include "util/strutil.h"
+#include "re2/bitmap256.h"
#include "re2/stringpiece.h"
namespace re2 {
// Constructors per Inst opcode
-void Prog::Inst::InitAlt(uint32 out, uint32 out1) {
+void Prog::Inst::InitAlt(uint32_t out, uint32_t out1) {
DCHECK_EQ(out_opcode_, 0);
set_out_opcode(out, kInstAlt);
out1_ = out1;
}
-void Prog::Inst::InitByteRange(int lo, int hi, int foldcase, uint32 out) {
+void Prog::Inst::InitByteRange(int lo, int hi, int foldcase, uint32_t out) {
DCHECK_EQ(out_opcode_, 0);
set_out_opcode(out, kInstByteRange);
lo_ = lo & 0xFF;
hi_ = hi & 0xFF;
- foldcase_ = foldcase;
+ foldcase_ = foldcase & 0xFF;
}
-void Prog::Inst::InitCapture(int cap, uint32 out) {
+void Prog::Inst::InitCapture(int cap, uint32_t out) {
DCHECK_EQ(out_opcode_, 0);
set_out_opcode(out, kInstCapture);
cap_ = cap;
}
-void Prog::Inst::InitEmptyWidth(EmptyOp empty, uint32 out) {
+void Prog::Inst::InitEmptyWidth(EmptyOp empty, uint32_t out) {
DCHECK_EQ(out_opcode_, 0);
set_out_opcode(out, kInstEmptyWidth);
empty_ = empty;
}
-void Prog::Inst::InitMatch(int32 id) {
+void Prog::Inst::InitMatch(int32_t id) {
DCHECK_EQ(out_opcode_, 0);
set_opcode(kInstMatch);
match_id_ = id;
}
-void Prog::Inst::InitNop(uint32 out) {
+void Prog::Inst::InitNop(uint32_t out) {
DCHECK_EQ(out_opcode_, 0);
set_opcode(kInstNop);
}
@@ -94,34 +103,25 @@ Prog::Prog()
: anchor_start_(false),
anchor_end_(false),
reversed_(false),
+ did_flatten_(false),
did_onepass_(false),
start_(0),
start_unanchored_(0),
size_(0),
- byte_inst_count_(0),
bytemap_range_(0),
+ first_byte_(-1),
flags_(0),
- onepass_statesize_(0),
- inst_(NULL),
- dfa_first_(NULL),
- dfa_longest_(NULL),
- dfa_mem_(0),
- delete_dfa_(NULL),
- unbytemap_(NULL),
+ list_count_(0),
onepass_nodes_(NULL),
- onepass_start_(NULL) {
+ dfa_mem_(0),
+ dfa_first_(NULL),
+ dfa_longest_(NULL) {
}
Prog::~Prog() {
- if (delete_dfa_) {
- if (dfa_first_)
- delete_dfa_(dfa_first_);
- if (dfa_longest_)
- delete_dfa_(dfa_longest_);
- }
+ DeleteDFA(dfa_longest_);
+ DeleteDFA(dfa_first_);
delete[] onepass_nodes_;
- delete[] inst_;
- delete[] unbytemap_;
}
typedef SparseSet Workq;
@@ -133,7 +133,6 @@ static inline void AddToQueue(Workq* q, int id) {
static string ProgToString(Prog* prog, Workq* q) {
string s;
-
for (Workq::iterator i = q->begin(); i != q->end(); ++i) {
int id = *i;
Prog::Inst* ip = prog->inst(id);
@@ -145,29 +144,56 @@ static string ProgToString(Prog* prog, Workq* q) {
return s;
}
-string Prog::Dump() {
- string map;
- if (false) { // Debugging
- int lo = 0;
- StringAppendF(&map, "byte map:\n");
- for (int i = 0; i < bytemap_range_; i++) {
- StringAppendF(&map, "\t%d. [%02x-%02x]\n", i, lo, unbytemap_[i]);
- lo = unbytemap_[i] + 1;
- }
- StringAppendF(&map, "\n");
+static string FlattenedProgToString(Prog* prog, int start) {
+ string s;
+ for (int id = start; id < prog->size(); id++) {
+ Prog::Inst* ip = prog->inst(id);
+ if (ip->last())
+ StringAppendF(&s, "%d. %s\n", id, ip->Dump().c_str());
+ else
+ StringAppendF(&s, "%d+ %s\n", id, ip->Dump().c_str());
}
+ return s;
+}
+
+string Prog::Dump() {
+ if (did_flatten_)
+ return FlattenedProgToString(this, start_);
Workq q(size_);
AddToQueue(&q, start_);
- return map + ProgToString(this, &q);
+ return ProgToString(this, &q);
}
string Prog::DumpUnanchored() {
+ if (did_flatten_)
+ return FlattenedProgToString(this, start_unanchored_);
+
Workq q(size_);
AddToQueue(&q, start_unanchored_);
return ProgToString(this, &q);
}
+string Prog::DumpByteMap() {
+ string map;
+ for (int c = 0; c < 256; c++) {
+ int b = bytemap_[c];
+ int lo = c;
+ while (c < 256-1 && bytemap_[c+1] == b)
+ c++;
+ int hi = c;
+ StringAppendF(&map, "[%02x-%02x] -> %d\n", lo, hi, b);
+ }
+ return map;
+}
+
+int Prog::first_byte() {
+ std::call_once(first_byte_once_, [](Prog* prog) {
+ prog->first_byte_ = prog->ComputeFirstByte();
+ }, this);
+ return first_byte_;
+}
+
static bool IsMatch(Prog*, Prog::Inst*);
// Peep-hole optimizer.
@@ -260,7 +286,7 @@ static bool IsMatch(Prog* prog, Prog::Inst* ip) {
}
}
-uint32 Prog::EmptyFlags(const StringPiece& text, const char* p) {
+uint32_t Prog::EmptyFlags(const StringPiece& text, const char* p) {
int flags = 0;
// ^ and \A
@@ -294,48 +320,504 @@ uint32 Prog::EmptyFlags(const StringPiece& text, const char* p) {
return flags;
}
-void Prog::MarkByteRange(int lo, int hi) {
- CHECK_GE(lo, 0);
- CHECK_GE(hi, 0);
- CHECK_LE(lo, 255);
- CHECK_LE(hi, 255);
- if (lo > 0)
- byterange_.Set(lo - 1);
- byterange_.Set(hi);
+// ByteMapBuilder implements a coloring algorithm.
+//
+// The first phase is a series of "mark and merge" batches: we mark one or more
+// [lo-hi] ranges, then merge them into our internal state. Batching is not for
+// performance; rather, it means that the ranges are treated indistinguishably.
+//
+// Internally, the ranges are represented using a bitmap that stores the splits
+// and a vector that stores the colors; both of them are indexed by the ranges'
+// last bytes. Thus, in order to merge a [lo-hi] range, we split at lo-1 and at
+// hi (if not already split), then recolor each range in between. The color map
+// (i.e. from the old color to the new color) is maintained for the lifetime of
+// the batch and so underpins this somewhat obscure approach to set operations.
+//
+// The second phase builds the bytemap from our internal state: we recolor each
+// range, then store the new color (which is now the byte class) in each of the
+// corresponding array elements. Finally, we output the number of byte classes.
+class ByteMapBuilder {
+ public:
+ ByteMapBuilder() {
+ // Initial state: the [0-255] range has color 256.
+ // This will avoid problems during the second phase,
+ // in which we assign byte classes numbered from 0.
+ splits_.Set(255);
+ colors_.resize(256);
+ colors_[255] = 256;
+ nextcolor_ = 257;
+ }
+
+ void Mark(int lo, int hi);
+ void Merge();
+ void Build(uint8_t* bytemap, int* bytemap_range);
+
+ private:
+ int Recolor(int oldcolor);
+
+ Bitmap256 splits_;
+ std::vector<int> colors_;
+ int nextcolor_;
+ std::vector<std::pair<int, int>> colormap_;
+ std::vector<std::pair<int, int>> ranges_;
+
+ ByteMapBuilder(const ByteMapBuilder&) = delete;
+ ByteMapBuilder& operator=(const ByteMapBuilder&) = delete;
+};
+
+void ByteMapBuilder::Mark(int lo, int hi) {
+ DCHECK_GE(lo, 0);
+ DCHECK_GE(hi, 0);
+ DCHECK_LE(lo, 255);
+ DCHECK_LE(hi, 255);
+ DCHECK_LE(lo, hi);
+
+ // Ignore any [0-255] ranges. They cause us to recolor every range, which
+ // has no effect on the eventual result and is therefore a waste of time.
+ if (lo == 0 && hi == 255)
+ return;
+
+ ranges_.emplace_back(lo, hi);
}
-void Prog::ComputeByteMap() {
- // Fill in bytemap with byte classes for prog_.
- // Ranges of bytes that are treated as indistinguishable
- // by the regexp program are mapped to a single byte class.
- // The vector prog_->byterange() marks the end of each
- // such range.
- const Bitmap<256>& v = byterange();
-
- COMPILE_ASSERT(8*sizeof(v.Word(0)) == 32, wordsize);
- uint8 n = 0;
- uint32 bits = 0;
- for (int i = 0; i < 256; i++) {
- if ((i&31) == 0)
- bits = v.Word(i >> 5);
- bytemap_[i] = n;
- n += bits & 1;
- bits >>= 1;
+void ByteMapBuilder::Merge() {
+ for (std::vector<std::pair<int, int>>::const_iterator it = ranges_.begin();
+ it != ranges_.end();
+ ++it) {
+ int lo = it->first-1;
+ int hi = it->second;
+
+ if (0 <= lo && !splits_.Test(lo)) {
+ splits_.Set(lo);
+ int next = splits_.FindNextSetBit(lo+1);
+ colors_[lo] = colors_[next];
+ }
+ if (!splits_.Test(hi)) {
+ splits_.Set(hi);
+ int next = splits_.FindNextSetBit(hi+1);
+ colors_[hi] = colors_[next];
+ }
+
+ int c = lo+1;
+ while (c < 256) {
+ int next = splits_.FindNextSetBit(c);
+ colors_[next] = Recolor(colors_[next]);
+ if (next == hi)
+ break;
+ c = next+1;
+ }
}
- bytemap_range_ = bytemap_[255] + 1;
- unbytemap_ = new uint8[bytemap_range_];
- for (int i = 0; i < 256; i++)
- unbytemap_[bytemap_[i]] = i;
-
- if (0) { // For debugging: use trivial byte map.
- for (int i = 0; i < 256; i++) {
- bytemap_[i] = i;
- unbytemap_[i] = i;
+ colormap_.clear();
+ ranges_.clear();
+}
+
+void ByteMapBuilder::Build(uint8_t* bytemap, int* bytemap_range) {
+ // Assign byte classes numbered from 0.
+ nextcolor_ = 0;
+
+ int c = 0;
+ while (c < 256) {
+ int next = splits_.FindNextSetBit(c);
+ uint8_t b = static_cast<uint8_t>(Recolor(colors_[next]));
+ while (c <= next) {
+ bytemap[c] = b;
+ c++;
}
+ }
+
+ *bytemap_range = nextcolor_;
+}
+
+int ByteMapBuilder::Recolor(int oldcolor) {
+ // Yes, this is a linear search. There can be at most 256
+ // colors and there will typically be far fewer than that.
+ // Also, we need to consider keys *and* values in order to
+ // avoid recoloring a given range more than once per batch.
+ std::vector<std::pair<int, int>>::const_iterator it =
+ std::find_if(colormap_.begin(), colormap_.end(),
+ [=](const std::pair<int, int>& kv) -> bool {
+ return kv.first == oldcolor || kv.second == oldcolor;
+ });
+ if (it != colormap_.end())
+ return it->second;
+ int newcolor = nextcolor_;
+ nextcolor_++;
+ colormap_.emplace_back(oldcolor, newcolor);
+ return newcolor;
+}
+
+void Prog::ComputeByteMap() {
+ // Fill in bytemap with byte classes for the program.
+ // Ranges of bytes that are treated indistinguishably
+ // will be mapped to a single byte class.
+ ByteMapBuilder builder;
+
+ // Don't repeat the work for ^ and $.
+ bool marked_line_boundaries = false;
+ // Don't repeat the work for \b and \B.
+ bool marked_word_boundaries = false;
+
+ for (int id = 0; id < size(); id++) {
+ Inst* ip = inst(id);
+ if (ip->opcode() == kInstByteRange) {
+ int lo = ip->lo();
+ int hi = ip->hi();
+ builder.Mark(lo, hi);
+ if (ip->foldcase() && lo <= 'z' && hi >= 'a') {
+ int foldlo = lo;
+ int foldhi = hi;
+ if (foldlo < 'a')
+ foldlo = 'a';
+ if (foldhi > 'z')
+ foldhi = 'z';
+ if (foldlo <= foldhi)
+ builder.Mark(foldlo + 'A' - 'a', foldhi + 'A' - 'a');
+ }
+ // If this Inst is not the last Inst in its list AND the next Inst is
+ // also a ByteRange AND the Insts have the same out, defer the merge.
+ if (!ip->last() &&
+ inst(id+1)->opcode() == kInstByteRange &&
+ ip->out() == inst(id+1)->out())
+ continue;
+ builder.Merge();
+ } else if (ip->opcode() == kInstEmptyWidth) {
+ if (ip->empty() & (kEmptyBeginLine|kEmptyEndLine) &&
+ !marked_line_boundaries) {
+ builder.Mark('\n', '\n');
+ builder.Merge();
+ marked_line_boundaries = true;
+ }
+ if (ip->empty() & (kEmptyWordBoundary|kEmptyNonWordBoundary) &&
+ !marked_word_boundaries) {
+ // We require two batches here: the first for ranges that are word
+ // characters, the second for ranges that are not word characters.
+ for (bool isword : {true, false}) {
+ int j;
+ for (int i = 0; i < 256; i = j) {
+ for (j = i + 1; j < 256 &&
+ Prog::IsWordChar(static_cast<uint8_t>(i)) ==
+ Prog::IsWordChar(static_cast<uint8_t>(j));
+ j++)
+ ;
+ if (Prog::IsWordChar(static_cast<uint8_t>(i)) == isword)
+ builder.Mark(i, j - 1);
+ }
+ builder.Merge();
+ }
+ marked_word_boundaries = true;
+ }
+ }
+ }
+
+ builder.Build(bytemap_, &bytemap_range_);
+
+ if (0) { // For debugging, use trivial bytemap.
+ LOG(ERROR) << "Using trivial bytemap.";
+ for (int i = 0; i < 256; i++)
+ bytemap_[i] = static_cast<uint8_t>(i);
bytemap_range_ = 256;
- LOG(INFO) << "Using trivial bytemap.";
}
}
-} // namespace re2
+// Prog::Flatten() implements a graph rewriting algorithm.
+//
+// The overall process is similar to epsilon removal, but retains some epsilon
+// transitions: those from Capture and EmptyWidth instructions; and those from
+// nullable subexpressions. (The latter avoids quadratic blowup in transitions
+// in the worst case.) It might be best thought of as Alt instruction elision.
+//
+// In conceptual terms, it divides the Prog into "trees" of instructions, then
+// traverses the "trees" in order to produce "lists" of instructions. A "tree"
+// is one or more instructions that grow from one "root" instruction to one or
+// more "leaf" instructions; if a "tree" has exactly one instruction, then the
+// "root" is also the "leaf". In most cases, a "root" is the successor of some
+// "leaf" (i.e. the "leaf" instruction's out() returns the "root" instruction)
+// and is considered a "successor root". A "leaf" can be a ByteRange, Capture,
+// EmptyWidth or Match instruction. However, this is insufficient for handling
+// nested nullable subexpressions correctly, so in some cases, a "root" is the
+// dominator of the instructions reachable from some "successor root" (i.e. it
+// has an unreachable predecessor) and is considered a "dominator root". Since
+// only Alt instructions can be "dominator roots" (other instructions would be
+// "leaves"), only Alt instructions are required to be marked as predecessors.
+//
+// Dividing the Prog into "trees" comprises two passes: marking the "successor
+// roots" and the predecessors; and marking the "dominator roots". Sorting the
+// "successor roots" by their bytecode offsets enables iteration in order from
+// greatest to least during the second pass; by working backwards in this case
+// and flooding the graph no further than "leaves" and already marked "roots",
+// it becomes possible to mark "dominator roots" without doing excessive work.
+//
+// Traversing the "trees" is just iterating over the "roots" in order of their
+// marking and flooding the graph no further than "leaves" and "roots". When a
+// "leaf" is reached, the instruction is copied with its successor remapped to
+// its "root" number. When a "root" is reached, a Nop instruction is generated
+// with its successor remapped similarly. As each "list" is produced, its last
+// instruction is marked as such. After all of the "lists" have been produced,
+// a pass over their instructions remaps their successors to bytecode offsets.
+void Prog::Flatten() {
+ if (did_flatten_)
+ return;
+ did_flatten_ = true;
+
+ // Scratch structures. It's important that these are reused by functions
+ // that we call in loops because they would thrash the heap otherwise.
+ SparseSet reachable(size());
+ std::vector<int> stk;
+ stk.reserve(size());
+
+ // First pass: Marks "successor roots" and predecessors.
+ // Builds the mapping from inst-ids to root-ids.
+ SparseArray<int> rootmap(size());
+ SparseArray<int> predmap(size());
+ std::vector<std::vector<int>> predvec;
+ MarkSuccessors(&rootmap, &predmap, &predvec, &reachable, &stk);
+
+ // Second pass: Marks "dominator roots".
+ SparseArray<int> sorted(rootmap);
+ std::sort(sorted.begin(), sorted.end(), sorted.less);
+ for (SparseArray<int>::const_iterator i = sorted.end() - 1;
+ i != sorted.begin();
+ --i) {
+ if (i->index() != start_unanchored() && i->index() != start())
+ MarkDominator(i->index(), &rootmap, &predmap, &predvec, &reachable, &stk);
+ }
+
+ // Third pass: Emits "lists". Remaps outs to root-ids.
+ // Builds the mapping from root-ids to flat-ids.
+ std::vector<int> flatmap(rootmap.size());
+ std::vector<Inst> flat;
+ flat.reserve(size());
+ for (SparseArray<int>::const_iterator i = rootmap.begin();
+ i != rootmap.end();
+ ++i) {
+ flatmap[i->value()] = static_cast<int>(flat.size());
+ EmitList(i->index(), &rootmap, &flat, &reachable, &stk);
+ flat.back().set_last();
+ }
+
+ list_count_ = static_cast<int>(flatmap.size());
+ for (int i = 0; i < kNumInst; i++)
+ inst_count_[i] = 0;
+
+ // Fourth pass: Remaps outs to flat-ids.
+ // Counts instructions by opcode.
+ for (int id = 0; id < static_cast<int>(flat.size()); id++) {
+ Inst* ip = &flat[id];
+ if (ip->opcode() != kInstAltMatch) // handled in EmitList()
+ ip->set_out(flatmap[ip->out()]);
+ inst_count_[ip->opcode()]++;
+ }
+ int total = 0;
+ for (int i = 0; i < kNumInst; i++)
+ total += inst_count_[i];
+ DCHECK_EQ(total, static_cast<int>(flat.size()));
+
+ // Remap start_unanchored and start.
+ if (start_unanchored() == 0) {
+ DCHECK_EQ(start(), 0);
+ } else if (start_unanchored() == start()) {
+ set_start_unanchored(flatmap[1]);
+ set_start(flatmap[1]);
+ } else {
+ set_start_unanchored(flatmap[1]);
+ set_start(flatmap[2]);
+ }
+
+ // Finally, replace the old instructions with the new instructions.
+ size_ = static_cast<int>(flat.size());
+ inst_ = PODArray<Inst>(size_);
+ memmove(inst_.data(), flat.data(), size_*sizeof(inst_[0]));
+}
+
+void Prog::MarkSuccessors(SparseArray<int>* rootmap,
+ SparseArray<int>* predmap,
+ std::vector<std::vector<int>>* predvec,
+ SparseSet* reachable, std::vector<int>* stk) {
+ // Mark the kInstFail instruction.
+ rootmap->set_new(0, rootmap->size());
+
+ // Mark the start_unanchored and start instructions.
+ if (!rootmap->has_index(start_unanchored()))
+ rootmap->set_new(start_unanchored(), rootmap->size());
+ if (!rootmap->has_index(start()))
+ rootmap->set_new(start(), rootmap->size());
+
+ reachable->clear();
+ stk->clear();
+ stk->push_back(start_unanchored());
+ while (!stk->empty()) {
+ int id = stk->back();
+ stk->pop_back();
+ Loop:
+ if (reachable->contains(id))
+ continue;
+ reachable->insert_new(id);
+
+ Inst* ip = inst(id);
+ switch (ip->opcode()) {
+ default:
+ LOG(DFATAL) << "unhandled opcode: " << ip->opcode();
+ break;
+
+ case kInstAltMatch:
+ case kInstAlt:
+ // Mark this instruction as a predecessor of each out.
+ for (int out : {ip->out(), ip->out1()}) {
+ if (!predmap->has_index(out)) {
+ predmap->set_new(out, static_cast<int>(predvec->size()));
+ predvec->emplace_back();
+ }
+ (*predvec)[predmap->get_existing(out)].emplace_back(id);
+ }
+ stk->push_back(ip->out1());
+ id = ip->out();
+ goto Loop;
+
+ case kInstByteRange:
+ case kInstCapture:
+ case kInstEmptyWidth:
+ // Mark the out of this instruction as a "root".
+ if (!rootmap->has_index(ip->out()))
+ rootmap->set_new(ip->out(), rootmap->size());
+ id = ip->out();
+ goto Loop;
+
+ case kInstNop:
+ id = ip->out();
+ goto Loop;
+
+ case kInstMatch:
+ case kInstFail:
+ break;
+ }
+ }
+}
+
+void Prog::MarkDominator(int root, SparseArray<int>* rootmap,
+ SparseArray<int>* predmap,
+ std::vector<std::vector<int>>* predvec,
+ SparseSet* reachable, std::vector<int>* stk) {
+ reachable->clear();
+ stk->clear();
+ stk->push_back(root);
+ while (!stk->empty()) {
+ int id = stk->back();
+ stk->pop_back();
+ Loop:
+ if (reachable->contains(id))
+ continue;
+ reachable->insert_new(id);
+
+ if (id != root && rootmap->has_index(id)) {
+ // We reached another "tree" via epsilon transition.
+ continue;
+ }
+
+ Inst* ip = inst(id);
+ switch (ip->opcode()) {
+ default:
+ LOG(DFATAL) << "unhandled opcode: " << ip->opcode();
+ break;
+
+ case kInstAltMatch:
+ case kInstAlt:
+ stk->push_back(ip->out1());
+ id = ip->out();
+ goto Loop;
+
+ case kInstByteRange:
+ case kInstCapture:
+ case kInstEmptyWidth:
+ break;
+
+ case kInstNop:
+ id = ip->out();
+ goto Loop;
+
+ case kInstMatch:
+ case kInstFail:
+ break;
+ }
+ }
+
+ for (SparseSet::const_iterator i = reachable->begin();
+ i != reachable->end();
+ ++i) {
+ int id = *i;
+ if (predmap->has_index(id)) {
+ for (int pred : (*predvec)[predmap->get_existing(id)]) {
+ if (!reachable->contains(pred)) {
+ // id has a predecessor that cannot be reached from root!
+ // Therefore, id must be a "root" too - mark it as such.
+ if (!rootmap->has_index(id))
+ rootmap->set_new(id, rootmap->size());
+ }
+ }
+ }
+ }
+}
+
+void Prog::EmitList(int root, SparseArray<int>* rootmap,
+ std::vector<Inst>* flat,
+ SparseSet* reachable, std::vector<int>* stk) {
+ reachable->clear();
+ stk->clear();
+ stk->push_back(root);
+ while (!stk->empty()) {
+ int id = stk->back();
+ stk->pop_back();
+ Loop:
+ if (reachable->contains(id))
+ continue;
+ reachable->insert_new(id);
+
+ if (id != root && rootmap->has_index(id)) {
+ // We reached another "tree" via epsilon transition. Emit a kInstNop
+ // instruction so that the Prog does not become quadratically larger.
+ flat->emplace_back();
+ flat->back().set_opcode(kInstNop);
+ flat->back().set_out(rootmap->get_existing(id));
+ continue;
+ }
+
+ Inst* ip = inst(id);
+ switch (ip->opcode()) {
+ default:
+ LOG(DFATAL) << "unhandled opcode: " << ip->opcode();
+ break;
+
+ case kInstAltMatch:
+ flat->emplace_back();
+ flat->back().set_opcode(kInstAltMatch);
+ flat->back().set_out(static_cast<int>(flat->size()));
+ flat->back().out1_ = static_cast<uint32_t>(flat->size())+1;
+ FALLTHROUGH_INTENDED;
+
+ case kInstAlt:
+ stk->push_back(ip->out1());
+ id = ip->out();
+ goto Loop;
+
+ case kInstByteRange:
+ case kInstCapture:
+ case kInstEmptyWidth:
+ flat->emplace_back();
+ memmove(&flat->back(), ip, sizeof *ip);
+ flat->back().set_out(rootmap->get_existing(ip->out()));
+ break;
+
+ case kInstNop:
+ id = ip->out();
+ goto Loop;
+
+ case kInstMatch:
+ case kInstFail:
+ flat->emplace_back();
+ memmove(&flat->back(), ip, sizeof *ip);
+ break;
+ }
+ }
+}
+
+} // namespace re2
diff --git a/re2/prog.h b/re2/prog.h
index 2cf65bc..268ab9d 100644
--- a/re2/prog.h
+++ b/re2/prog.h
@@ -2,50 +2,29 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_PROG_H_
+#define RE2_PROG_H_
+
// Compiled representation of regular expressions.
// See regexp.h for the Regexp class, which represents a regular
// expression symbolically.
-#ifndef RE2_PROG_H__
-#define RE2_PROG_H__
+#include <stdint.h>
+#include <functional>
+#include <mutex>
+#include <string>
+#include <vector>
+#include <type_traits>
#include "util/util.h"
+#include "util/logging.h"
+#include "util/pod_array.h"
+#include "util/sparse_array.h"
+#include "util/sparse_set.h"
#include "re2/re2.h"
namespace re2 {
-// Simple fixed-size bitmap.
-template<int Bits>
-class Bitmap {
- public:
- Bitmap() { Reset(); }
- int Size() { return Bits; }
-
- void Reset() {
- for (int i = 0; i < Words; i++)
- w_[i] = 0;
- }
- bool Get(int k) const {
- return w_[k >> WordLog] & (1<<(k & 31));
- }
- void Set(int k) {
- w_[k >> WordLog] |= 1<<(k & 31);
- }
- void Clear(int k) {
- w_[k >> WordLog] &= ~(1<<(k & 31));
- }
- uint32 Word(int i) const {
- return w_[i];
- }
-
- private:
- static const int WordLog = 5;
- static const int Words = (Bits+31)/32;
- uint32 w_[Words];
- DISALLOW_EVIL_CONSTRUCTORS(Bitmap);
-};
-
-
// Opcodes for Inst
enum InstOp {
kInstAlt = 0, // choose between out_ and out1_
@@ -56,6 +35,7 @@ enum InstOp {
kInstMatch, // found a match!
kInstNop, // no-op; occasionally unavoidable
kInstFail, // never match; occasionally unavoidable
+ kNumInst,
};
// Bit flags for empty-width specials
@@ -69,10 +49,8 @@ enum EmptyOp {
kEmptyAllFlags = (1<<6)-1,
};
-class Regexp;
-
class DFA;
-struct OneState;
+class Regexp;
// Compiled form of regexp program.
class Prog {
@@ -83,31 +61,40 @@ class Prog {
// Single instruction in regexp program.
class Inst {
public:
- Inst() : out_opcode_(0), out1_(0) { }
+ // See the assertion below for why this is so.
+ Inst() = default;
+
+ // Copyable.
+ Inst(const Inst&) = default;
+ Inst& operator=(const Inst&) = default;
// Constructors per opcode
- void InitAlt(uint32 out, uint32 out1);
- void InitByteRange(int lo, int hi, int foldcase, uint32 out);
- void InitCapture(int cap, uint32 out);
- void InitEmptyWidth(EmptyOp empty, uint32 out);
+ void InitAlt(uint32_t out, uint32_t out1);
+ void InitByteRange(int lo, int hi, int foldcase, uint32_t out);
+ void InitCapture(int cap, uint32_t out);
+ void InitEmptyWidth(EmptyOp empty, uint32_t out);
void InitMatch(int id);
- void InitNop(uint32 out);
+ void InitNop(uint32_t out);
void InitFail();
// Getters
- int id(Prog* p) { return this - p->inst_; }
+ int id(Prog* p) { return static_cast<int>(this - p->inst_.data()); }
InstOp opcode() { return static_cast<InstOp>(out_opcode_&7); }
- int out() { return out_opcode_>>3; }
- int out1() { DCHECK(opcode() == kInstAlt || opcode() == kInstAltMatch); return out1_; }
+ int last() { return (out_opcode_>>3)&1; }
+ int out() { return out_opcode_>>4; }
+ int out1() { DCHECK(opcode() == kInstAlt || opcode() == kInstAltMatch); return out1_; }
int cap() { DCHECK_EQ(opcode(), kInstCapture); return cap_; }
int lo() { DCHECK_EQ(opcode(), kInstByteRange); return lo_; }
int hi() { DCHECK_EQ(opcode(), kInstByteRange); return hi_; }
int foldcase() { DCHECK_EQ(opcode(), kInstByteRange); return foldcase_; }
int match_id() { DCHECK_EQ(opcode(), kInstMatch); return match_id_; }
EmptyOp empty() { DCHECK_EQ(opcode(), kInstEmptyWidth); return empty_; }
- bool greedy(Prog *p) {
+
+ bool greedy(Prog* p) {
DCHECK_EQ(opcode(), kInstAltMatch);
- return p->inst(out())->opcode() == kInstByteRange;
+ return p->inst(out())->opcode() == kInstByteRange ||
+ (p->inst(out())->opcode() == kInstNop &&
+ p->inst(p->inst(out())->out())->opcode() == kInstByteRange);
}
// Does this inst (an kInstByteRange) match c?
@@ -122,54 +109,61 @@ class Prog {
string Dump();
// Maximum instruction id.
- // (Must fit in out_opcode_, and PatchList steals another bit.)
+ // (Must fit in out_opcode_. PatchList/last steal another bit.)
static const int kMaxInst = (1<<28) - 1;
private:
void set_opcode(InstOp opcode) {
- out_opcode_ = (out()<<3) | opcode;
+ out_opcode_ = (out()<<4) | (last()<<3) | opcode;
+ }
+
+ void set_last() {
+ out_opcode_ = (out()<<4) | (1<<3) | opcode();
}
void set_out(int out) {
- out_opcode_ = (out<<3) | opcode();
+ out_opcode_ = (out<<4) | (last()<<3) | opcode();
}
void set_out_opcode(int out, InstOp opcode) {
- out_opcode_ = (out<<3) | opcode;
+ out_opcode_ = (out<<4) | (last()<<3) | opcode;
}
- uint32 out_opcode_; // 29 bits of out, 3 (low) bits opcode
- union { // additional instruction arguments:
- uint32 out1_; // opcode == kInstAlt
- // alternate next instruction
-
- int32 cap_; // opcode == kInstCapture
- // Index of capture register (holds text
- // position recorded by capturing parentheses).
- // For \n (the submatch for the nth parentheses),
- // the left parenthesis captures into register 2*n
- // and the right one captures into register 2*n+1.
-
- int32 match_id_; // opcode == kInstMatch
- // Match ID to identify this match (for re2::Set).
-
- struct { // opcode == kInstByteRange
- uint8 lo_; // byte range is lo_-hi_ inclusive
- uint8 hi_; //
- uint8 foldcase_; // convert A-Z to a-z before checking range.
+ uint32_t out_opcode_; // 28 bits: out, 1 bit: last, 3 (low) bits: opcode
+ union { // additional instruction arguments:
+ uint32_t out1_; // opcode == kInstAlt
+ // alternate next instruction
+
+ int32_t cap_; // opcode == kInstCapture
+ // Index of capture register (holds text
+ // position recorded by capturing parentheses).
+ // For \n (the submatch for the nth parentheses),
+ // the left parenthesis captures into register 2*n
+ // and the right one captures into register 2*n+1.
+
+ int32_t match_id_; // opcode == kInstMatch
+ // Match ID to identify this match (for re2::Set).
+
+ struct { // opcode == kInstByteRange
+ uint8_t lo_; // byte range is lo_-hi_ inclusive
+ uint8_t hi_; //
+ uint8_t foldcase_; // convert A-Z to a-z before checking range.
};
- EmptyOp empty_; // opcode == kInstEmptyWidth
- // empty_ is bitwise OR of kEmpty* flags above.
+ EmptyOp empty_; // opcode == kInstEmptyWidth
+ // empty_ is bitwise OR of kEmpty* flags above.
};
friend class Compiler;
friend struct PatchList;
friend class Prog;
-
- DISALLOW_EVIL_CONSTRUCTORS(Inst);
};
+ // Inst must be trivial so that we can freely clear it with memset(3).
+ // Arrays of Inst are initialised by copying the initial elements with
+ // memmove(3) and then clearing any remaining elements with memset(3).
+ static_assert(std::is_trivial<Inst>::value, "Inst must be trivial");
+
// Whether to anchor the search.
enum Anchor {
kUnanchored, // match anywhere
@@ -200,13 +194,13 @@ class Prog {
int start_unanchored() { return start_unanchored_; }
void set_start(int start) { start_ = start; }
void set_start_unanchored(int start) { start_unanchored_ = start; }
- int64 size() { return size_; }
+ int size() { return size_; }
bool reversed() { return reversed_; }
void set_reversed(bool reversed) { reversed_ = reversed; }
- int64 byte_inst_count() { return byte_inst_count_; }
- const Bitmap<256>& byterange() { return byterange_; }
- void set_dfa_mem(int64 dfa_mem) { dfa_mem_ = dfa_mem; }
- int64 dfa_mem() { return dfa_mem_; }
+ int list_count() { return list_count_; }
+ int inst_count(InstOp op) { return inst_count_[op]; }
+ void set_dfa_mem(int64_t dfa_mem) { dfa_mem_ = dfa_mem; }
+ int64_t dfa_mem() { return dfa_mem_; }
int flags() { return flags_; }
void set_flags(int flags) { flags_ = flags; }
bool anchor_start() { return anchor_start_; }
@@ -214,21 +208,19 @@ class Prog {
bool anchor_end() { return anchor_end_; }
void set_anchor_end(bool b) { anchor_end_ = b; }
int bytemap_range() { return bytemap_range_; }
- const uint8* bytemap() { return bytemap_; }
+ const uint8_t* bytemap() { return bytemap_; }
+
+ // Lazily computed.
+ int first_byte();
// Returns string representation of program for debugging.
string Dump();
string DumpUnanchored();
-
- // Record that at some point in the prog, the bytes in the range
- // lo-hi (inclusive) are treated as different from bytes outside the range.
- // Tracking this lets the DFA collapse commonly-treated byte ranges
- // when recording state pointers, greatly reducing its memory footprint.
- void MarkByteRange(int lo, int hi);
+ string DumpByteMap();
// Returns the set of kEmpty flags that are in effect at
// position p within context.
- static uint32 EmptyFlags(const StringPiece& context, const char* p);
+ static uint32_t EmptyFlags(const StringPiece& context, const char* p);
// Returns whether byte c is a word character: ASCII only.
// Used by the implementation of \b and \B.
@@ -237,7 +229,7 @@ class Prog {
// (the DFA has only one-byte lookahead).
// - even if the lookahead were possible, the Progs would be huge.
// This crude approximation is the same one PCRE uses.
- static bool IsWordChar(uint8 c) {
+ static bool IsWordChar(uint8_t c) {
return ('A' <= c && c <= 'Z') ||
('a' <= c && c <= 'z') ||
('0' <= c && c <= '9') ||
@@ -270,19 +262,37 @@ class Prog {
// If matches != NULL and kind == kManyMatch and there is a match,
// SearchDFA fills matches with the match IDs of the final matching state.
bool SearchDFA(const StringPiece& text, const StringPiece& context,
- Anchor anchor, MatchKind kind,
- StringPiece* match0, bool* failed,
- vector<int>* matches);
-
- // Build the entire DFA for the given match kind. FOR TESTING ONLY.
+ Anchor anchor, MatchKind kind, StringPiece* match0,
+ bool* failed, SparseSet* matches);
+
+ // The callback issued after building each DFA state with BuildEntireDFA().
+ // If next is null, then the memory budget has been exhausted and building
+ // will halt. Otherwise, the state has been built and next points to an array
+ // of bytemap_range()+1 slots holding the next states as per the bytemap and
+ // kByteEndText. The number of the state is implied by the callback sequence:
+ // the first callback is for state 0, the second callback is for state 1, ...
+ // match indicates whether the state is a matching state.
+ using DFAStateCallback = std::function<void(const int* next, bool match)>;
+
+ // Build the entire DFA for the given match kind.
// Usually the DFA is built out incrementally, as needed, which
- // avoids lots of unnecessary work. This function is useful only
- // for testing purposes. Returns number of states.
- int BuildEntireDFA(MatchKind kind);
+ // avoids lots of unnecessary work.
+ // If cb is not empty, it receives one callback per state built.
+ // Returns the number of states built.
+ // FOR TESTING OR EXPERIMENTAL PURPOSES ONLY.
+ int BuildEntireDFA(MatchKind kind, const DFAStateCallback& cb);
+
+ // Controls whether the DFA should bail out early if the NFA would be faster.
+ // FOR TESTING ONLY.
+ static void TEST_dfa_should_bail_when_slow(bool b);
- // Compute byte map.
+ // Compute bytemap.
void ComputeByteMap();
+ // Computes whether all matches must begin with the same first
+ // byte, and if so, returns that byte. If not, returns -1.
+ int ComputeFirstByte();
+
// Run peep-hole optimizer on program.
void Optimize();
@@ -329,48 +339,80 @@ class Prog {
// Returns true on success, false on error.
bool PossibleMatchRange(string* min, string* max, int maxlen);
+ // EXPERIMENTAL! SUBJECT TO CHANGE!
+ // Outputs the program fanout into the given sparse array.
+ void Fanout(SparseArray<int>* fanout);
+
// Compiles a collection of regexps to Prog. Each regexp will have
- // its own Match instruction recording the index in the vector.
- static Prog* CompileSet(const RE2::Options& options, RE2::Anchor anchor,
- Regexp* re);
+ // its own Match instruction recording the index in the output vector.
+ static Prog* CompileSet(Regexp* re, RE2::Anchor anchor, int64_t max_mem);
+
+ // Flattens the Prog from "tree" form to "list" form. This is an in-place
+ // operation in the sense that the old instructions are lost.
+ void Flatten();
+
+ // Walks the Prog; the "successor roots" or predecessors of the reachable
+ // instructions are marked in rootmap or predmap/predvec, respectively.
+ // reachable and stk are preallocated scratch structures.
+ void MarkSuccessors(SparseArray<int>* rootmap,
+ SparseArray<int>* predmap,
+ std::vector<std::vector<int>>* predvec,
+ SparseSet* reachable, std::vector<int>* stk);
+
+ // Walks the Prog from the given "root" instruction; the "dominator root"
+ // of the reachable instructions (if such exists) is marked in rootmap.
+ // reachable and stk are preallocated scratch structures.
+ void MarkDominator(int root, SparseArray<int>* rootmap,
+ SparseArray<int>* predmap,
+ std::vector<std::vector<int>>* predvec,
+ SparseSet* reachable, std::vector<int>* stk);
+
+ // Walks the Prog from the given "root" instruction; the reachable
+ // instructions are emitted in "list" form and appended to flat.
+ // reachable and stk are preallocated scratch structures.
+ void EmitList(int root, SparseArray<int>* rootmap,
+ std::vector<Inst>* flat,
+ SparseSet* reachable, std::vector<int>* stk);
private:
friend class Compiler;
DFA* GetDFA(MatchKind kind);
+ void DeleteDFA(DFA* dfa);
bool anchor_start_; // regexp has explicit start anchor
bool anchor_end_; // regexp has explicit end anchor
bool reversed_; // whether program runs backward over input
+ bool did_flatten_; // has Flatten been called?
bool did_onepass_; // has IsOnePass been called?
int start_; // entry point for program
int start_unanchored_; // unanchored entry point for program
int size_; // number of instructions
- int byte_inst_count_; // number of kInstByteRange instructions
int bytemap_range_; // bytemap_[x] < bytemap_range_
+ int first_byte_; // required first byte for match, or -1 if none
int flags_; // regexp parse flags
- int onepass_statesize_; // byte size of each OneState* node
- Inst* inst_; // pointer to instruction array
+ int list_count_; // count of lists (see above)
+ int inst_count_[kNumInst]; // count of instructions by opcode
+
+ PODArray<Inst> inst_; // pointer to instruction array
+ uint8_t* onepass_nodes_; // data for OnePass nodes
- Mutex dfa_mutex_; // Protects dfa_first_, dfa_longest_
- DFA* volatile dfa_first_; // DFA cached for kFirstMatch
- DFA* volatile dfa_longest_; // DFA cached for kLongestMatch and kFullMatch
- int64 dfa_mem_; // Maximum memory for DFAs.
- void (*delete_dfa_)(DFA* dfa);
+ int64_t dfa_mem_; // Maximum memory for DFAs.
+ DFA* dfa_first_; // DFA cached for kFirstMatch/kManyMatch
+ DFA* dfa_longest_; // DFA cached for kLongestMatch/kFullMatch
- Bitmap<256> byterange_; // byterange.Get(x) true if x ends a
- // commonly-treated byte range.
- uint8 bytemap_[256]; // map from input bytes to byte classes
- uint8 *unbytemap_; // bytemap_[unbytemap_[x]] == x
+ uint8_t bytemap_[256]; // map from input bytes to byte classes
- uint8* onepass_nodes_; // data for OnePass nodes
- OneState* onepass_start_; // start node for OnePass program
+ std::once_flag first_byte_once_;
+ std::once_flag dfa_first_once_;
+ std::once_flag dfa_longest_once_;
- DISALLOW_EVIL_CONSTRUCTORS(Prog);
+ Prog(const Prog&) = delete;
+ Prog& operator=(const Prog&) = delete;
};
} // namespace re2
-#endif // RE2_PROG_H__
+#endif // RE2_PROG_H_
diff --git a/re2/re2.cc b/re2/re2.cc
index 8d1d468..1529807 100644
--- a/re2/re2.cc
+++ b/re2/re2.cc
@@ -9,32 +9,34 @@
#include "re2/re2.h"
-#include <stdio.h>
-#include <string>
-#include <pthread.h>
+#include <assert.h>
+#include <ctype.h>
#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm>
+#include <iterator>
+#include <mutex>
+#include <string>
+#include <utility>
+#include <vector>
+
#include "util/util.h"
-#include "util/flags.h"
+#include "util/logging.h"
+#include "util/sparse_array.h"
+#include "util/strutil.h"
+#include "util/utf.h"
#include "re2/prog.h"
#include "re2/regexp.h"
-DEFINE_bool(trace_re2, false, "trace RE2 execution");
-
namespace re2 {
// Maximum number of args we can set
static const int kMaxArgs = 16;
static const int kVecSize = 1+kMaxArgs;
-const VariadicFunction2<bool, const StringPiece&, const RE2&, RE2::Arg, RE2::FullMatchN> RE2::FullMatch;
-const VariadicFunction2<bool, const StringPiece&, const RE2&, RE2::Arg, RE2::PartialMatchN> RE2::PartialMatch;
-const VariadicFunction2<bool, StringPiece*, const RE2&, RE2::Arg, RE2::ConsumeN> RE2::Consume;
-const VariadicFunction2<bool, StringPiece*, const RE2&, RE2::Arg, RE2::FindAndConsumeN> RE2::FindAndConsume;
-
-// This will trigger LNK2005 error in MSVC.
-#ifndef COMPILER_MSVC
const int RE2::Options::kDefaultMaxMem; // initialized in re2.h
-#endif // COMPILER_MSVC
RE2::Options::Options(RE2::CannedOptions opt)
: encoding_(opt == RE2::Latin1 ? EncodingLatin1 : EncodingUTF8),
@@ -44,6 +46,7 @@ RE2::Options::Options(RE2::CannedOptions opt)
max_mem_(kDefaultMaxMem),
literal_(false),
never_nl_(false),
+ dot_nl_(false),
never_capture_(false),
case_sensitive_(true),
perl_classes_(false),
@@ -51,22 +54,11 @@ RE2::Options::Options(RE2::CannedOptions opt)
one_line_(false) {
}
-// static empty things for use as const references.
-// To avoid global constructors, initialized on demand.
-GLOBAL_MUTEX(empty_mutex);
-static const string *empty_string;
-static const map<string, int> *empty_named_groups;
-static const map<int, string> *empty_group_names;
-
-static void InitEmpty() {
- GLOBAL_MUTEX_LOCK(empty_mutex);
- if (empty_string == NULL) {
- empty_string = new string;
- empty_named_groups = new map<string, int>;
- empty_group_names = new map<int, string>;
- }
- GLOBAL_MUTEX_UNLOCK(empty_mutex);
-}
+// static empty objects for use as const references.
+// To avoid global constructors, allocated in RE2::Init().
+static const string* empty_string;
+static const std::map<string, int>* empty_named_groups;
+static const std::map<int, string>* empty_group_names;
// Converts from Regexp error code to RE2 error code.
// Maybe some day they will diverge. In any event, this
@@ -107,8 +99,8 @@ static RE2::ErrorCode RegexpErrorToRE2(re2::RegexpStatusCode code) {
static string trunc(const StringPiece& pattern) {
if (pattern.size() < 100)
- return pattern.as_string();
- return pattern.substr(0, 100).as_string() + "...";
+ return string(pattern);
+ return string(pattern.substr(0, 100)) + "...";
}
@@ -151,6 +143,9 @@ int RE2::Options::ParseFlags() const {
if (never_nl())
flags |= Regexp::NeverNL;
+ if (dot_nl())
+ flags |= Regexp::DotNL;
+
if (never_capture())
flags |= Regexp::NeverCapture;
@@ -170,19 +165,24 @@ int RE2::Options::ParseFlags() const {
}
void RE2::Init(const StringPiece& pattern, const Options& options) {
- mutex_ = new Mutex;
- pattern_ = pattern.as_string();
+ static std::once_flag empty_once;
+ std::call_once(empty_once, []() {
+ empty_string = new string;
+ empty_named_groups = new std::map<string, int>;
+ empty_group_names = new std::map<int, string>;
+ });
+
+ pattern_ = string(pattern);
options_.Copy(options);
- InitEmpty();
- error_ = empty_string;
- error_code_ = NoError;
- suffix_regexp_ = NULL;
entire_regexp_ = NULL;
+ suffix_regexp_ = NULL;
prog_ = NULL;
+ num_captures_ = -1;
rprog_ = NULL;
+ error_ = empty_string;
+ error_code_ = NoError;
named_groups_ = NULL;
group_names_ = NULL;
- num_captures_ = -1;
RegexpStatus status;
entire_regexp_ = Regexp::Parse(
@@ -190,19 +190,16 @@ void RE2::Init(const StringPiece& pattern, const Options& options) {
static_cast<Regexp::ParseFlags>(options_.ParseFlags()),
&status);
if (entire_regexp_ == NULL) {
- if (error_ == empty_string)
- error_ = new string(status.Text());
if (options_.log_errors()) {
LOG(ERROR) << "Error parsing '" << trunc(pattern_) << "': "
<< status.Text();
}
- error_arg_ = status.error_arg().as_string();
+ error_ = new string(status.Text());
error_code_ = RegexpErrorToRE2(status.code());
+ error_arg_ = string(status.error_arg());
return;
}
- prefix_.clear();
- prefix_foldcase_ = false;
re2::Regexp* suffix;
if (entire_regexp_->RequiredPrefix(&prefix_, &prefix_foldcase_, &suffix))
suffix_regexp_ = suffix;
@@ -221,6 +218,11 @@ void RE2::Init(const StringPiece& pattern, const Options& options) {
return;
}
+ // We used to compute this lazily, but it's used during the
+ // typical control flow for a match call, so we now compute
+ // it eagerly, which avoids the overhead of std::once_flag.
+ num_captures_ = suffix_regexp_->NumCaptures();
+
// Could delay this until the first match call that
// cares about submatch information, but the one-pass
// machine's memory gets cut from the DFA memory budget,
@@ -231,17 +233,16 @@ void RE2::Init(const StringPiece& pattern, const Options& options) {
// Returns rprog_, computing it if needed.
re2::Prog* RE2::ReverseProg() const {
- MutexLock l(mutex_);
- if (rprog_ == NULL && error_ == empty_string) {
- rprog_ = suffix_regexp_->CompileToReverseProg(options_.max_mem()/3);
- if (rprog_ == NULL) {
- if (options_.log_errors())
- LOG(ERROR) << "Error reverse compiling '" << trunc(pattern_) << "'";
- error_ = new string("pattern too large - reverse compile failed");
- error_code_ = RE2::ErrorPatternTooLarge;
- return NULL;
+ std::call_once(rprog_once_, [](const RE2* re) {
+ re->rprog_ =
+ re->suffix_regexp_->CompileToReverseProg(re->options_.max_mem() / 3);
+ if (re->rprog_ == NULL) {
+ if (re->options_.log_errors())
+ LOG(ERROR) << "Error reverse compiling '" << trunc(re->pattern_) << "'";
+ re->error_ = new string("pattern too large - reverse compile failed");
+ re->error_code_ = RE2::ErrorPatternTooLarge;
}
- }
+ }, this);
return rprog_;
}
@@ -250,7 +251,6 @@ RE2::~RE2() {
suffix_regexp_->Decref();
if (entire_regexp_)
entire_regexp_->Decref();
- delete mutex_;
delete prog_;
delete rprog_;
if (error_ != empty_string)
@@ -267,29 +267,64 @@ int RE2::ProgramSize() const {
return prog_->size();
}
-// Returns named_groups_, computing it if needed.
-const map<string, int>& RE2::NamedCapturingGroups() const {
- MutexLock l(mutex_);
- if (!ok())
- return *empty_named_groups;
- if (named_groups_ == NULL) {
- named_groups_ = suffix_regexp_->NamedCaptures();
- if (named_groups_ == NULL)
- named_groups_ = empty_named_groups;
+int RE2::ReverseProgramSize() const {
+ if (prog_ == NULL)
+ return -1;
+ Prog* prog = ReverseProg();
+ if (prog == NULL)
+ return -1;
+ return prog->size();
+}
+
+static int Fanout(Prog* prog, std::map<int, int>* histogram) {
+ SparseArray<int> fanout(prog->size());
+ prog->Fanout(&fanout);
+ histogram->clear();
+ for (SparseArray<int>::iterator i = fanout.begin(); i != fanout.end(); ++i) {
+ // TODO(junyer): Optimise this?
+ int bucket = 0;
+ while (1 << bucket < i->value()) {
+ bucket++;
+ }
+ (*histogram)[bucket]++;
}
+ return histogram->rbegin()->first;
+}
+
+int RE2::ProgramFanout(std::map<int, int>* histogram) const {
+ if (prog_ == NULL)
+ return -1;
+ return Fanout(prog_, histogram);
+}
+
+int RE2::ReverseProgramFanout(std::map<int, int>* histogram) const {
+ if (prog_ == NULL)
+ return -1;
+ Prog* prog = ReverseProg();
+ if (prog == NULL)
+ return -1;
+ return Fanout(prog, histogram);
+}
+
+// Returns named_groups_, computing it if needed.
+const std::map<string, int>& RE2::NamedCapturingGroups() const {
+ std::call_once(named_groups_once_, [](const RE2* re) {
+ if (re->suffix_regexp_ != NULL)
+ re->named_groups_ = re->suffix_regexp_->NamedCaptures();
+ if (re->named_groups_ == NULL)
+ re->named_groups_ = empty_named_groups;
+ }, this);
return *named_groups_;
}
// Returns group_names_, computing it if needed.
-const map<int, string>& RE2::CapturingGroupNames() const {
- MutexLock l(mutex_);
- if (!ok())
- return *empty_group_names;
- if (group_names_ == NULL) {
- group_names_ = suffix_regexp_->CaptureNames();
- if (group_names_ == NULL)
- group_names_ = empty_group_names;
- }
+const std::map<int, string>& RE2::CapturingGroupNames() const {
+ std::call_once(group_names_once_, [](const RE2* re) {
+ if (re->suffix_regexp_ != NULL)
+ re->group_names_ = re->suffix_regexp_->CaptureNames();
+ if (re->group_names_ == NULL)
+ re->group_names_ = empty_group_names;
+ }, this);
return *group_names_;
}
@@ -307,7 +342,7 @@ bool RE2::PartialMatchN(const StringPiece& text, const RE2& re,
bool RE2::ConsumeN(StringPiece* input, const RE2& re,
const Arg* const args[], int n) {
- int consumed;
+ size_t consumed;
if (re.DoMatch(*input, ANCHOR_START, &consumed, args, n)) {
input->remove_prefix(consumed);
return true;
@@ -318,7 +353,7 @@ bool RE2::ConsumeN(StringPiece* input, const RE2& re,
bool RE2::FindAndConsumeN(StringPiece* input, const RE2& re,
const Arg* const args[], int n) {
- int consumed;
+ size_t consumed;
if (re.DoMatch(*input, UNANCHORED, &consumed, args, n)) {
input->remove_prefix(consumed);
return true;
@@ -327,28 +362,9 @@ bool RE2::FindAndConsumeN(StringPiece* input, const RE2& re,
}
}
-// Returns the maximum submatch needed for the rewrite to be done by Replace().
-// E.g. if rewrite == "foo \\2,\\1", returns 2.
-int RE2::MaxSubmatch(const StringPiece& rewrite) {
- int max = 0;
- for (const char *s = rewrite.data(), *end = s + rewrite.size();
- s < end; s++) {
- if (*s == '\\') {
- s++;
- int c = (s < end) ? *s : -1;
- if (isdigit(c)) {
- int n = (c - '0');
- if (n > max)
- max = n;
- }
- }
- }
- return max;
-}
-
-bool RE2::Replace(string *str,
- const RE2& re,
- const StringPiece& rewrite) {
+bool RE2::Replace(string* str,
+ const RE2& re,
+ const StringPiece& rewrite) {
StringPiece vec[kVecSize];
int nvec = 1 + MaxSubmatch(rewrite);
if (nvec > arraysize(vec))
@@ -366,9 +382,9 @@ bool RE2::Replace(string *str,
return true;
}
-int RE2::GlobalReplace(string *str,
- const RE2& re,
- const StringPiece& rewrite) {
+int RE2::GlobalReplace(string* str,
+ const RE2& re,
+ const StringPiece& rewrite) {
StringPiece vec[kVecSize];
int nvec = 1 + MaxSubmatch(rewrite);
if (nvec > arraysize(vec))
@@ -379,13 +395,44 @@ int RE2::GlobalReplace(string *str,
const char* lastend = NULL;
string out;
int count = 0;
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // Iterate just once when fuzzing. Otherwise, we easily get bogged down
+ // and coverage is unlikely to improve despite significant expense.
+ while (p == str->data()) {
+#else
while (p <= ep) {
- if (!re.Match(*str, p - str->data(), str->size(), UNANCHORED, vec, nvec))
+#endif
+ if (!re.Match(*str, static_cast<size_t>(p - str->data()),
+ str->size(), UNANCHORED, vec, nvec))
break;
if (p < vec[0].begin())
out.append(p, vec[0].begin() - p);
if (vec[0].begin() == lastend && vec[0].size() == 0) {
// Disallow empty match at end of last match: skip ahead.
+ //
+ // fullrune() takes int, not size_t. However, it just looks
+ // at the leading byte and treats any length >= 4 the same.
+ if (re.options().encoding() == RE2::Options::EncodingUTF8 &&
+ fullrune(p, static_cast<int>(std::min(static_cast<ptrdiff_t>(4),
+ ep - p)))) {
+ // re is in UTF-8 mode and there is enough left of str
+ // to allow us to advance by up to UTFmax bytes.
+ Rune r;
+ int n = chartorune(&r, p);
+ // Some copies of chartorune have a bug that accepts
+ // encodings of values in (10FFFF, 1FFFFF] as valid.
+ if (r > Runemax) {
+ n = 1;
+ r = Runeerror;
+ }
+ if (!(n == 1 && r == Runeerror)) { // no decoding error
+ out.append(p, n);
+ p += n;
+ continue;
+ }
+ }
+ // Most likely, re is in Latin-1 mode. If it is in UTF-8 mode,
+ // we fell through from above and the GIGO principle applies.
if (p < ep)
out.append(p, 1);
p++;
@@ -402,14 +449,15 @@ int RE2::GlobalReplace(string *str,
if (p < ep)
out.append(p, ep - p);
+ using std::swap;
swap(out, *str);
return count;
}
-bool RE2::Extract(const StringPiece &text,
- const RE2& re,
- const StringPiece &rewrite,
- string *out) {
+bool RE2::Extract(const StringPiece& text,
+ const RE2& re,
+ const StringPiece& rewrite,
+ string* out) {
StringPiece vec[kVecSize];
int nvec = 1 + MaxSubmatch(rewrite);
if (nvec > arraysize(vec))
@@ -433,7 +481,7 @@ string RE2::QuoteMeta(const StringPiece& unquoted) {
// that. (This also makes it identical to the perl function of the
// same name except for the null-character special case;
// see `perldoc -f quotemeta`.)
- for (int ii = 0; ii < unquoted.length(); ++ii) {
+ for (size_t ii = 0; ii < unquoted.size(); ++ii) {
// Note that using 'isalnum' here raises the benchmark time from
// 32ns to 58ns:
if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') &&
@@ -464,19 +512,19 @@ bool RE2::PossibleMatchRange(string* min, string* max, int maxlen) const {
if (prog_ == NULL)
return false;
- int n = prefix_.size();
+ int n = static_cast<int>(prefix_.size());
if (n > maxlen)
n = maxlen;
// Determine initial min max from prefix_ literal.
- string pmin, pmax;
- pmin = prefix_.substr(0, n);
- pmax = prefix_.substr(0, n);
+ *min = prefix_.substr(0, n);
+ *max = prefix_.substr(0, n);
if (prefix_foldcase_) {
- // prefix is ASCII lowercase; change pmin to uppercase.
+ // prefix is ASCII lowercase; change *min to uppercase.
for (int i = 0; i < n; i++) {
- if ('a' <= pmin[i] && pmin[i] <= 'z')
- pmin[i] += 'A' - 'a';
+ char& c = (*min)[i];
+ if ('a' <= c && c <= 'z')
+ c += 'A' - 'a';
}
}
@@ -484,13 +532,13 @@ bool RE2::PossibleMatchRange(string* min, string* max, int maxlen) const {
string dmin, dmax;
maxlen -= n;
if (maxlen > 0 && prog_->PossibleMatchRange(&dmin, &dmax, maxlen)) {
- pmin += dmin;
- pmax += dmax;
- } else if (pmax.size() > 0) {
+ min->append(dmin);
+ max->append(dmax);
+ } else if (!max->empty()) {
// prog_->PossibleMatchRange has failed us,
// but we still have useful information from prefix_.
- // Round up pmax to allow any possible suffix.
- pmax = PrefixSuccessor(pmax);
+ // Round up *max to allow any possible suffix.
+ PrefixSuccessor(max);
} else {
// Nothing useful.
*min = "";
@@ -498,19 +546,17 @@ bool RE2::PossibleMatchRange(string* min, string* max, int maxlen) const {
return false;
}
- *min = pmin;
- *max = pmax;
return true;
}
// Avoid possible locale nonsense in standard strcasecmp.
// The string a is known to be all lowercase.
-static int ascii_strcasecmp(const char* a, const char* b, int len) {
- const char *ae = a + len;
+static int ascii_strcasecmp(const char* a, const char* b, size_t len) {
+ const char* ae = a + len;
for (; a < ae; a++, b++) {
- uint8 x = *a;
- uint8 y = *b;
+ uint8_t x = *a;
+ uint8_t y = *b;
if ('A' <= y && y <= 'Z')
y += 'a' - 'A';
if (x != y)
@@ -523,20 +569,23 @@ static int ascii_strcasecmp(const char* a, const char* b, int len) {
/***** Actual matching and rewriting code *****/
bool RE2::Match(const StringPiece& text,
- int startpos,
- int endpos,
+ size_t startpos,
+ size_t endpos,
Anchor re_anchor,
StringPiece* submatch,
int nsubmatch) const {
- if (!ok() || suffix_regexp_ == NULL) {
+ if (!ok()) {
if (options_.log_errors())
LOG(ERROR) << "Invalid RE2: " << *error_;
return false;
}
- if (startpos < 0 || startpos > endpos || endpos > text.size()) {
+ if (startpos > endpos || endpos > text.size()) {
if (options_.log_errors())
- LOG(ERROR) << "RE2: invalid startpos, endpos pair.";
+ LOG(ERROR) << "RE2: invalid startpos, endpos pair. ["
+ << "startpos: " << startpos << ", "
+ << "endpos: " << endpos << ", "
+ << "text size: " << text.size() << "]";
return false;
}
@@ -569,7 +618,7 @@ bool RE2::Match(const StringPiece& text,
re_anchor = ANCHOR_START;
// Check for the required prefix, if any.
- int prefixlen = 0;
+ size_t prefixlen = 0;
if (!prefix_.empty()) {
if (startpos != 0)
return false;
@@ -605,7 +654,7 @@ bool RE2::Match(const StringPiece& text,
const int MaxBitStateProg = 500; // prog_->size() <= Max.
const int MaxBitStateVector = 256*1024; // bit vector size <= Max (bits)
bool can_bit_state = prog_->size() <= MaxBitStateProg;
- int bit_state_text_max = MaxBitStateVector / prog_->size();
+ size_t bit_state_text_max = MaxBitStateVector / prog_->size();
bool dfa_failed = false;
switch (re_anchor) {
@@ -614,24 +663,16 @@ bool RE2::Match(const StringPiece& text,
if (!prog_->SearchDFA(subtext, text, anchor, kind,
matchp, &dfa_failed, NULL)) {
if (dfa_failed) {
+ if (options_.log_errors())
+ LOG(ERROR) << "DFA out of memory: size " << prog_->size() << ", "
+ << "bytemap range " << prog_->bytemap_range() << ", "
+ << "list count " << prog_->list_count();
// Fall back to NFA below.
skipped_test = true;
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " DFA failed.";
break;
}
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " used DFA - no match.";
return false;
}
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " used DFA - match";
if (matchp == NULL) // Matched. Don't care where
return true;
// SearchDFA set match[0].end() but didn't know where the
@@ -643,26 +684,18 @@ bool RE2::Match(const StringPiece& text,
if (!prog->SearchDFA(match, text, Prog::kAnchored,
Prog::kLongestMatch, &match, &dfa_failed, NULL)) {
if (dfa_failed) {
+ if (options_.log_errors())
+ LOG(ERROR) << "DFA out of memory: size " << prog->size() << ", "
+ << "bytemap range " << prog->bytemap_range() << ", "
+ << "list count " << prog->list_count();
// Fall back to NFA below.
skipped_test = true;
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " reverse DFA failed.";
break;
}
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " DFA inconsistency.";
if (options_.log_errors())
- LOG(ERROR) << "DFA inconsistency";
+ LOG(ERROR) << "SearchDFA inconsistency";
return false;
}
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " used reverse DFA.";
break;
}
@@ -681,35 +714,24 @@ bool RE2::Match(const StringPiece& text,
// the DFA does.
if (can_one_pass && text.size() <= 4096 &&
(ncap > 1 || text.size() <= 8)) {
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " skipping DFA for OnePass.";
skipped_test = true;
break;
}
if (can_bit_state && text.size() <= bit_state_text_max && ncap > 1) {
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " skipping DFA for BitState.";
skipped_test = true;
break;
}
if (!prog_->SearchDFA(subtext, text, anchor, kind,
&match, &dfa_failed, NULL)) {
if (dfa_failed) {
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " DFA failed.";
+ if (options_.log_errors())
+ LOG(ERROR) << "DFA out of memory: size " << prog_->size() << ", "
+ << "bytemap range " << prog_->bytemap_range() << ", "
+ << "list count " << prog_->list_count();
+ // Fall back to NFA below.
skipped_test = true;
break;
}
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " used DFA - no match.";
return false;
}
break;
@@ -735,20 +757,12 @@ bool RE2::Match(const StringPiece& text,
}
if (can_one_pass && anchor != Prog::kUnanchored) {
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " using OnePass.";
if (!prog_->SearchOnePass(subtext1, text, anchor, kind, submatch, ncap)) {
if (!skipped_test && options_.log_errors())
LOG(ERROR) << "SearchOnePass inconsistency";
return false;
}
} else if (can_bit_state && subtext1.size() <= bit_state_text_max) {
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " using BitState.";
if (!prog_->SearchBitState(subtext1, text, anchor,
kind, submatch, ncap)) {
if (!skipped_test && options_.log_errors())
@@ -756,10 +770,6 @@ bool RE2::Match(const StringPiece& text,
return false;
}
} else {
- if (FLAGS_trace_re2)
- LOG(INFO) << "Match " << trunc(pattern_)
- << " [" << CEscape(subtext) << "]"
- << " using NFA.";
if (!prog_->SearchNFA(subtext1, text, anchor, kind, submatch, ncap)) {
if (!skipped_test && options_.log_errors())
LOG(ERROR) << "SearchNFA inconsistency";
@@ -770,19 +780,19 @@ bool RE2::Match(const StringPiece& text,
// Adjust overall match for required prefix that we stripped off.
if (prefixlen > 0 && nsubmatch > 0)
- submatch[0] = StringPiece(submatch[0].begin() - prefixlen,
+ submatch[0] = StringPiece(submatch[0].data() - prefixlen,
submatch[0].size() + prefixlen);
// Zero submatches that don't exist in the regexp.
for (int i = ncap; i < nsubmatch; i++)
- submatch[i] = NULL;
+ submatch[i] = StringPiece();
return true;
}
// Internal matcher - like Match() but takes Args not StringPieces.
bool RE2::DoMatch(const StringPiece& text,
- Anchor anchor,
- int* consumed,
+ Anchor re_anchor,
+ size_t* consumed,
const Arg* const* args,
int n) const {
if (!ok()) {
@@ -791,6 +801,11 @@ bool RE2::DoMatch(const StringPiece& text,
return false;
}
+ if (NumberOfCapturingGroups() < n) {
+ // RE has fewer capturing groups than number of Arg pointers passed in.
+ return false;
+ }
+
// Count number of capture groups needed.
int nvec;
if (n == 0 && consumed == NULL)
@@ -809,13 +824,13 @@ bool RE2::DoMatch(const StringPiece& text,
heapvec = vec;
}
- if (!Match(text, 0, text.size(), anchor, vec, nvec)) {
+ if (!Match(text, 0, text.size(), re_anchor, vec, nvec)) {
delete[] heapvec;
return false;
}
- if(consumed != NULL)
- *consumed = vec[0].end() - text.begin();
+ if (consumed != NULL)
+ *consumed = static_cast<size_t>(vec[0].end() - text.begin());
if (n == 0 || args == NULL) {
// We are not interested in results
@@ -823,21 +838,11 @@ bool RE2::DoMatch(const StringPiece& text,
return true;
}
- int ncap = NumberOfCapturingGroups();
- if (ncap < n) {
- // RE has fewer capturing groups than number of arg pointers passed in
- VLOG(1) << "Asked for " << n << " but only have " << ncap;
- delete[] heapvec;
- return false;
- }
-
// If we got here, we must have matched the whole pattern.
for (int i = 0; i < n; i++) {
const StringPiece& s = vec[i+1];
if (!args[i]->Parse(s.data(), s.size())) {
// TODO: Should we indicate what the error was?
- VLOG(1) << "Parse error on #" << i << " " << s << " "
- << (void*)s.data() << "/" << s.size();
delete[] heapvec;
return false;
}
@@ -847,54 +852,6 @@ bool RE2::DoMatch(const StringPiece& text,
return true;
}
-// Append the "rewrite" string, with backslash subsitutions from "vec",
-// to string "out".
-bool RE2::Rewrite(string *out, const StringPiece &rewrite,
- const StringPiece *vec, int veclen) const {
- for (const char *s = rewrite.data(), *end = s + rewrite.size();
- s < end; s++) {
- int c = *s;
- if (c == '\\') {
- s++;
- c = (s < end) ? *s : -1;
- if (isdigit(c)) {
- int n = (c - '0');
- if (n >= veclen) {
- if (options_.log_errors()) {
- LOG(ERROR) << "requested group " << n
- << " in regexp " << rewrite.data();
- }
- return false;
- }
- StringPiece snip = vec[n];
- if (snip.size() > 0)
- out->append(snip.data(), snip.size());
- } else if (c == '\\') {
- out->push_back('\\');
- } else {
- if (options_.log_errors())
- LOG(ERROR) << "invalid rewrite pattern: " << rewrite.data();
- return false;
- }
- } else {
- out->push_back(c);
- }
- }
- return true;
-}
-
-// Return the number of capturing subpatterns, or -1 if the
-// regexp wasn't valid on construction.
-int RE2::NumberOfCapturingGroups() const {
- if (suffix_regexp_ == NULL)
- return -1;
- ANNOTATE_BENIGN_RACE(&num_captures_, "benign race: in the worst case"
- " multiple threads end up doing the same work in parallel.");
- if (num_captures_ == -1)
- num_captures_ = suffix_regexp_->NumCaptures();
- return num_captures_;
-}
-
// Checks that the rewrite string is well-formed with respect to this
// regular expression.
bool RE2::CheckRewriteString(const StringPiece& rewrite, string* error) const {
@@ -933,33 +890,96 @@ bool RE2::CheckRewriteString(const StringPiece& rewrite, string* error) const {
return true;
}
+// Returns the maximum submatch needed for the rewrite to be done by Replace().
+// E.g. if rewrite == "foo \\2,\\1", returns 2.
+int RE2::MaxSubmatch(const StringPiece& rewrite) {
+ int max = 0;
+ for (const char *s = rewrite.data(), *end = s + rewrite.size();
+ s < end; s++) {
+ if (*s == '\\') {
+ s++;
+ int c = (s < end) ? *s : -1;
+ if (isdigit(c)) {
+ int n = (c - '0');
+ if (n > max)
+ max = n;
+ }
+ }
+ }
+ return max;
+}
+
+// Append the "rewrite" string, with backslash subsitutions from "vec",
+// to string "out".
+bool RE2::Rewrite(string* out,
+ const StringPiece& rewrite,
+ const StringPiece* vec,
+ int veclen) const {
+ for (const char *s = rewrite.data(), *end = s + rewrite.size();
+ s < end; s++) {
+ if (*s != '\\') {
+ out->push_back(*s);
+ continue;
+ }
+ s++;
+ int c = (s < end) ? *s : -1;
+ if (isdigit(c)) {
+ int n = (c - '0');
+ if (n >= veclen) {
+ if (options_.log_errors()) {
+ LOG(ERROR) << "requested group " << n
+ << " in regexp " << rewrite.data();
+ }
+ return false;
+ }
+ StringPiece snip = vec[n];
+ if (snip.size() > 0)
+ out->append(snip.data(), snip.size());
+ } else if (c == '\\') {
+ out->push_back('\\');
+ } else {
+ if (options_.log_errors())
+ LOG(ERROR) << "invalid rewrite pattern: " << rewrite.data();
+ return false;
+ }
+ }
+ return true;
+}
+
/***** Parsers for various types *****/
-bool RE2::Arg::parse_null(const char* str, int n, void* dest) {
+bool RE2::Arg::parse_null(const char* str, size_t n, void* dest) {
// We fail if somebody asked us to store into a non-NULL void* pointer
return (dest == NULL);
}
-bool RE2::Arg::parse_string(const char* str, int n, void* dest) {
+bool RE2::Arg::parse_string(const char* str, size_t n, void* dest) {
if (dest == NULL) return true;
reinterpret_cast<string*>(dest)->assign(str, n);
return true;
}
-bool RE2::Arg::parse_stringpiece(const char* str, int n, void* dest) {
+bool RE2::Arg::parse_stringpiece(const char* str, size_t n, void* dest) {
if (dest == NULL) return true;
- reinterpret_cast<StringPiece*>(dest)->set(str, n);
+ *(reinterpret_cast<StringPiece*>(dest)) = StringPiece(str, n);
return true;
}
-bool RE2::Arg::parse_char(const char* str, int n, void* dest) {
+bool RE2::Arg::parse_char(const char* str, size_t n, void* dest) {
if (n != 1) return false;
if (dest == NULL) return true;
*(reinterpret_cast<char*>(dest)) = str[0];
return true;
}
-bool RE2::Arg::parse_uchar(const char* str, int n, void* dest) {
+bool RE2::Arg::parse_schar(const char* str, size_t n, void* dest) {
+ if (n != 1) return false;
+ if (dest == NULL) return true;
+ *(reinterpret_cast<signed char*>(dest)) = str[0];
+ return true;
+}
+
+bool RE2::Arg::parse_uchar(const char* str, size_t n, void* dest) {
if (n != 1) return false;
if (dest == NULL) return true;
*(reinterpret_cast<unsigned char*>(dest)) = str[0];
@@ -969,16 +989,23 @@ bool RE2::Arg::parse_uchar(const char* str, int n, void* dest) {
// Largest number spec that we are willing to parse
static const int kMaxNumberLength = 32;
-// REQUIRES "buf" must have length at least kMaxNumberLength+1
+// REQUIRES "buf" must have length at least nbuf.
// Copies "str" into "buf" and null-terminates.
// Overwrites *np with the new length.
-static const char* TerminateNumber(char* buf, const char* str, int* np) {
- int n = *np;
- if (n <= 0) return "";
+static const char* TerminateNumber(char* buf, size_t nbuf, const char* str,
+ size_t* np, bool accept_spaces) {
+ size_t n = *np;
+ if (n == 0) return "";
if (n > 0 && isspace(*str)) {
// We are less forgiving than the strtoxxx() routines and do not
- // allow leading spaces.
- return "";
+ // allow leading spaces. We do allow leading spaces for floats.
+ if (!accept_spaces) {
+ return "";
+ }
+ while (n > 0 && isspace(*str)) {
+ n--;
+ str++;
+ }
}
// Although buf has a fixed maximum size, we can still handle
@@ -1008,7 +1035,7 @@ static const char* TerminateNumber(char* buf, const char* str, int* np) {
str--;
}
- if (n > kMaxNumberLength) return "";
+ if (n > nbuf-1) return "";
memmove(buf, str, n);
if (neg) {
@@ -1020,12 +1047,12 @@ static const char* TerminateNumber(char* buf, const char* str, int* np) {
}
bool RE2::Arg::parse_long_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
if (n == 0) return false;
char buf[kMaxNumberLength+1];
- str = TerminateNumber(buf, str, &n);
+ str = TerminateNumber(buf, sizeof buf, str, &n, false);
char* end;
errno = 0;
long r = strtol(str, &end, radix);
@@ -1037,16 +1064,16 @@ bool RE2::Arg::parse_long_radix(const char* str,
}
bool RE2::Arg::parse_ulong_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
if (n == 0) return false;
char buf[kMaxNumberLength+1];
- str = TerminateNumber(buf, str, &n);
+ str = TerminateNumber(buf, sizeof buf, str, &n, false);
if (str[0] == '-') {
- // strtoul() will silently accept negative numbers and parse
- // them. This module is more strict and treats them as errors.
- return false;
+ // strtoul() will silently accept negative numbers and parse
+ // them. This module is more strict and treats them as errors.
+ return false;
}
char* end;
@@ -1060,77 +1087,77 @@ bool RE2::Arg::parse_ulong_radix(const char* str,
}
bool RE2::Arg::parse_short_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
long r;
- if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
- if ((short)r != r) return false; // Out of range
+ if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
+ if ((short)r != r) return false; // Out of range
if (dest == NULL) return true;
- *(reinterpret_cast<short*>(dest)) = r;
+ *(reinterpret_cast<short*>(dest)) = (short)r;
return true;
}
bool RE2::Arg::parse_ushort_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
unsigned long r;
- if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
- if ((ushort)r != r) return false; // Out of range
+ if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
+ if ((unsigned short)r != r) return false; // Out of range
if (dest == NULL) return true;
- *(reinterpret_cast<unsigned short*>(dest)) = r;
+ *(reinterpret_cast<unsigned short*>(dest)) = (unsigned short)r;
return true;
}
bool RE2::Arg::parse_int_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
long r;
- if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
- if ((int)r != r) return false; // Out of range
+ if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
+ if ((int)r != r) return false; // Out of range
if (dest == NULL) return true;
- *(reinterpret_cast<int*>(dest)) = r;
+ *(reinterpret_cast<int*>(dest)) = (int)r;
return true;
}
bool RE2::Arg::parse_uint_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
unsigned long r;
- if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
- if ((uint)r != r) return false; // Out of range
+ if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
+ if ((unsigned int)r != r) return false; // Out of range
if (dest == NULL) return true;
- *(reinterpret_cast<unsigned int*>(dest)) = r;
+ *(reinterpret_cast<unsigned int*>(dest)) = (unsigned int)r;
return true;
}
bool RE2::Arg::parse_longlong_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
if (n == 0) return false;
char buf[kMaxNumberLength+1];
- str = TerminateNumber(buf, str, &n);
+ str = TerminateNumber(buf, sizeof buf, str, &n, false);
char* end;
errno = 0;
- int64 r = strtoll(str, &end, radix);
+ long long r = strtoll(str, &end, radix);
if (end != str + n) return false; // Leftover junk
if (errno) return false;
if (dest == NULL) return true;
- *(reinterpret_cast<int64*>(dest)) = r;
+ *(reinterpret_cast<long long*>(dest)) = r;
return true;
}
bool RE2::Arg::parse_ulonglong_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
if (n == 0) return false;
char buf[kMaxNumberLength+1];
- str = TerminateNumber(buf, str, &n);
+ str = TerminateNumber(buf, sizeof buf, str, &n, false);
if (str[0] == '-') {
// strtoull() will silently accept negative numbers and parse
// them. This module is more strict and treats them as errors.
@@ -1138,72 +1165,71 @@ bool RE2::Arg::parse_ulonglong_radix(const char* str,
}
char* end;
errno = 0;
- uint64 r = strtoull(str, &end, radix);
+ unsigned long long r = strtoull(str, &end, radix);
if (end != str + n) return false; // Leftover junk
if (errno) return false;
if (dest == NULL) return true;
- *(reinterpret_cast<uint64*>(dest)) = r;
+ *(reinterpret_cast<unsigned long long*>(dest)) = r;
return true;
}
-static bool parse_double_float(const char* str, int n, bool isfloat, void *dest) {
+static bool parse_double_float(const char* str, size_t n, bool isfloat,
+ void* dest) {
if (n == 0) return false;
static const int kMaxLength = 200;
- char buf[kMaxLength];
- if (n >= kMaxLength) return false;
- memcpy(buf, str, n);
- buf[n] = '\0';
- errno = 0;
+ char buf[kMaxLength+1];
+ str = TerminateNumber(buf, sizeof buf, str, &n, true);
char* end;
+ errno = 0;
double r;
if (isfloat) {
- r = strtof(buf, &end);
+ r = strtof(str, &end);
} else {
- r = strtod(buf, &end);
+ r = strtod(str, &end);
}
- if (end != buf + n) return false; // Leftover junk
+ if (end != str + n) return false; // Leftover junk
if (errno) return false;
if (dest == NULL) return true;
if (isfloat) {
- *(reinterpret_cast<float*>(dest)) = r;
+ *(reinterpret_cast<float*>(dest)) = (float)r;
} else {
*(reinterpret_cast<double*>(dest)) = r;
}
return true;
}
-bool RE2::Arg::parse_double(const char* str, int n, void* dest) {
+bool RE2::Arg::parse_double(const char* str, size_t n, void* dest) {
return parse_double_float(str, n, false, dest);
}
-bool RE2::Arg::parse_float(const char* str, int n, void* dest) {
+bool RE2::Arg::parse_float(const char* str, size_t n, void* dest) {
return parse_double_float(str, n, true, dest);
}
-
-#define DEFINE_INTEGER_PARSERS(name) \
- bool RE2::Arg::parse_##name(const char* str, int n, void* dest) { \
- return parse_##name##_radix(str, n, dest, 10); \
- } \
- bool RE2::Arg::parse_##name##_hex(const char* str, int n, void* dest) { \
- return parse_##name##_radix(str, n, dest, 16); \
- } \
- bool RE2::Arg::parse_##name##_octal(const char* str, int n, void* dest) { \
- return parse_##name##_radix(str, n, dest, 8); \
- } \
- bool RE2::Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \
- return parse_##name##_radix(str, n, dest, 0); \
+#define DEFINE_INTEGER_PARSER(name) \
+ bool RE2::Arg::parse_##name(const char* str, size_t n, void* dest) { \
+ return parse_##name##_radix(str, n, dest, 10); \
+ } \
+ bool RE2::Arg::parse_##name##_hex(const char* str, size_t n, void* dest) { \
+ return parse_##name##_radix(str, n, dest, 16); \
+ } \
+ bool RE2::Arg::parse_##name##_octal(const char* str, size_t n, void* dest) { \
+ return parse_##name##_radix(str, n, dest, 8); \
+ } \
+ bool RE2::Arg::parse_##name##_cradix(const char* str, size_t n, \
+ void* dest) { \
+ return parse_##name##_radix(str, n, dest, 0); \
}
-DEFINE_INTEGER_PARSERS(short);
-DEFINE_INTEGER_PARSERS(ushort);
-DEFINE_INTEGER_PARSERS(int);
-DEFINE_INTEGER_PARSERS(uint);
-DEFINE_INTEGER_PARSERS(long);
-DEFINE_INTEGER_PARSERS(ulong);
-DEFINE_INTEGER_PARSERS(longlong);
-DEFINE_INTEGER_PARSERS(ulonglong);
+DEFINE_INTEGER_PARSER(short);
+DEFINE_INTEGER_PARSER(ushort);
+DEFINE_INTEGER_PARSER(int);
+DEFINE_INTEGER_PARSER(uint);
+DEFINE_INTEGER_PARSER(long);
+DEFINE_INTEGER_PARSER(ulong);
+DEFINE_INTEGER_PARSER(longlong);
+DEFINE_INTEGER_PARSER(ulonglong);
-#undef DEFINE_INTEGER_PARSERS
+#undef DEFINE_INTEGER_PARSER
} // namespace re2
diff --git a/re2/re2.h b/re2/re2.h
index 272028b..216347d 100644
--- a/re2/re2.h
+++ b/re2/re2.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#ifndef RE2_RE2_H
-#define RE2_RE2_H
+#ifndef RE2_RE2_H_
+#define RE2_RE2_H_
// C++ interface to the re2 regular-expression library.
// RE2 supports Perl-style regular expressions (with extensions like
@@ -17,7 +17,7 @@
// some of the more complicated things thrown away. In particular,
// backreferences and generalized assertions are not available, nor is \Z.
//
-// See http://code.google.com/p/re2/wiki/Syntax for the syntax
+// See https://github.com/google/re2/wiki/Syntax for the syntax
// supported by RE2, and a comparison with PCRE and PERL regexps.
//
// For those not familiar with Perl's regular expressions,
@@ -53,9 +53,19 @@
// CHECK(RE2::FullMatch(latin1_string, RE2(latin1_pattern, RE2::Latin1)));
//
// -----------------------------------------------------------------------
-// MATCHING WITH SUB-STRING EXTRACTION:
-//
-// You can supply extra pointer arguments to extract matched subpieces.
+// MATCHING WITH SUBSTRING EXTRACTION:
+//
+// You can supply extra pointer arguments to extract matched substrings.
+// On match failure, none of the pointees will have been modified.
+// On match success, the substrings will be converted (as necessary) and
+// their values will be assigned to their pointees until all conversions
+// have succeeded or one conversion has failed.
+// On conversion failure, the pointees will be in an indeterminate state
+// because the caller has no way of knowing which conversion failed.
+// However, conversion cannot fail for types like string and StringPiece
+// that do not inspect the substring contents. Hence, in the common case
+// where all of the pointees are of such types, failure is always due to
+// match failure and thus none of the pointees will have been modified.
//
// Example: extracts "ruby" into "s" and 1234 into "i"
// int i;
@@ -65,7 +75,7 @@
// Example: fails because string cannot be stored in integer
// CHECK(!RE2::FullMatch("ruby", "(.*)", &i));
//
-// Example: fails because there aren't enough sub-patterns:
+// Example: fails because there aren't enough sub-patterns
// CHECK(!RE2::FullMatch("ruby:1234", "\\w+:\\d+", &s));
//
// Example: does not try to extract any extra sub-patterns
@@ -179,35 +189,24 @@
// RE2::Octal(&a), RE2::Hex(&b), RE2::CRadix(&c), RE2::CRadix(&d));
// will leave 64 in a, b, c, and d.
-
+#include <stddef.h>
#include <stdint.h>
+#include <algorithm>
#include <map>
+#include <mutex>
#include <string>
+
#include "re2/stringpiece.h"
-#include "re2/variadic_function.h"
namespace re2 {
-
-using std::string;
-using std::map;
-class Mutex;
class Prog;
class Regexp;
+} // namespace re2
+
+namespace re2 {
-// The following enum should be used only as a constructor argument to indicate
-// that the variable has static storage class, and that the constructor should
-// do nothing to its state. It indicates to the reader that it is legal to
-// declare a static instance of the class, provided the constructor is given
-// the LINKER_INITIALIZED argument. Normally, it is unsafe to declare a
-// static variable that has a constructor or a destructor because invocation
-// order is undefined. However, IF the type can be initialized by filling with
-// zeroes (which the loader does for static variables), AND the type's
-// destructor does nothing to the storage, then a constructor for static
-// initialization can be declared as
-// explicit MyClass(LinkerInitialized x) {}
-// and invoked as
-// static MyClass my_variable_name(LINKER_INITIALIZED);
-enum LinkerInitialized { LINKER_INITIALIZED };
+// TODO(junyer): Get rid of this.
+using std::string;
// Interface for regular expression matching. Also corresponds to a
// pre-compiled regular expression. An "RE2" object is safe for
@@ -240,7 +239,7 @@ class RE2 {
ErrorBadPerlOp, // bad perl operator
ErrorBadUTF8, // invalid UTF-8 in regexp
ErrorBadNamedCapture, // bad named capture group
- ErrorPatternTooLarge, // pattern too large (compile failed)
+ ErrorPatternTooLarge // pattern too large (compile failed)
};
// Predefined common options.
@@ -263,7 +262,7 @@ class RE2 {
RE2(const string& pattern);
#endif
RE2(const StringPiece& pattern);
- RE2(const StringPiece& pattern, const Options& option);
+ RE2(const StringPiece& pattern, const Options& options);
~RE2();
// Returns whether RE2 was created properly.
@@ -289,29 +288,71 @@ class RE2 {
// Returns the program size, a very approximate measure of a regexp's "cost".
// Larger numbers are more expensive than smaller numbers.
int ProgramSize() const;
+ int ReverseProgramSize() const;
+
+ // EXPERIMENTAL! SUBJECT TO CHANGE!
+ // Outputs the program fanout as a histogram bucketed by powers of 2.
+ // Returns the number of the largest non-empty bucket.
+ int ProgramFanout(std::map<int, int>* histogram) const;
+ int ReverseProgramFanout(std::map<int, int>* histogram) const;
// Returns the underlying Regexp; not for general use.
// Returns entire_regexp_ so that callers don't need
// to know about prefix_ and prefix_foldcase_.
re2::Regexp* Regexp() const { return entire_regexp_; }
+ /***** The array-based matching interface ******/
+
+ // The functions here have names ending in 'N' and are used to implement
+ // the functions whose names are the prefix before the 'N'. It is sometimes
+ // useful to invoke them directly, but the syntax is awkward, so the 'N'-less
+ // versions should be preferred.
+ static bool FullMatchN(const StringPiece& text, const RE2& re,
+ const Arg* const args[], int argc);
+ static bool PartialMatchN(const StringPiece& text, const RE2& re,
+ const Arg* const args[], int argc);
+ static bool ConsumeN(StringPiece* input, const RE2& re,
+ const Arg* const args[], int argc);
+ static bool FindAndConsumeN(StringPiece* input, const RE2& re,
+ const Arg* const args[], int argc);
+
+#ifndef SWIG
+ private:
+ template <typename F, typename SP>
+ static inline bool Apply(F f, SP sp, const RE2& re) {
+ return f(sp, re, NULL, 0);
+ }
+
+ template <typename F, typename SP, typename... A>
+ static inline bool Apply(F f, SP sp, const RE2& re, const A&... a) {
+ const Arg* const args[] = {&a...};
+ const int argc = sizeof...(a);
+ return f(sp, re, args, argc);
+ }
+
+ public:
+ // In order to allow FullMatch() et al. to be called with a varying number
+ // of arguments of varying types, we use two layers of variadic templates.
+ // The first layer constructs the temporary Arg objects. The second layer
+ // (above) constructs the array of pointers to the temporary Arg objects.
+
/***** The useful part: the matching interface *****/
- // Matches "text" against "pattern". If pointer arguments are
+ // Matches "text" against "re". If pointer arguments are
// supplied, copies matched sub-patterns into them.
//
// You can pass in a "const char*" or a "string" for "text".
- // You can pass in a "const char*" or a "string" or a "RE2" for "pattern".
+ // You can pass in a "const char*" or a "string" or a "RE2" for "re".
//
// The provided pointer arguments can be pointers to any scalar numeric
// type, or one of:
// string (matched piece is copied to string)
// StringPiece (StringPiece is mutated to point to matched piece)
- // T (where "bool T::ParseFrom(const char*, int)" exists)
+ // T (where "bool T::ParseFrom(const char*, size_t)" exists)
// (void*)NULL (the corresponding matched sub-pattern is not copied)
//
// Returns true iff all of the following conditions are satisfied:
- // a. "text" matches "pattern" exactly
+ // a. "text" matches "re" exactly
// b. The number of matched sub-patterns is >= number of supplied pointers
// c. The "i"th argument has a suitable type for holding the
// string captured as the "i"th sub-pattern. If you pass in
@@ -325,36 +366,37 @@ class RE2 {
// valid number):
// int number;
// RE2::FullMatch("abc", "[a-z]+(\\d+)?", &number);
- static bool FullMatchN(const StringPiece& text, const RE2& re,
- const Arg* const args[], int argc);
- static const VariadicFunction2<
- bool, const StringPiece&, const RE2&, Arg, RE2::FullMatchN> FullMatch;
+ template <typename... A>
+ static bool FullMatch(const StringPiece& text, const RE2& re, A&&... a) {
+ return Apply(FullMatchN, text, re, Arg(std::forward<A>(a))...);
+ }
- // Exactly like FullMatch(), except that "pattern" is allowed to match
+ // Exactly like FullMatch(), except that "re" is allowed to match
// a substring of "text".
- static bool PartialMatchN(const StringPiece& text, const RE2& re, // 3..16 args
- const Arg* const args[], int argc);
- static const VariadicFunction2<
- bool, const StringPiece&, const RE2&, Arg, RE2::PartialMatchN> PartialMatch;
+ template <typename... A>
+ static bool PartialMatch(const StringPiece& text, const RE2& re, A&&... a) {
+ return Apply(PartialMatchN, text, re, Arg(std::forward<A>(a))...);
+ }
- // Like FullMatch() and PartialMatch(), except that pattern has to
- // match a prefix of "text", and "input" is advanced past the matched
+ // Like FullMatch() and PartialMatch(), except that "re" has to match
+ // a prefix of the text, and "input" is advanced past the matched
// text. Note: "input" is modified iff this routine returns true.
- static bool ConsumeN(StringPiece* input, const RE2& pattern, // 3..16 args
- const Arg* const args[], int argc);
- static const VariadicFunction2<
- bool, StringPiece*, const RE2&, Arg, RE2::ConsumeN> Consume;
-
- // Like Consume(..), but does not anchor the match at the beginning of the
- // string. That is, "pattern" need not start its match at the beginning of
- // "input". For example, "FindAndConsume(s, "(\\w+)", &word)" finds the next
- // word in "s" and stores it in "word".
- static bool FindAndConsumeN(StringPiece* input, const RE2& pattern,
- const Arg* const args[], int argc);
- static const VariadicFunction2<
- bool, StringPiece*, const RE2&, Arg, RE2::FindAndConsumeN> FindAndConsume;
-
- // Replace the first match of "pattern" in "str" with "rewrite".
+ template <typename... A>
+ static bool Consume(StringPiece* input, const RE2& re, A&&... a) {
+ return Apply(ConsumeN, input, re, Arg(std::forward<A>(a))...);
+ }
+
+ // Like Consume(), but does not anchor the match at the beginning of
+ // the text. That is, "re" need not start its match at the beginning
+ // of "input". For example, "FindAndConsume(s, "(\\w+)", &word)" finds
+ // the next word in "s" and stores it in "word".
+ template <typename... A>
+ static bool FindAndConsume(StringPiece* input, const RE2& re, A&&... a) {
+ return Apply(FindAndConsumeN, input, re, Arg(std::forward<A>(a))...);
+ }
+#endif
+
+ // Replace the first match of "re" in "str" with "rewrite".
// Within "rewrite", backslash-escaped digits (\1 to \9) can be
// used to insert text matching corresponding parenthesized group
// from the pattern. \0 in "rewrite" refers to the entire matching
@@ -367,8 +409,8 @@ class RE2 {
//
// Returns true if the pattern matches and a replacement occurs,
// false otherwise.
- static bool Replace(string *str,
- const RE2& pattern,
+ static bool Replace(string* str,
+ const RE2& re,
const StringPiece& rewrite);
// Like Replace(), except replaces successive non-overlapping occurrences
@@ -384,8 +426,8 @@ class RE2 {
// replacing "ana" within "banana" makes only one replacement, not two.
//
// Returns the number of replacements made.
- static int GlobalReplace(string *str,
- const RE2& pattern,
+ static int GlobalReplace(string* str,
+ const RE2& re,
const StringPiece& rewrite);
// Like Replace, except that if the pattern matches, "rewrite"
@@ -394,10 +436,12 @@ class RE2 {
//
// Returns true iff a match occurred and the extraction happened
// successfully; if no match occurs, the string is left unaffected.
- static bool Extract(const StringPiece &text,
- const RE2& pattern,
- const StringPiece &rewrite,
- string *out);
+ //
+ // REQUIRES: "text" must not alias any part of "*out".
+ static bool Extract(const StringPiece& text,
+ const RE2& re,
+ const StringPiece& rewrite,
+ string* out);
// Escapes all potentially meaningful regexp characters in
// 'unquoted'. The returned string, used as a regular expression,
@@ -429,52 +473,52 @@ class RE2 {
enum Anchor {
UNANCHORED, // No anchoring
ANCHOR_START, // Anchor at start only
- ANCHOR_BOTH, // Anchor at start and end
+ ANCHOR_BOTH // Anchor at start and end
};
// Return the number of capturing subpatterns, or -1 if the
// regexp wasn't valid on construction. The overall match ($0)
// does not count: if the regexp is "(a)(b)", returns 2.
- int NumberOfCapturingGroups() const;
-
+ int NumberOfCapturingGroups() const { return num_captures_; }
// Return a map from names to capturing indices.
// The map records the index of the leftmost group
// with the given name.
// Only valid until the re is deleted.
- const map<string, int>& NamedCapturingGroups() const;
+ const std::map<string, int>& NamedCapturingGroups() const;
// Return a map from capturing indices to names.
// The map has no entries for unnamed groups.
// Only valid until the re is deleted.
- const map<int, string>& CapturingGroupNames() const;
+ const std::map<int, string>& CapturingGroupNames() const;
// General matching routine.
// Match against text starting at offset startpos
// and stopping the search at offset endpos.
// Returns true if match found, false if not.
- // On a successful match, fills in match[] (up to nmatch entries)
+ // On a successful match, fills in submatch[] (up to nsubmatch entries)
// with information about submatches.
- // I.e. matching RE2("(foo)|(bar)baz") on "barbazbla" will return true,
- // setting match[0] = "barbaz", match[1] = NULL, match[2] = "bar",
- // match[3] = NULL, ..., up to match[nmatch-1] = NULL.
+ // I.e. matching RE2("(foo)|(bar)baz") on "barbazbla" will return true, with
+ // submatch[0] = "barbaz", submatch[1].data() = NULL, submatch[2] = "bar",
+ // submatch[3].data() = NULL, ..., up to submatch[nsubmatch-1].data() = NULL.
+ // Caveat: submatch[] may be clobbered even on match failure.
//
// Don't ask for more match information than you will use:
- // runs much faster with nmatch == 1 than nmatch > 1, and
- // runs even faster if nmatch == 0.
- // Doesn't make sense to use nmatch > 1 + NumberOfCapturingGroups(),
+ // runs much faster with nsubmatch == 1 than nsubmatch > 1, and
+ // runs even faster if nsubmatch == 0.
+ // Doesn't make sense to use nsubmatch > 1 + NumberOfCapturingGroups(),
// but will be handled correctly.
//
// Passing text == StringPiece(NULL, 0) will be handled like any other
// empty string, but note that on return, it will not be possible to tell
// whether submatch i matched the empty string or did not match:
- // either way, match[i] == NULL.
+ // either way, submatch[i].data() == NULL.
bool Match(const StringPiece& text,
- int startpos,
- int endpos,
- Anchor anchor,
- StringPiece *match,
- int nmatch) const;
+ size_t startpos,
+ size_t endpos,
+ Anchor re_anchor,
+ StringPiece* submatch,
+ int nsubmatch) const;
// Check that the given rewrite string is suitable for use with this
// regular expression. It checks that:
@@ -495,8 +539,8 @@ class RE2 {
// Returns true on success. This method can fail because of a malformed
// rewrite string. CheckRewriteString guarantees that the rewrite will
// be sucessful.
- bool Rewrite(string *out,
- const StringPiece &rewrite,
+ bool Rewrite(string* out,
+ const StringPiece& rewrite,
const StringPiece* vec,
int veclen) const;
@@ -512,13 +556,15 @@ class RE2 {
// max_mem (see below) approx. max memory footprint of RE2
// literal (false) interpret string as literal, not regexp
// never_nl (false) never match \n, even if it is in regexp
+ // dot_nl (false) dot matches everything including new line
// never_capture (false) parse all parens as non-capturing
// case_sensitive (true) match is case-sensitive (regexp can override
// with (?i) unless in posix_syntax mode)
//
// The following options are only consulted when posix_syntax == true.
- // (When posix_syntax == false these features are always enabled and
- // cannot be turned off.)
+ // When posix_syntax == false, these features are always enabled and
+ // cannot be turned off; to perform multi-line matching in that case,
+ // begin the regexp with (?m).
// perl_classes (false) allow Perl's \d \s \w \D \S \W
// word_boundary (false) allow Perl's \b \B (word boundary and not)
// one_line (false) ^ and $ only match beginning and end of text
@@ -534,7 +580,7 @@ class RE2 {
// can have two DFAs (one first match, one longest match).
// That makes 4 DFAs:
//
- // forward, first-match - used for UNANCHORED or ANCHOR_LEFT searches
+ // forward, first-match - used for UNANCHORED or ANCHOR_START searches
// if opt.longest_match() == false
// forward, longest-match - used for all ANCHOR_BOTH searches,
// and the other two kinds if
@@ -567,13 +613,14 @@ class RE2 {
max_mem_(kDefaultMaxMem),
literal_(false),
never_nl_(false),
+ dot_nl_(false),
never_capture_(false),
case_sensitive_(true),
perl_classes_(false),
word_boundary_(false),
one_line_(false) {
}
-
+
/*implicit*/ Options(CannedOptions);
Encoding encoding() const { return encoding_; }
@@ -599,8 +646,8 @@ class RE2 {
bool log_errors() const { return log_errors_; }
void set_log_errors(bool b) { log_errors_ = b; }
- int max_mem() const { return max_mem_; }
- void set_max_mem(int m) { max_mem_ = m; }
+ int64_t max_mem() const { return max_mem_; }
+ void set_max_mem(int64_t m) { max_mem_ = m; }
bool literal() const { return literal_; }
void set_literal(bool b) { literal_ = b; }
@@ -608,6 +655,9 @@ class RE2 {
bool never_nl() const { return never_nl_; }
void set_never_nl(bool b) { never_nl_ = b; }
+ bool dot_nl() const { return dot_nl_; }
+ void set_dot_nl(bool b) { dot_nl_ = b; }
+
bool never_capture() const { return never_capture_; }
void set_never_capture(bool b) { never_capture_ = b; }
@@ -624,18 +674,7 @@ class RE2 {
void set_one_line(bool b) { one_line_ = b; }
void Copy(const Options& src) {
- encoding_ = src.encoding_;
- posix_syntax_ = src.posix_syntax_;
- longest_match_ = src.longest_match_;
- log_errors_ = src.log_errors_;
- max_mem_ = src.max_mem_;
- literal_ = src.literal_;
- never_nl_ = src.never_nl_;
- never_capture_ = src.never_capture_;
- case_sensitive_ = src.case_sensitive_;
- perl_classes_ = src.perl_classes_;
- word_boundary_ = src.word_boundary_;
- one_line_ = src.one_line_;
+ *this = src;
}
int ParseFlags() const;
@@ -648,19 +687,16 @@ class RE2 {
int64_t max_mem_;
bool literal_;
bool never_nl_;
+ bool dot_nl_;
bool never_capture_;
bool case_sensitive_;
bool perl_classes_;
bool word_boundary_;
bool one_line_;
-
- //DISALLOW_EVIL_CONSTRUCTORS(Options);
- Options(const Options&);
- void operator=(const Options&);
};
// Returns the options set in the constructor.
- const Options& options() const { return options_; };
+ const Options& options() const { return options_; }
// Argument converters; see below.
static inline Arg CRadix(short* x);
@@ -694,38 +730,42 @@ class RE2 {
void Init(const StringPiece& pattern, const Options& options);
bool DoMatch(const StringPiece& text,
- Anchor anchor,
- int* consumed,
- const Arg* const args[],
- int n) const;
+ Anchor re_anchor,
+ size_t* consumed,
+ const Arg* const args[],
+ int n) const;
re2::Prog* ReverseProg() const;
- mutable Mutex* mutex_;
- string pattern_; // string regular expression
- Options options_; // option flags
+ string pattern_; // string regular expression
+ Options options_; // option flags
string prefix_; // required prefix (before regexp_)
bool prefix_foldcase_; // prefix is ASCII case-insensitive
re2::Regexp* entire_regexp_; // parsed regular expression
re2::Regexp* suffix_regexp_; // parsed regular expression, prefix removed
re2::Prog* prog_; // compiled program for regexp
- mutable re2::Prog* rprog_; // reverse program for regexp
- bool is_one_pass_; // can use prog_->SearchOnePass?
- mutable const string* error_; // Error indicator
- // (or points to empty string)
- mutable ErrorCode error_code_; // Error code
- mutable string error_arg_; // Fragment of regexp showing error
- mutable int num_captures_; // Number of capturing groups
+ int num_captures_; // Number of capturing groups
+ bool is_one_pass_; // can use prog_->SearchOnePass?
+
+ mutable re2::Prog* rprog_; // reverse program for regexp
+ mutable const string* error_; // Error indicator
+ // (or points to empty string)
+ mutable ErrorCode error_code_; // Error code
+ mutable string error_arg_; // Fragment of regexp showing error
// Map from capture names to indices
- mutable const map<string, int>* named_groups_;
+ mutable const std::map<string, int>* named_groups_;
// Map from capture indices to names
- mutable const map<int, string>* group_names_;
+ mutable const std::map<int, string>* group_names_;
- //DISALLOW_EVIL_CONSTRUCTORS(RE2);
- RE2(const RE2&);
- void operator=(const RE2&);
+ // Onces for lazy computations.
+ mutable std::once_flag rprog_once_;
+ mutable std::once_flag named_groups_once_;
+ mutable std::once_flag group_names_once_;
+
+ RE2(const RE2&) = delete;
+ RE2& operator=(const RE2&) = delete;
};
/***** Implementation details *****/
@@ -736,7 +776,7 @@ class RE2 {
template <class T>
class _RE2_MatchObject {
public:
- static inline bool Parse(const char* str, int n, void* dest) {
+ static inline bool Parse(const char* str, size_t n, void* dest) {
if (dest == NULL) return true;
T* object = reinterpret_cast<T*>(dest);
return object->ParseFrom(str, n);
@@ -750,106 +790,170 @@ class RE2::Arg {
// Constructor specially designed for NULL arguments
Arg(void*);
+ Arg(std::nullptr_t);
- typedef bool (*Parser)(const char* str, int n, void* dest);
+ typedef bool (*Parser)(const char* str, size_t n, void* dest);
// Type-specific parsers
-#define MAKE_PARSER(type,name) \
- Arg(type* p) : arg_(p), parser_(name) { } \
- Arg(type* p, Parser parser) : arg_(p), parser_(parser) { } \
-
-
- MAKE_PARSER(char, parse_char);
- MAKE_PARSER(signed char, parse_char);
- MAKE_PARSER(unsigned char, parse_uchar);
- MAKE_PARSER(short, parse_short);
- MAKE_PARSER(unsigned short, parse_ushort);
- MAKE_PARSER(int, parse_int);
- MAKE_PARSER(unsigned int, parse_uint);
- MAKE_PARSER(long, parse_long);
- MAKE_PARSER(unsigned long, parse_ulong);
- MAKE_PARSER(long long, parse_longlong);
- MAKE_PARSER(unsigned long long, parse_ulonglong);
- MAKE_PARSER(float, parse_float);
- MAKE_PARSER(double, parse_double);
- MAKE_PARSER(string, parse_string);
- MAKE_PARSER(StringPiece, parse_stringpiece);
+#define MAKE_PARSER(type, name) \
+ Arg(type* p) : arg_(p), parser_(name) {} \
+ Arg(type* p, Parser parser) : arg_(p), parser_(parser) {}
+
+ MAKE_PARSER(char, parse_char)
+ MAKE_PARSER(signed char, parse_schar)
+ MAKE_PARSER(unsigned char, parse_uchar)
+ MAKE_PARSER(float, parse_float)
+ MAKE_PARSER(double, parse_double)
+ MAKE_PARSER(string, parse_string)
+ MAKE_PARSER(StringPiece, parse_stringpiece)
+
+ MAKE_PARSER(short, parse_short)
+ MAKE_PARSER(unsigned short, parse_ushort)
+ MAKE_PARSER(int, parse_int)
+ MAKE_PARSER(unsigned int, parse_uint)
+ MAKE_PARSER(long, parse_long)
+ MAKE_PARSER(unsigned long, parse_ulong)
+ MAKE_PARSER(long long, parse_longlong)
+ MAKE_PARSER(unsigned long long, parse_ulonglong)
#undef MAKE_PARSER
- // Generic constructor
- template <class T> Arg(T*, Parser parser);
- // Generic constructor template
+ // Generic constructor templates
template <class T> Arg(T* p)
- : arg_(p), parser_(_RE2_MatchObject<T>::Parse) {
- }
+ : arg_(p), parser_(_RE2_MatchObject<T>::Parse) { }
+ template <class T> Arg(T* p, Parser parser)
+ : arg_(p), parser_(parser) { }
// Parse the data
- bool Parse(const char* str, int n) const;
+ bool Parse(const char* str, size_t n) const;
private:
void* arg_;
Parser parser_;
- static bool parse_null (const char* str, int n, void* dest);
- static bool parse_char (const char* str, int n, void* dest);
- static bool parse_uchar (const char* str, int n, void* dest);
- static bool parse_float (const char* str, int n, void* dest);
- static bool parse_double (const char* str, int n, void* dest);
- static bool parse_string (const char* str, int n, void* dest);
- static bool parse_stringpiece (const char* str, int n, void* dest);
-
-#define DECLARE_INTEGER_PARSER(name) \
- private: \
- static bool parse_ ## name(const char* str, int n, void* dest); \
- static bool parse_ ## name ## _radix( \
- const char* str, int n, void* dest, int radix); \
- public: \
- static bool parse_ ## name ## _hex(const char* str, int n, void* dest); \
- static bool parse_ ## name ## _octal(const char* str, int n, void* dest); \
- static bool parse_ ## name ## _cradix(const char* str, int n, void* dest)
-
- DECLARE_INTEGER_PARSER(short);
- DECLARE_INTEGER_PARSER(ushort);
- DECLARE_INTEGER_PARSER(int);
- DECLARE_INTEGER_PARSER(uint);
- DECLARE_INTEGER_PARSER(long);
- DECLARE_INTEGER_PARSER(ulong);
- DECLARE_INTEGER_PARSER(longlong);
- DECLARE_INTEGER_PARSER(ulonglong);
+ static bool parse_null (const char* str, size_t n, void* dest);
+ static bool parse_char (const char* str, size_t n, void* dest);
+ static bool parse_schar (const char* str, size_t n, void* dest);
+ static bool parse_uchar (const char* str, size_t n, void* dest);
+ static bool parse_float (const char* str, size_t n, void* dest);
+ static bool parse_double (const char* str, size_t n, void* dest);
+ static bool parse_string (const char* str, size_t n, void* dest);
+ static bool parse_stringpiece (const char* str, size_t n, void* dest);
+
+#define DECLARE_INTEGER_PARSER(name) \
+ private: \
+ static bool parse_##name(const char* str, size_t n, void* dest); \
+ static bool parse_##name##_radix(const char* str, size_t n, void* dest, \
+ int radix); \
+ \
+ public: \
+ static bool parse_##name##_hex(const char* str, size_t n, void* dest); \
+ static bool parse_##name##_octal(const char* str, size_t n, void* dest); \
+ static bool parse_##name##_cradix(const char* str, size_t n, void* dest);
+
+ DECLARE_INTEGER_PARSER(short)
+ DECLARE_INTEGER_PARSER(ushort)
+ DECLARE_INTEGER_PARSER(int)
+ DECLARE_INTEGER_PARSER(uint)
+ DECLARE_INTEGER_PARSER(long)
+ DECLARE_INTEGER_PARSER(ulong)
+ DECLARE_INTEGER_PARSER(longlong)
+ DECLARE_INTEGER_PARSER(ulonglong)
#undef DECLARE_INTEGER_PARSER
+
};
inline RE2::Arg::Arg() : arg_(NULL), parser_(parse_null) { }
inline RE2::Arg::Arg(void* p) : arg_(p), parser_(parse_null) { }
+inline RE2::Arg::Arg(std::nullptr_t p) : arg_(p), parser_(parse_null) { }
-inline bool RE2::Arg::Parse(const char* str, int n) const {
+inline bool RE2::Arg::Parse(const char* str, size_t n) const {
return (*parser_)(str, n, arg_);
}
// This part of the parser, appropriate only for ints, deals with bases
-#define MAKE_INTEGER_PARSER(type, name) \
- inline RE2::Arg RE2::Hex(type* ptr) { \
- return RE2::Arg(ptr, RE2::Arg::parse_ ## name ## _hex); } \
- inline RE2::Arg RE2::Octal(type* ptr) { \
- return RE2::Arg(ptr, RE2::Arg::parse_ ## name ## _octal); } \
- inline RE2::Arg RE2::CRadix(type* ptr) { \
- return RE2::Arg(ptr, RE2::Arg::parse_ ## name ## _cradix); }
-
-MAKE_INTEGER_PARSER(short, short);
-MAKE_INTEGER_PARSER(unsigned short, ushort);
-MAKE_INTEGER_PARSER(int, int);
-MAKE_INTEGER_PARSER(unsigned int, uint);
-MAKE_INTEGER_PARSER(long, long);
-MAKE_INTEGER_PARSER(unsigned long, ulong);
-MAKE_INTEGER_PARSER(long long, longlong);
-MAKE_INTEGER_PARSER(unsigned long long, ulonglong);
+#define MAKE_INTEGER_PARSER(type, name) \
+ inline RE2::Arg RE2::Hex(type* ptr) { \
+ return RE2::Arg(ptr, RE2::Arg::parse_##name##_hex); \
+ } \
+ inline RE2::Arg RE2::Octal(type* ptr) { \
+ return RE2::Arg(ptr, RE2::Arg::parse_##name##_octal); \
+ } \
+ inline RE2::Arg RE2::CRadix(type* ptr) { \
+ return RE2::Arg(ptr, RE2::Arg::parse_##name##_cradix); \
+ }
+
+MAKE_INTEGER_PARSER(short, short)
+MAKE_INTEGER_PARSER(unsigned short, ushort)
+MAKE_INTEGER_PARSER(int, int)
+MAKE_INTEGER_PARSER(unsigned int, uint)
+MAKE_INTEGER_PARSER(long, long)
+MAKE_INTEGER_PARSER(unsigned long, ulong)
+MAKE_INTEGER_PARSER(long long, longlong)
+MAKE_INTEGER_PARSER(unsigned long long, ulonglong)
#undef MAKE_INTEGER_PARSER
+#ifndef SWIG
+
+// Silence warnings about missing initializers for members of LazyRE2.
+// Note that we test for Clang first because it defines __GNUC__ as well.
+#if defined(__clang__)
+#elif defined(__GNUC__) && __GNUC__ >= 6
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+// Helper for writing global or static RE2s safely.
+// Write
+// static LazyRE2 re = {".*"};
+// and then use *re instead of writing
+// static RE2 re(".*");
+// The former is more careful about multithreaded
+// situations than the latter.
+//
+// N.B. This class never deletes the RE2 object that
+// it constructs: that's a feature, so that it can be used
+// for global and function static variables.
+class LazyRE2 {
+ private:
+ struct NoArg {};
+
+ public:
+ typedef RE2 element_type; // support std::pointer_traits
+
+ // Constructor omitted to preserve braced initialization in C++98.
+
+ // Pretend to be a pointer to Type (never NULL due to on-demand creation):
+ RE2& operator*() const { return *get(); }
+ RE2* operator->() const { return get(); }
+
+ // Named accessor/initializer:
+ RE2* get() const {
+ std::call_once(once_, &LazyRE2::Init, this);
+ return ptr_;
+ }
+
+ // All data fields must be public to support {"foo"} initialization.
+ const char* pattern_;
+ RE2::CannedOptions options_;
+ NoArg barrier_against_excess_initializers_;
+
+ mutable RE2* ptr_;
+ mutable std::once_flag once_;
+
+ private:
+ static void Init(const LazyRE2* lazy_re2) {
+ lazy_re2->ptr_ = new RE2(lazy_re2->pattern_, lazy_re2->options_);
+ }
+
+ void operator=(const LazyRE2&); // disallowed
+};
+#endif // SWIG
+
} // namespace re2
using re2::RE2;
+using re2::LazyRE2;
-#endif /* RE2_RE2_H */
+#endif // RE2_RE2_H_
diff --git a/re2/regexp.cc b/re2/regexp.cc
index a74ceec..7cfbbcb 100644
--- a/re2/regexp.cc
+++ b/re2/regexp.cc
@@ -5,8 +5,21 @@
// Regular expression representation.
// Tested by parse_test.cc
-#include "util/util.h"
#include "re2/regexp.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <algorithm>
+#include <map>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include "util/util.h"
+#include "util/logging.h"
+#include "util/mutex.h"
+#include "util/utf.h"
#include "re2/stringpiece.h"
#include "re2/walker-inl.h"
@@ -14,9 +27,9 @@ namespace re2 {
// Constructor. Allocates vectors as appropriate for operator.
Regexp::Regexp(RegexpOp op, ParseFlags parse_flags)
- : op_(op),
+ : op_(static_cast<uint8_t>(op)),
simple_(false),
- parse_flags_(static_cast<uint16>(parse_flags)),
+ parse_flags_(static_cast<uint16_t>(parse_flags)),
ref_(1),
nsub_(0),
down_(NULL) {
@@ -43,7 +56,8 @@ Regexp::~Regexp() {
delete[] runes_;
break;
case kRegexpCharClass:
- cc_->Delete();
+ if (cc_)
+ cc_->Delete();
delete ccb_;
break;
}
@@ -59,30 +73,29 @@ bool Regexp::QuickDestroy() {
return false;
}
-static map<Regexp*, int> *ref_map;
-GLOBAL_MUTEX(ref_mutex);
+// Lazily allocated.
+static Mutex* ref_mutex;
+static std::map<Regexp*, int>* ref_map;
int Regexp::Ref() {
if (ref_ < kMaxRef)
return ref_;
- GLOBAL_MUTEX_LOCK(ref_mutex);
- int r = 0;
- if (ref_map != NULL) {
- r = (*ref_map)[this];
- }
- GLOBAL_MUTEX_UNLOCK(ref_mutex);
- return r;
+ MutexLock l(ref_mutex);
+ return (*ref_map)[this];
}
// Increments reference count, returns object as convenience.
Regexp* Regexp::Incref() {
if (ref_ >= kMaxRef-1) {
+ static std::once_flag ref_once;
+ std::call_once(ref_once, []() {
+ ref_mutex = new Mutex;
+ ref_map = new std::map<Regexp*, int>;
+ });
+
// Store ref count in overflow map.
- GLOBAL_MUTEX_LOCK(ref_mutex);
- if (ref_map == NULL) {
- ref_map = new map<Regexp*, int>;
- }
+ MutexLock l(ref_mutex);
if (ref_ == kMaxRef) {
// already overflowed
(*ref_map)[this]++;
@@ -91,7 +104,6 @@ Regexp* Regexp::Incref() {
(*ref_map)[this] = kMaxRef;
ref_ = kMaxRef;
}
- GLOBAL_MUTEX_UNLOCK(ref_mutex);
return this;
}
@@ -103,15 +115,14 @@ Regexp* Regexp::Incref() {
void Regexp::Decref() {
if (ref_ == kMaxRef) {
// Ref count is stored in overflow map.
- GLOBAL_MUTEX_LOCK(ref_mutex);
+ MutexLock l(ref_mutex);
int r = (*ref_map)[this] - 1;
if (r < kMaxRef) {
- ref_ = r;
+ ref_ = static_cast<uint16_t>(r);
ref_map->erase(this);
} else {
(*ref_map)[this] = r;
}
- GLOBAL_MUTEX_UNLOCK(ref_mutex);
return;
}
ref_--;
@@ -179,31 +190,45 @@ Regexp* Regexp::HaveMatch(int match_id, ParseFlags flags) {
return re;
}
-Regexp* Regexp::Plus(Regexp* sub, ParseFlags flags) {
- if (sub->op() == kRegexpPlus && sub->parse_flags() == flags)
+Regexp* Regexp::StarPlusOrQuest(RegexpOp op, Regexp* sub, ParseFlags flags) {
+ // Squash **, ++ and ??.
+ if (op == sub->op() && flags == sub->parse_flags())
return sub;
- Regexp* re = new Regexp(kRegexpPlus, flags);
+
+ // Squash *+, *?, +*, +?, ?* and ?+. They all squash to *, so because
+ // op is Star/Plus/Quest, we just have to check that sub->op() is too.
+ if ((sub->op() == kRegexpStar ||
+ sub->op() == kRegexpPlus ||
+ sub->op() == kRegexpQuest) &&
+ flags == sub->parse_flags()) {
+ // If sub is Star, no need to rewrite it.
+ if (sub->op() == kRegexpStar)
+ return sub;
+
+ // Rewrite sub to Star.
+ Regexp* re = new Regexp(kRegexpStar, flags);
+ re->AllocSub(1);
+ re->sub()[0] = sub->sub()[0]->Incref();
+ sub->Decref(); // We didn't consume the reference after all.
+ return re;
+ }
+
+ Regexp* re = new Regexp(op, flags);
re->AllocSub(1);
re->sub()[0] = sub;
return re;
}
+Regexp* Regexp::Plus(Regexp* sub, ParseFlags flags) {
+ return StarPlusOrQuest(kRegexpPlus, sub, flags);
+}
+
Regexp* Regexp::Star(Regexp* sub, ParseFlags flags) {
- if (sub->op() == kRegexpStar && sub->parse_flags() == flags)
- return sub;
- Regexp* re = new Regexp(kRegexpStar, flags);
- re->AllocSub(1);
- re->sub()[0] = sub;
- return re;
+ return StarPlusOrQuest(kRegexpStar, sub, flags);
}
Regexp* Regexp::Quest(Regexp* sub, ParseFlags flags) {
- if (sub->op() == kRegexpQuest && sub->parse_flags() == flags)
- return sub;
- Regexp* re = new Regexp(kRegexpQuest, flags);
- re->AllocSub(1);
- re->sub()[0] = sub;
- return re;
+ return StarPlusOrQuest(kRegexpQuest, sub, flags);
}
Regexp* Regexp::ConcatOrAlternate(RegexpOp op, Regexp** sub, int nsub,
@@ -211,6 +236,13 @@ Regexp* Regexp::ConcatOrAlternate(RegexpOp op, Regexp** sub, int nsub,
if (nsub == 1)
return sub[0];
+ if (nsub == 0) {
+ if (op == kRegexpAlternate)
+ return new Regexp(kRegexpNoMatch, flags);
+ else
+ return new Regexp(kRegexpEmptyMatch, flags);
+ }
+
Regexp** subcopy = NULL;
if (op == kRegexpAlternate && can_factor) {
// Going to edit sub; make a copy so we don't step on caller.
@@ -303,13 +335,15 @@ Regexp* Regexp::NewCharClass(CharClass* cc, ParseFlags flags) {
return re;
}
-// Swaps this and that in place.
void Regexp::Swap(Regexp* that) {
- // Can use memmove because Regexp is just a struct (no vtable).
+ // Regexp is not trivially copyable, so we cannot freely copy it with
+ // memmove(3), but swapping objects like so is safe for our purposes.
char tmp[sizeof *this];
- memmove(tmp, this, sizeof tmp);
- memmove(this, that, sizeof tmp);
- memmove(that, tmp, sizeof tmp);
+ void* vthis = reinterpret_cast<void*>(this);
+ void* vthat = reinterpret_cast<void*>(that);
+ memmove(tmp, vthis, sizeof *this);
+ memmove(vthis, vthat, sizeof *this);
+ memmove(vthat, tmp, sizeof *this);
}
// Tests equality of all top-level structure but not subregexps.
@@ -405,7 +439,7 @@ bool Regexp::Equal(Regexp* a, Regexp* b) {
// The stack (vector) has pairs of regexps waiting to
// be compared. The regexps are only equal if
// all the pairs end up being equal.
- vector<Regexp*> stk;
+ std::vector<Regexp*> stk;
for (;;) {
// Invariant: TopEqual(a, b) == true.
@@ -445,10 +479,11 @@ bool Regexp::Equal(Regexp* a, Regexp* b) {
continue;
}
- int n = stk.size();
+ size_t n = stk.size();
if (n == 0)
break;
+ DCHECK_GE(n, 2);
a = stk[n-2];
b = stk[n-1];
stk.resize(n-2);
@@ -517,7 +552,9 @@ class NumCapturesWalker : public Regexp::Walker<Ignored> {
private:
int ncapture_;
- DISALLOW_EVIL_CONSTRUCTORS(NumCapturesWalker);
+
+ NumCapturesWalker(const NumCapturesWalker&) = delete;
+ NumCapturesWalker& operator=(const NumCapturesWalker&) = delete;
};
int Regexp::NumCaptures() {
@@ -532,8 +569,8 @@ class NamedCapturesWalker : public Regexp::Walker<Ignored> {
NamedCapturesWalker() : map_(NULL) {}
~NamedCapturesWalker() { delete map_; }
- map<string, int>* TakeMap() {
- map<string, int>* m = map_;
+ std::map<string, int>* TakeMap() {
+ std::map<string, int>* m = map_;
map_ = NULL;
return m;
}
@@ -542,7 +579,7 @@ class NamedCapturesWalker : public Regexp::Walker<Ignored> {
if (re->op() == kRegexpCapture && re->name() != NULL) {
// Allocate map once we find a name.
if (map_ == NULL)
- map_ = new map<string, int>;
+ map_ = new std::map<string, int>;
// Record first occurrence of each name.
// (The rule is that if you have the same name
@@ -560,11 +597,13 @@ class NamedCapturesWalker : public Regexp::Walker<Ignored> {
}
private:
- map<string, int>* map_;
- DISALLOW_EVIL_CONSTRUCTORS(NamedCapturesWalker);
+ std::map<string, int>* map_;
+
+ NamedCapturesWalker(const NamedCapturesWalker&) = delete;
+ NamedCapturesWalker& operator=(const NamedCapturesWalker&) = delete;
};
-map<string, int>* Regexp::NamedCaptures() {
+std::map<string, int>* Regexp::NamedCaptures() {
NamedCapturesWalker w;
w.Walk(this, 0);
return w.TakeMap();
@@ -576,8 +615,8 @@ class CaptureNamesWalker : public Regexp::Walker<Ignored> {
CaptureNamesWalker() : map_(NULL) {}
~CaptureNamesWalker() { delete map_; }
- map<int, string>* TakeMap() {
- map<int, string>* m = map_;
+ std::map<int, string>* TakeMap() {
+ std::map<int, string>* m = map_;
map_ = NULL;
return m;
}
@@ -586,7 +625,7 @@ class CaptureNamesWalker : public Regexp::Walker<Ignored> {
if (re->op() == kRegexpCapture && re->name() != NULL) {
// Allocate map once we find a name.
if (map_ == NULL)
- map_ = new map<int, string>;
+ map_ = new std::map<int, string>;
(*map_)[re->cap()] = *re->name();
}
@@ -600,11 +639,13 @@ class CaptureNamesWalker : public Regexp::Walker<Ignored> {
}
private:
- map<int, string>* map_;
- DISALLOW_EVIL_CONSTRUCTORS(CaptureNamesWalker);
+ std::map<int, string>* map_;
+
+ CaptureNamesWalker(const CaptureNamesWalker&) = delete;
+ CaptureNamesWalker& operator=(const CaptureNamesWalker&) = delete;
};
-map<int, string>* Regexp::CaptureNames() {
+std::map<int, string>* Regexp::CaptureNames() {
CaptureNamesWalker w;
w.Walk(this, 0);
return w.TakeMap();
@@ -614,7 +655,7 @@ map<int, string>* Regexp::CaptureNames() {
// with a fixed string prefix. If so, returns the prefix and
// the regexp that remains after the prefix. The prefix might
// be ASCII case-insensitive.
-bool Regexp::RequiredPrefix(string *prefix, bool *foldcase, Regexp** suffix) {
+bool Regexp::RequiredPrefix(string* prefix, bool* foldcase, Regexp** suffix) {
// No need for a walker: the regexp must be of the form
// 1. some number of ^ anchors
// 2. a literal char or string
@@ -643,7 +684,7 @@ bool Regexp::RequiredPrefix(string *prefix, bool *foldcase, Regexp** suffix) {
if (re->parse_flags() & Latin1) {
prefix->resize(re->nrunes_);
for (int j = 0; j < re->nrunes_; j++)
- (*prefix)[j] = re->runes_[j];
+ (*prefix)[j] = static_cast<char>(re->runes_[j]);
} else {
// Convert to UTF-8 in place.
// Assume worst-case space and then trim.
@@ -652,7 +693,7 @@ bool Regexp::RequiredPrefix(string *prefix, bool *foldcase, Regexp** suffix) {
for (int j = 0; j < re->nrunes_; j++) {
Rune r = re->runes_[j];
if (r < Runeself)
- *p++ = r;
+ *p++ = static_cast<char>(r);
else
p += runetochar(p, &r);
}
@@ -662,14 +703,14 @@ bool Regexp::RequiredPrefix(string *prefix, bool *foldcase, Regexp** suffix) {
case kRegexpLiteral:
if ((re->parse_flags() & Latin1) || re->rune_ < Runeself) {
- prefix->append(1, re->rune_);
+ prefix->append(1, static_cast<char>(re->rune_));
} else {
char buf[UTFmax];
prefix->append(buf, runetochar(buf, &re->rune_));
}
break;
}
- *foldcase = (sub[i]->parse_flags() & FoldCase);
+ *foldcase = (sub[i]->parse_flags() & FoldCase) != 0;
i++;
// The rest.
@@ -704,13 +745,13 @@ bool CharClassBuilder::AddRange(Rune lo, Rune hi) {
if (lo <= 'z' && hi >= 'A') {
// Overlaps some alpha, maybe not all.
// Update bitmaps telling which ASCII letters are in the set.
- Rune lo1 = max<Rune>(lo, 'A');
- Rune hi1 = min<Rune>(hi, 'Z');
+ Rune lo1 = std::max<Rune>(lo, 'A');
+ Rune hi1 = std::min<Rune>(hi, 'Z');
if (lo1 <= hi1)
upper_ |= ((1 << (hi1 - lo1 + 1)) - 1) << (lo1 - 'A');
- lo1 = max<Rune>(lo, 'a');
- hi1 = min<Rune>(hi, 'z');
+ lo1 = std::max<Rune>(lo, 'a');
+ hi1 = std::min<Rune>(hi, 'z');
if (lo1 <= hi1)
lower_ |= ((1 << (hi1 - lo1 + 1)) - 1) << (lo1 - 'a');
}
@@ -826,7 +867,7 @@ void CharClassBuilder::RemoveAbove(Rune r) {
void CharClassBuilder::Negate() {
// Build up negation and then copy in.
// Could edit ranges in place, but C++ won't let me.
- vector<RuneRange> v;
+ std::vector<RuneRange> v;
v.reserve(ranges_.size() + 1);
// In negation, first range begins at 0, unless
@@ -849,7 +890,7 @@ void CharClassBuilder::Negate() {
}
ranges_.clear();
- for (int i = 0; i < v.size(); i++)
+ for (size_t i = 0; i < v.size(); i++)
ranges_.insert(v[i]);
upper_ = AlphaMask & ~upper_;
@@ -863,7 +904,7 @@ void CharClassBuilder::Negate() {
CharClass* CharClass::New(int maxranges) {
CharClass* cc;
- uint8* data = new uint8[sizeof *cc + maxranges*sizeof cc->ranges_[0]];
+ uint8_t* data = new uint8_t[sizeof *cc + maxranges*sizeof cc->ranges_[0]];
cc = reinterpret_cast<CharClass*>(data);
cc->ranges_ = reinterpret_cast<RuneRange*>(data + sizeof *cc);
cc->nranges_ = 0;
@@ -873,9 +914,7 @@ CharClass* CharClass::New(int maxranges) {
}
void CharClass::Delete() {
- if (this == NULL)
- return;
- uint8 *data = reinterpret_cast<uint8*>(this);
+ uint8_t* data = reinterpret_cast<uint8_t*>(this);
delete[] data;
}
@@ -917,12 +956,12 @@ bool CharClass::Contains(Rune r) {
}
CharClass* CharClassBuilder::GetCharClass() {
- CharClass* cc = CharClass::New(ranges_.size());
+ CharClass* cc = CharClass::New(static_cast<int>(ranges_.size()));
int n = 0;
for (iterator it = begin(); it != end(); ++it)
cc->ranges_[n++] = *it;
cc->nranges_ = n;
- DCHECK_LE(n, ranges_.size());
+ DCHECK_LE(n, static_cast<int>(ranges_.size()));
cc->nrunes_ = nrunes_;
cc->folds_ascii_ = FoldsASCII();
return cc;
diff --git a/re2/regexp.h b/re2/regexp.h
index 331c017..2ca96cd 100644
--- a/re2/regexp.h
+++ b/re2/regexp.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_REGEXP_H_
+#define RE2_REGEXP_H_
+
// --- SPONSORED LINK --------------------------------------------------
// If you want to use this library for regular expression matching,
// you should use re2/re2.h, which provides a class RE2 that
@@ -83,10 +86,14 @@
// form accessible to clients, so that client code can analyze the
// parsed regular expressions.
-#ifndef RE2_REGEXP_H__
-#define RE2_REGEXP_H__
+#include <stdint.h>
+#include <map>
+#include <set>
+#include <string>
#include "util/util.h"
+#include "util/logging.h"
+#include "util/utf.h"
#include "re2/stringpiece.h"
namespace re2 {
@@ -185,10 +192,10 @@ class RegexpStatus {
RegexpStatus() : code_(kRegexpSuccess), tmp_(NULL) {}
~RegexpStatus() { delete tmp_; }
- void set_code(enum RegexpStatusCode code) { code_ = code; }
+ void set_code(RegexpStatusCode code) { code_ = code; }
void set_error_arg(const StringPiece& error_arg) { error_arg_ = error_arg; }
void set_tmp(string* tmp) { delete tmp_; tmp_ = tmp; }
- enum RegexpStatusCode code() const { return code_; }
+ RegexpStatusCode code() const { return code_; }
const StringPiece& error_arg() const { return error_arg_; }
bool ok() const { return code() == kRegexpSuccess; }
@@ -197,23 +204,21 @@ class RegexpStatus {
// Returns text equivalent of code, e.g.:
// "Bad character class"
- static string CodeText(enum RegexpStatusCode code);
+ static string CodeText(RegexpStatusCode code);
// Returns text describing error, e.g.:
// "Bad character class: [z-a]"
string Text() const;
private:
- enum RegexpStatusCode code_; // Kind of error
+ RegexpStatusCode code_; // Kind of error
StringPiece error_arg_; // Piece of regexp containing syntax error.
string* tmp_; // Temporary storage, possibly where error_arg_ is.
- DISALLOW_EVIL_CONSTRUCTORS(RegexpStatus);
+ RegexpStatus(const RegexpStatus&) = delete;
+ RegexpStatus& operator=(const RegexpStatus&) = delete;
};
-// Walker to implement Simplify.
-class SimplifyWalker;
-
// Compiled form; see prog.h
class Prog;
@@ -261,7 +266,9 @@ class CharClass {
int nrunes_;
RuneRange *ranges_;
int nranges_;
- DISALLOW_EVIL_CONSTRUCTORS(CharClass);
+
+ CharClass(const CharClass&) = delete;
+ CharClass& operator=(const CharClass&) = delete;
};
class Regexp {
@@ -269,51 +276,52 @@ class Regexp {
// Flags for parsing. Can be ORed together.
enum ParseFlags {
- NoParseFlags = 0,
- FoldCase = 1<<0, // Fold case during matching (case-insensitive).
- Literal = 1<<1, // Treat s as literal string instead of a regexp.
- ClassNL = 1<<2, // Allow char classes like [^a-z] and \D and \s
- // and [[:space:]] to match newline.
- DotNL = 1<<3, // Allow . to match newline.
- MatchNL = ClassNL | DotNL,
- OneLine = 1<<4, // Treat ^ and $ as only matching at beginning and
- // end of text, not around embedded newlines.
- // (Perl's default)
- Latin1 = 1<<5, // Regexp and text are in Latin1, not UTF-8.
- NonGreedy = 1<<6, // Repetition operators are non-greedy by default.
- PerlClasses = 1<<7, // Allow Perl character classes like \d.
- PerlB = 1<<8, // Allow Perl's \b and \B.
- PerlX = 1<<9, // Perl extensions:
- // non-capturing parens - (?: )
- // non-greedy operators - *? +? ?? {}?
- // flag edits - (?i) (?-i) (?i: )
- // i - FoldCase
- // m - !OneLine
- // s - DotNL
- // U - NonGreedy
- // line ends: \A \z
- // \Q and \E to disable/enable metacharacters
- // (?P<name>expr) for named captures
- // \C to match any single byte
- UnicodeGroups = 1<<10, // Allow \p{Han} for Unicode Han group
- // and \P{Han} for its negation.
- NeverNL = 1<<11, // Never match NL, even if the regexp mentions
- // it explicitly.
- NeverCapture = 1<<12, // Parse all parens as non-capturing.
+ NoParseFlags = 0,
+ FoldCase = 1<<0, // Fold case during matching (case-insensitive).
+ Literal = 1<<1, // Treat s as literal string instead of a regexp.
+ ClassNL = 1<<2, // Allow char classes like [^a-z] and \D and \s
+ // and [[:space:]] to match newline.
+ DotNL = 1<<3, // Allow . to match newline.
+ MatchNL = ClassNL | DotNL,
+ OneLine = 1<<4, // Treat ^ and $ as only matching at beginning and
+ // end of text, not around embedded newlines.
+ // (Perl's default)
+ Latin1 = 1<<5, // Regexp and text are in Latin1, not UTF-8.
+ NonGreedy = 1<<6, // Repetition operators are non-greedy by default.
+ PerlClasses = 1<<7, // Allow Perl character classes like \d.
+ PerlB = 1<<8, // Allow Perl's \b and \B.
+ PerlX = 1<<9, // Perl extensions:
+ // non-capturing parens - (?: )
+ // non-greedy operators - *? +? ?? {}?
+ // flag edits - (?i) (?-i) (?i: )
+ // i - FoldCase
+ // m - !OneLine
+ // s - DotNL
+ // U - NonGreedy
+ // line ends: \A \z
+ // \Q and \E to disable/enable metacharacters
+ // (?P<name>expr) for named captures
+ // \C to match any single byte
+ UnicodeGroups = 1<<10, // Allow \p{Han} for Unicode Han group
+ // and \P{Han} for its negation.
+ NeverNL = 1<<11, // Never match NL, even if the regexp mentions
+ // it explicitly.
+ NeverCapture = 1<<12, // Parse all parens as non-capturing.
// As close to Perl as we can get.
- LikePerl = ClassNL | OneLine | PerlClasses | PerlB | PerlX |
- UnicodeGroups,
+ LikePerl = ClassNL | OneLine | PerlClasses | PerlB | PerlX |
+ UnicodeGroups,
// Internal use only.
- WasDollar = 1<<15, // on kRegexpEndText: was $ in regexp text
+ WasDollar = 1<<13, // on kRegexpEndText: was $ in regexp text
+ AllParseFlags = (1<<14)-1,
};
// Get. No set, Regexps are logically immutable once created.
RegexpOp op() { return static_cast<RegexpOp>(op_); }
int nsub() { return nsub_; }
- bool simple() { return simple_; }
- enum ParseFlags parse_flags() { return static_cast<ParseFlags>(parse_flags_); }
+ bool simple() { return simple_ != 0; }
+ ParseFlags parse_flags() { return static_cast<ParseFlags>(parse_flags_); }
int Ref(); // For testing.
Regexp** sub() {
@@ -353,6 +361,7 @@ class Regexp {
// removed. The result will capture exactly the same
// subexpressions the original did, unless formatted with ToString.
Regexp* Simplify();
+ friend class CoalesceWalker;
friend class SimplifyWalker;
// Parses the regexp src and then simplifies it and sets *dst to the
@@ -369,12 +378,12 @@ class Regexp {
// Returns a map from names to capturing group indices,
// or NULL if the regexp contains no named capture groups.
// The caller is responsible for deleting the map.
- map<string, int>* NamedCaptures();
+ std::map<string, int>* NamedCaptures();
// Returns a map from capturing group indices to capturing group
// names or NULL if the regexp contains no named capture groups. The
// caller is responsible for deleting the map.
- map<int, string>* CaptureNames();
+ std::map<int, string>* CaptureNames();
// Returns a string representation of the current regexp,
// using as few parentheses as possible.
@@ -410,8 +419,8 @@ class Regexp {
// Construction and execution of prog will
// stay within approximately max_mem bytes of memory.
// If max_mem <= 0, a reasonable default is used.
- Prog* CompileToProg(int64 max_mem);
- Prog* CompileToReverseProg(int64 max_mem);
+ Prog* CompileToProg(int64_t max_mem);
+ Prog* CompileToReverseProg(int64_t max_mem);
// Whether to expect this library to find exactly the same answer as PCRE
// when running this regexp. Most regexps do mimic PCRE exactly, but a few
@@ -427,7 +436,9 @@ class Regexp {
// begin with a non-empty fixed string (perhaps after ASCII
// case-folding). If so, returns the prefix and the sub-regexp that
// follows it.
- bool RequiredPrefix(string* prefix, bool *foldcase, Regexp** suffix);
+ // Callers should expect *prefix, *foldcase and *suffix to be "zeroed"
+ // regardless of the return value.
+ bool RequiredPrefix(string* prefix, bool* foldcase, Regexp** suffix);
private:
// Constructor allocates vectors as appropriate for operator.
@@ -441,6 +452,7 @@ class Regexp {
// Helpers for Parse. Listed here so they can edit Regexps.
class ParseState;
+
friend class ParseState;
friend bool ParseCharClass(StringPiece* s, Regexp** out_re,
RegexpStatus* status);
@@ -451,6 +463,10 @@ class Regexp {
// Computes whether Regexp is already simple.
bool ComputeSimple();
+ // Constructor that generates a Star, Plus or Quest,
+ // squashing the pair if sub is also a Star, Plus or Quest.
+ static Regexp* StarPlusOrQuest(RegexpOp op, Regexp* sub, ParseFlags flags);
+
// Constructor that generates a concatenation or alternation,
// enforcing the limit on the number of subexpressions for
// a particular Regexp.
@@ -478,8 +494,7 @@ class Regexp {
// Simplifies an alternation of literal strings by factoring out
// common prefixes.
static int FactorAlternation(Regexp** sub, int nsub, ParseFlags flags);
- static int FactorAlternationRecursive(Regexp** sub, int nsub,
- ParseFlags flags, int maxdepth);
+ friend class FactorAlternationImpl;
// Is a == b? Only efficient on regexps that have not been through
// Simplify yet - the expansion of a kRegexpRepeat will make this
@@ -488,11 +503,10 @@ class Regexp {
// Allocate space for n sub-regexps.
void AllocSub(int n) {
- if (n < 0 || static_cast<uint16>(n) != n)
- LOG(FATAL) << "Cannot AllocSub " << n;
+ DCHECK(n >= 0 && static_cast<uint16_t>(n) == n);
if (n > 1)
submany_ = new Regexp*[n];
- nsub_ = n;
+ nsub_ = static_cast<uint16_t>(n);
}
// Add Rune to LiteralString
@@ -502,38 +516,38 @@ class Regexp {
void Swap(Regexp *that);
// Operator. See description of operators above.
- // uint8 instead of RegexpOp to control space usage.
- uint8 op_;
+ // uint8_t instead of RegexpOp to control space usage.
+ uint8_t op_;
// Is this regexp structure already simple
// (has it been returned by Simplify)?
- // uint8 instead of bool to control space usage.
- uint8 simple_;
+ // uint8_t instead of bool to control space usage.
+ uint8_t simple_;
// Flags saved from parsing and used during execution.
// (Only FoldCase is used.)
- // uint16 instead of ParseFlags to control space usage.
- uint16 parse_flags_;
+ // uint16_t instead of ParseFlags to control space usage.
+ uint16_t parse_flags_;
// Reference count. Exists so that SimplifyRegexp can build
// regexp structures that are dags rather than trees to avoid
// exponential blowup in space requirements.
- // uint16 to control space usage.
+ // uint16_t to control space usage.
// The standard regexp routines will never generate a
- // ref greater than the maximum repeat count (100),
+ // ref greater than the maximum repeat count (kMaxRepeat),
// but even so, Incref and Decref consult an overflow map
// when ref_ reaches kMaxRef.
- uint16 ref_;
- static const uint16 kMaxRef = 0xffff;
+ uint16_t ref_;
+ static const uint16_t kMaxRef = 0xffff;
// Subexpressions.
- // uint16 to control space usage.
+ // uint16_t to control space usage.
// Concat and Alternate handle larger numbers of subexpressions
// by building concatenation or alternation trees.
// Other routines should call Concat or Alternate instead of
// filling in sub() by hand.
- uint16 nsub_;
- static const uint16 kMaxNsub = 0xffff;
+ uint16_t nsub_;
+ static const uint16_t kMaxNsub = 0xffff;
union {
Regexp** submany_; // if nsub_ > 1
Regexp* subone_; // if nsub_ == 1
@@ -568,11 +582,12 @@ class Regexp {
void *the_union_[2]; // as big as any other element, for memset
};
- DISALLOW_EVIL_CONSTRUCTORS(Regexp);
+ Regexp(const Regexp&) = delete;
+ Regexp& operator=(const Regexp&) = delete;
};
// Character class set: contains non-overlapping, non-abutting RuneRanges.
-typedef set<RuneRange, RuneRangeLess> RuneRangeSet;
+typedef std::set<RuneRange, RuneRangeLess> RuneRangeSet;
class CharClassBuilder {
public:
@@ -597,37 +612,41 @@ class CharClassBuilder {
void AddRangeFlags(Rune lo, Rune hi, Regexp::ParseFlags parse_flags);
private:
- static const uint32 AlphaMask = (1<<26) - 1;
- uint32 upper_; // bitmap of A-Z
- uint32 lower_; // bitmap of a-z
+ static const uint32_t AlphaMask = (1<<26) - 1;
+ uint32_t upper_; // bitmap of A-Z
+ uint32_t lower_; // bitmap of a-z
int nrunes_;
RuneRangeSet ranges_;
- DISALLOW_EVIL_CONSTRUCTORS(CharClassBuilder);
+
+ CharClassBuilder(const CharClassBuilder&) = delete;
+ CharClassBuilder& operator=(const CharClassBuilder&) = delete;
};
-// Tell g++ that bitwise ops on ParseFlags produce ParseFlags.
-inline Regexp::ParseFlags operator|(Regexp::ParseFlags a, Regexp::ParseFlags b)
-{
- return static_cast<Regexp::ParseFlags>(static_cast<int>(a) | static_cast<int>(b));
+// Bitwise ops on ParseFlags produce ParseFlags.
+inline Regexp::ParseFlags operator|(Regexp::ParseFlags a,
+ Regexp::ParseFlags b) {
+ return static_cast<Regexp::ParseFlags>(
+ static_cast<int>(a) | static_cast<int>(b));
}
-inline Regexp::ParseFlags operator^(Regexp::ParseFlags a, Regexp::ParseFlags b)
-{
- return static_cast<Regexp::ParseFlags>(static_cast<int>(a) ^ static_cast<int>(b));
+inline Regexp::ParseFlags operator^(Regexp::ParseFlags a,
+ Regexp::ParseFlags b) {
+ return static_cast<Regexp::ParseFlags>(
+ static_cast<int>(a) ^ static_cast<int>(b));
}
-inline Regexp::ParseFlags operator&(Regexp::ParseFlags a, Regexp::ParseFlags b)
-{
- return static_cast<Regexp::ParseFlags>(static_cast<int>(a) & static_cast<int>(b));
+inline Regexp::ParseFlags operator&(Regexp::ParseFlags a,
+ Regexp::ParseFlags b) {
+ return static_cast<Regexp::ParseFlags>(
+ static_cast<int>(a) & static_cast<int>(b));
}
-inline Regexp::ParseFlags operator~(Regexp::ParseFlags a)
-{
- return static_cast<Regexp::ParseFlags>(~static_cast<int>(a));
+inline Regexp::ParseFlags operator~(Regexp::ParseFlags a) {
+ // Attempting to produce a value out of enum's range has undefined behaviour.
+ return static_cast<Regexp::ParseFlags>(
+ ~static_cast<int>(a) & static_cast<int>(Regexp::AllParseFlags));
}
-
-
} // namespace re2
-#endif // RE2_REGEXP_H__
+#endif // RE2_REGEXP_H_
diff --git a/re2/set.cc b/re2/set.cc
index 2bcd30a..2572d50 100644
--- a/re2/set.cc
+++ b/re2/set.cc
@@ -4,36 +4,43 @@
#include "re2/set.h"
+#include <stddef.h>
+#include <algorithm>
+#include <memory>
+
#include "util/util.h"
+#include "util/logging.h"
+#include "util/pod_array.h"
#include "re2/stringpiece.h"
#include "re2/prog.h"
#include "re2/re2.h"
#include "re2/regexp.h"
-using namespace re2;
+namespace re2 {
RE2::Set::Set(const RE2::Options& options, RE2::Anchor anchor) {
options_.Copy(options);
+ options_.set_never_capture(true); // might unblock some optimisations
anchor_ = anchor;
prog_ = NULL;
compiled_ = false;
+ size_ = 0;
}
RE2::Set::~Set() {
- for (int i = 0; i < re_.size(); i++)
- re_[i]->Decref();
+ for (size_t i = 0; i < elem_.size(); i++)
+ elem_[i].second->Decref();
delete prog_;
}
int RE2::Set::Add(const StringPiece& pattern, string* error) {
if (compiled_) {
- LOG(DFATAL) << "RE2::Set::Add after Compile";
+ LOG(DFATAL) << "RE2::Set::Add() called after compiling";
return -1;
}
Regexp::ParseFlags pf = static_cast<Regexp::ParseFlags>(
options_.ParseFlags());
-
RegexpStatus status;
re2::Regexp* re = Regexp::Parse(pattern, pf, &status);
if (re == NULL) {
@@ -45,69 +52,102 @@ int RE2::Set::Add(const StringPiece& pattern, string* error) {
}
// Concatenate with match index and push on vector.
- int n = re_.size();
+ int n = static_cast<int>(elem_.size());
re2::Regexp* m = re2::Regexp::HaveMatch(n, pf);
if (re->op() == kRegexpConcat) {
int nsub = re->nsub();
- re2::Regexp** sub = new re2::Regexp*[nsub + 1];
+ PODArray<re2::Regexp*> sub(nsub + 1);
for (int i = 0; i < nsub; i++)
sub[i] = re->sub()[i]->Incref();
sub[nsub] = m;
re->Decref();
- re = re2::Regexp::Concat(sub, nsub + 1, pf);
- delete[] sub;
+ re = re2::Regexp::Concat(sub.data(), nsub + 1, pf);
} else {
re2::Regexp* sub[2];
sub[0] = re;
sub[1] = m;
re = re2::Regexp::Concat(sub, 2, pf);
}
- re_.push_back(re);
+ elem_.emplace_back(string(pattern), re);
return n;
}
bool RE2::Set::Compile() {
if (compiled_) {
- LOG(DFATAL) << "RE2::Set::Compile multiple times";
+ LOG(DFATAL) << "RE2::Set::Compile() called more than once";
return false;
}
compiled_ = true;
+ size_ = static_cast<int>(elem_.size());
+
+ // Sort the elements by their patterns. This is good enough for now
+ // until we have a Regexp comparison function. (Maybe someday...)
+ std::sort(elem_.begin(), elem_.end(),
+ [](const Elem& a, const Elem& b) -> bool {
+ return a.first < b.first;
+ });
+
+ PODArray<re2::Regexp*> sub(size_);
+ for (int i = 0; i < size_; i++)
+ sub[i] = elem_[i].second;
+ elem_.clear();
+ elem_.shrink_to_fit();
Regexp::ParseFlags pf = static_cast<Regexp::ParseFlags>(
options_.ParseFlags());
- re2::Regexp* re = re2::Regexp::Alternate(const_cast<re2::Regexp**>(&re_[0]),
- re_.size(), pf);
- re_.clear();
- re2::Regexp* sre = re->Simplify();
- re->Decref();
- re = sre;
- if (re == NULL) {
- if (options_.log_errors())
- LOG(ERROR) << "Error simplifying during Compile.";
- return false;
- }
+ re2::Regexp* re = re2::Regexp::Alternate(sub.data(), size_, pf);
- prog_ = Prog::CompileSet(options_, anchor_, re);
+ prog_ = Prog::CompileSet(re, anchor_, options_.max_mem());
+ re->Decref();
return prog_ != NULL;
}
-bool RE2::Set::Match(const StringPiece& text, vector<int>* v) const {
+bool RE2::Set::Match(const StringPiece& text, std::vector<int>* v) const {
+ return Match(text, v, NULL);
+}
+
+bool RE2::Set::Match(const StringPiece& text, std::vector<int>* v,
+ ErrorInfo* error_info) const {
if (!compiled_) {
- LOG(DFATAL) << "RE2::Set::Match without Compile";
+ LOG(DFATAL) << "RE2::Set::Match() called before compiling";
+ if (error_info != NULL)
+ error_info->kind = kNotCompiled;
return false;
}
- v->clear();
- bool failed;
- bool ret = prog_->SearchDFA(text, text, Prog::kAnchored,
- Prog::kManyMatch, NULL, &failed, v);
- if (failed)
- LOG(DFATAL) << "RE2::Set::Match: DFA ran out of cache space";
-
- if (ret == false)
+ bool dfa_failed = false;
+ std::unique_ptr<SparseSet> matches;
+ if (v != NULL) {
+ matches.reset(new SparseSet(size_));
+ v->clear();
+ }
+ bool ret = prog_->SearchDFA(text, text, Prog::kAnchored, Prog::kManyMatch,
+ NULL, &dfa_failed, matches.get());
+ if (dfa_failed) {
+ if (options_.log_errors())
+ LOG(ERROR) << "DFA out of memory: size " << prog_->size() << ", "
+ << "bytemap range " << prog_->bytemap_range() << ", "
+ << "list count " << prog_->list_count();
+ if (error_info != NULL)
+ error_info->kind = kOutOfMemory;
return false;
- if (v->size() == 0) {
- LOG(DFATAL) << "RE2::Set::Match: match but unknown regexp set";
+ }
+ if (ret == false) {
+ if (error_info != NULL)
+ error_info->kind = kNoError;
return false;
}
+ if (v != NULL) {
+ if (matches->empty()) {
+ LOG(DFATAL) << "RE2::Set::Match() matched, but no matches returned?!";
+ if (error_info != NULL)
+ error_info->kind = kInconsistent;
+ return false;
+ }
+ v->assign(matches->begin(), matches->end());
+ }
+ if (error_info != NULL)
+ error_info->kind = kNoError;
return true;
}
+
+} // namespace re2
diff --git a/re2/set.h b/re2/set.h
index d716425..a8c2caa 100644
--- a/re2/set.h
+++ b/re2/set.h
@@ -2,54 +2,79 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#ifndef RE2_SET_H
-#define RE2_SET_H
+#ifndef RE2_SET_H_
+#define RE2_SET_H_
+#include <string>
#include <utility>
#include <vector>
#include "re2/re2.h"
namespace re2 {
-using std::vector;
+class Prog;
+class Regexp;
+} // namespace re2
+
+namespace re2 {
// An RE2::Set represents a collection of regexps that can
// be searched for simultaneously.
class RE2::Set {
public:
+ enum ErrorKind {
+ kNoError = 0,
+ kNotCompiled, // The set is not compiled.
+ kOutOfMemory, // The DFA ran out of memory.
+ kInconsistent, // The result is inconsistent. This should never happen.
+ };
+
+ struct ErrorInfo {
+ ErrorKind kind;
+ };
+
Set(const RE2::Options& options, RE2::Anchor anchor);
~Set();
- // Add adds regexp pattern to the set, interpreted using the RE2 options.
- // (The RE2 constructor's default options parameter is RE2::UTF8.)
- // Add returns the regexp index that will be used to identify
- // it in the result of Match, or -1 if the regexp cannot be parsed.
+ // Adds pattern to the set using the options passed to the constructor.
+ // Returns the index that will identify the regexp in the output of Match(),
+ // or -1 if the regexp cannot be parsed.
// Indices are assigned in sequential order starting from 0.
- // Error returns do not increment the index.
- // If an error occurs and error != NULL, *error will hold an error message.
+ // Errors do not increment the index; if error is not NULL, *error will hold
+ // the error message from the parser.
int Add(const StringPiece& pattern, string* error);
- // Compile prepares the Set for matching.
- // Add must not be called again after Compile.
- // Compile must be called before FullMatch or PartialMatch.
- // Compile may return false if it runs out of memory.
+ // Compiles the set in preparation for matching.
+ // Returns false if the compiler runs out of memory.
+ // Add() must not be called again after Compile().
+ // Compile() must be called before Match().
bool Compile();
- // Match returns true if text matches any of the regexps in the set.
- // If so, it fills v with the indices of the matching regexps.
- bool Match(const StringPiece& text, vector<int>* v) const;
+ // Returns true if text matches at least one of the regexps in the set.
+ // Fills v (if not NULL) with the indices of the matching regexps.
+ // Callers must not expect v to be sorted.
+ bool Match(const StringPiece& text, std::vector<int>* v) const;
+
+ // As above, but populates error_info (if not NULL) when none of the regexps
+ // in the set matched. This can inform callers when DFA execution fails, for
+ // example, because they might wish to handle that case differently.
+ bool Match(const StringPiece& text, std::vector<int>* v,
+ ErrorInfo* error_info) const;
private:
+ typedef std::pair<string, re2::Regexp*> Elem;
+
RE2::Options options_;
RE2::Anchor anchor_;
- vector<re2::Regexp*> re_;
+ std::vector<Elem> elem_;
re2::Prog* prog_;
bool compiled_;
- //DISALLOW_EVIL_CONSTRUCTORS(Set);
- Set(const Set&);
- void operator=(const Set&);
+ int size_;
+
+ Set(const Set&) = delete;
+ Set& operator=(const Set&) = delete;
};
} // namespace re2
-#endif // RE2_SET_H
+#endif // RE2_SET_H_
diff --git a/re2/simplify.cc b/re2/simplify.cc
index faf3208..7cc0419 100644
--- a/re2/simplify.cc
+++ b/re2/simplify.cc
@@ -6,7 +6,12 @@
// to use simple extended regular expression features.
// Also sort and simplify character classes.
+#include <string>
+
#include "util/util.h"
+#include "util/logging.h"
+#include "util/pod_array.h"
+#include "util/utf.h"
#include "re2/regexp.h"
#include "re2/walker-inl.h"
@@ -61,7 +66,7 @@ bool Regexp::ComputeSimple() {
// These are simple as long as the subpieces are simple.
subs = sub();
for (int i = 0; i < nsub_; i++)
- if (!subs[i]->simple_)
+ if (!subs[i]->simple())
return false;
return true;
case kRegexpCharClass:
@@ -71,12 +76,12 @@ bool Regexp::ComputeSimple() {
return !cc_->empty() && !cc_->full();
case kRegexpCapture:
subs = sub();
- return subs[0]->simple_;
+ return subs[0]->simple();
case kRegexpStar:
case kRegexpPlus:
case kRegexpQuest:
subs = sub();
- if (!subs[0]->simple_)
+ if (!subs[0]->simple())
return false;
switch (subs[0]->op_) {
case kRegexpStar:
@@ -97,6 +102,37 @@ bool Regexp::ComputeSimple() {
}
// Walker subclass used by Simplify.
+// Coalesces runs of star/plus/quest/repeat of the same literal along with any
+// occurrences of that literal into repeats of that literal. It also works for
+// char classes, any char and any byte.
+// PostVisit creates the coalesced result, which should then be simplified.
+class CoalesceWalker : public Regexp::Walker<Regexp*> {
+ public:
+ CoalesceWalker() {}
+ virtual Regexp* PostVisit(Regexp* re, Regexp* parent_arg, Regexp* pre_arg,
+ Regexp** child_args, int nchild_args);
+ virtual Regexp* Copy(Regexp* re);
+ virtual Regexp* ShortVisit(Regexp* re, Regexp* parent_arg);
+
+ private:
+ // These functions are declared inside CoalesceWalker so that
+ // they can edit the private fields of the Regexps they construct.
+
+ // Returns true if r1 and r2 can be coalesced. In particular, ensures that
+ // the parse flags are consistent. (They will not be checked again later.)
+ static bool CanCoalesce(Regexp* r1, Regexp* r2);
+
+ // Coalesces *r1ptr and *r2ptr. In most cases, the array elements afterwards
+ // will be empty match and the coalesced op. In other cases, where part of a
+ // literal string was removed to be coalesced, the array elements afterwards
+ // will be the coalesced op and the remainder of the literal string.
+ static void DoCoalesce(Regexp** r1ptr, Regexp** r2ptr);
+
+ CoalesceWalker(const CoalesceWalker&) = delete;
+ CoalesceWalker& operator=(const CoalesceWalker&) = delete;
+};
+
+// Walker subclass used by Simplify.
// The simplify walk is purely post-recursive: given the simplified children,
// PostVisit creates the simplified result.
// The child_args are simplified Regexp*s.
@@ -104,9 +140,7 @@ class SimplifyWalker : public Regexp::Walker<Regexp*> {
public:
SimplifyWalker() {}
virtual Regexp* PreVisit(Regexp* re, Regexp* parent_arg, bool* stop);
- virtual Regexp* PostVisit(Regexp* re,
- Regexp* parent_arg,
- Regexp* pre_arg,
+ virtual Regexp* PostVisit(Regexp* re, Regexp* parent_arg, Regexp* pre_arg,
Regexp** child_args, int nchild_args);
virtual Regexp* Copy(Regexp* re);
virtual Regexp* ShortVisit(Regexp* re, Regexp* parent_arg);
@@ -130,7 +164,8 @@ class SimplifyWalker : public Regexp::Walker<Regexp*> {
// Caller must Decref return value when done with it.
static Regexp* SimplifyCharClass(Regexp* re);
- DISALLOW_EVIL_CONSTRUCTORS(SimplifyWalker);
+ SimplifyWalker(const SimplifyWalker&) = delete;
+ SimplifyWalker& operator=(const SimplifyWalker&) = delete;
};
// Simplifies a regular expression, returning a new regexp.
@@ -143,14 +178,261 @@ class SimplifyWalker : public Regexp::Walker<Regexp*> {
// Caller must Decref() return value when done with it.
Regexp* Regexp::Simplify() {
- if (simple_)
- return Incref();
- SimplifyWalker w;
- return w.Walk(this, NULL);
+ CoalesceWalker cw;
+ Regexp* cre = cw.Walk(this, NULL);
+ if (cre == NULL)
+ return cre;
+ SimplifyWalker sw;
+ Regexp* sre = sw.Walk(cre, NULL);
+ cre->Decref();
+ return sre;
}
#define Simplify DontCallSimplify // Avoid accidental recursion
+// Utility function for PostVisit implementations that compares re->sub() with
+// child_args to determine whether any child_args changed. In the common case,
+// where nothing changed, calls Decref() for all child_args and returns false,
+// so PostVisit must return re->Incref(). Otherwise, returns true.
+static bool ChildArgsChanged(Regexp* re, Regexp** child_args) {
+ for (int i = 0; i < re->nsub(); i++) {
+ Regexp* sub = re->sub()[i];
+ Regexp* newsub = child_args[i];
+ if (newsub != sub)
+ return true;
+ }
+ for (int i = 0; i < re->nsub(); i++) {
+ Regexp* newsub = child_args[i];
+ newsub->Decref();
+ }
+ return false;
+}
+
+Regexp* CoalesceWalker::Copy(Regexp* re) {
+ return re->Incref();
+}
+
+Regexp* CoalesceWalker::ShortVisit(Regexp* re, Regexp* parent_arg) {
+ // This should never be called, since we use Walk and not
+ // WalkExponential.
+ LOG(DFATAL) << "CoalesceWalker::ShortVisit called";
+ return re->Incref();
+}
+
+Regexp* CoalesceWalker::PostVisit(Regexp* re,
+ Regexp* parent_arg,
+ Regexp* pre_arg,
+ Regexp** child_args,
+ int nchild_args) {
+ if (re->nsub() == 0)
+ return re->Incref();
+
+ if (re->op() != kRegexpConcat) {
+ if (!ChildArgsChanged(re, child_args))
+ return re->Incref();
+
+ // Something changed. Build a new op.
+ Regexp* nre = new Regexp(re->op(), re->parse_flags());
+ nre->AllocSub(re->nsub());
+ Regexp** nre_subs = nre->sub();
+ for (int i = 0; i < re->nsub(); i++)
+ nre_subs[i] = child_args[i];
+ // Repeats and Captures have additional data that must be copied.
+ if (re->op() == kRegexpRepeat) {
+ nre->min_ = re->min();
+ nre->max_ = re->max();
+ } else if (re->op() == kRegexpCapture) {
+ nre->cap_ = re->cap();
+ }
+ return nre;
+ }
+
+ bool can_coalesce = false;
+ for (int i = 0; i < re->nsub(); i++) {
+ if (i+1 < re->nsub() &&
+ CanCoalesce(child_args[i], child_args[i+1])) {
+ can_coalesce = true;
+ break;
+ }
+ }
+ if (!can_coalesce) {
+ if (!ChildArgsChanged(re, child_args))
+ return re->Incref();
+
+ // Something changed. Build a new op.
+ Regexp* nre = new Regexp(re->op(), re->parse_flags());
+ nre->AllocSub(re->nsub());
+ Regexp** nre_subs = nre->sub();
+ for (int i = 0; i < re->nsub(); i++)
+ nre_subs[i] = child_args[i];
+ return nre;
+ }
+
+ for (int i = 0; i < re->nsub(); i++) {
+ if (i+1 < re->nsub() &&
+ CanCoalesce(child_args[i], child_args[i+1]))
+ DoCoalesce(&child_args[i], &child_args[i+1]);
+ }
+ // Determine how many empty matches were left by DoCoalesce.
+ int n = 0;
+ for (int i = n; i < re->nsub(); i++) {
+ if (child_args[i]->op() == kRegexpEmptyMatch)
+ n++;
+ }
+ // Build a new op.
+ Regexp* nre = new Regexp(re->op(), re->parse_flags());
+ nre->AllocSub(re->nsub() - n);
+ Regexp** nre_subs = nre->sub();
+ for (int i = 0, j = 0; i < re->nsub(); i++) {
+ if (child_args[i]->op() == kRegexpEmptyMatch) {
+ child_args[i]->Decref();
+ continue;
+ }
+ nre_subs[j] = child_args[i];
+ j++;
+ }
+ return nre;
+}
+
+bool CoalesceWalker::CanCoalesce(Regexp* r1, Regexp* r2) {
+ // r1 must be a star/plus/quest/repeat of a literal, char class, any char or
+ // any byte.
+ if ((r1->op() == kRegexpStar ||
+ r1->op() == kRegexpPlus ||
+ r1->op() == kRegexpQuest ||
+ r1->op() == kRegexpRepeat) &&
+ (r1->sub()[0]->op() == kRegexpLiteral ||
+ r1->sub()[0]->op() == kRegexpCharClass ||
+ r1->sub()[0]->op() == kRegexpAnyChar ||
+ r1->sub()[0]->op() == kRegexpAnyByte)) {
+ // r2 must be a star/plus/quest/repeat of the same literal, char class,
+ // any char or any byte.
+ if ((r2->op() == kRegexpStar ||
+ r2->op() == kRegexpPlus ||
+ r2->op() == kRegexpQuest ||
+ r2->op() == kRegexpRepeat) &&
+ Regexp::Equal(r1->sub()[0], r2->sub()[0]) &&
+ // The parse flags must be consistent.
+ ((r1->parse_flags() & Regexp::NonGreedy) ==
+ (r2->parse_flags() & Regexp::NonGreedy))) {
+ return true;
+ }
+ // ... OR an occurrence of that literal, char class, any char or any byte
+ if (Regexp::Equal(r1->sub()[0], r2)) {
+ return true;
+ }
+ // ... OR a literal string that begins with that literal.
+ if (r1->sub()[0]->op() == kRegexpLiteral &&
+ r2->op() == kRegexpLiteralString &&
+ r2->runes()[0] == r1->sub()[0]->rune() &&
+ // The parse flags must be consistent.
+ ((r1->sub()[0]->parse_flags() & Regexp::FoldCase) ==
+ (r2->parse_flags() & Regexp::FoldCase))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void CoalesceWalker::DoCoalesce(Regexp** r1ptr, Regexp** r2ptr) {
+ Regexp* r1 = *r1ptr;
+ Regexp* r2 = *r2ptr;
+
+ Regexp* nre = Regexp::Repeat(
+ r1->sub()[0]->Incref(), r1->parse_flags(), 0, 0);
+
+ switch (r1->op()) {
+ case kRegexpStar:
+ nre->min_ = 0;
+ nre->max_ = -1;
+ break;
+
+ case kRegexpPlus:
+ nre->min_ = 1;
+ nre->max_ = -1;
+ break;
+
+ case kRegexpQuest:
+ nre->min_ = 0;
+ nre->max_ = 1;
+ break;
+
+ case kRegexpRepeat:
+ nre->min_ = r1->min();
+ nre->max_ = r1->max();
+ break;
+
+ default:
+ LOG(DFATAL) << "DoCoalesce failed: r1->op() is " << r1->op();
+ nre->Decref();
+ return;
+ }
+
+ switch (r2->op()) {
+ case kRegexpStar:
+ nre->max_ = -1;
+ goto LeaveEmpty;
+
+ case kRegexpPlus:
+ nre->min_++;
+ nre->max_ = -1;
+ goto LeaveEmpty;
+
+ case kRegexpQuest:
+ if (nre->max() != -1)
+ nre->max_++;
+ goto LeaveEmpty;
+
+ case kRegexpRepeat:
+ nre->min_ += r2->min();
+ if (r2->max() == -1)
+ nre->max_ = -1;
+ else if (nre->max() != -1)
+ nre->max_ += r2->max();
+ goto LeaveEmpty;
+
+ case kRegexpLiteral:
+ case kRegexpCharClass:
+ case kRegexpAnyChar:
+ case kRegexpAnyByte:
+ nre->min_++;
+ if (nre->max() != -1)
+ nre->max_++;
+ goto LeaveEmpty;
+
+ LeaveEmpty:
+ *r1ptr = new Regexp(kRegexpEmptyMatch, Regexp::NoParseFlags);
+ *r2ptr = nre;
+ break;
+
+ case kRegexpLiteralString: {
+ Rune r = r1->sub()[0]->rune();
+ // Determine how much of the literal string is removed.
+ // We know that we have at least one rune. :)
+ int n = 1;
+ while (n < r2->nrunes() && r2->runes()[n] == r)
+ n++;
+ nre->min_ += n;
+ if (nre->max() != -1)
+ nre->max_ += n;
+ if (n == r2->nrunes())
+ goto LeaveEmpty;
+ *r1ptr = nre;
+ *r2ptr = Regexp::LiteralString(
+ &r2->runes()[n], r2->nrunes() - n, r2->parse_flags());
+ break;
+ }
+
+ default:
+ LOG(DFATAL) << "DoCoalesce failed: r2->op() is " << r2->op();
+ nre->Decref();
+ return;
+ }
+
+ r1->Decref();
+ r2->Decref();
+}
+
Regexp* SimplifyWalker::Copy(Regexp* re) {
return re->Incref();
}
@@ -163,7 +445,7 @@ Regexp* SimplifyWalker::ShortVisit(Regexp* re, Regexp* parent_arg) {
}
Regexp* SimplifyWalker::PreVisit(Regexp* re, Regexp* parent_arg, bool* stop) {
- if (re->simple_) {
+ if (re->simple()) {
*stop = true;
return re->Incref();
}
@@ -196,29 +478,14 @@ Regexp* SimplifyWalker::PostVisit(Regexp* re,
case kRegexpConcat:
case kRegexpAlternate: {
// These are simple as long as the subpieces are simple.
- // Two passes to avoid allocation in the common case.
- bool changed = false;
- Regexp** subs = re->sub();
- for (int i = 0; i < re->nsub_; i++) {
- Regexp* sub = subs[i];
- Regexp* newsub = child_args[i];
- if (newsub != sub) {
- changed = true;
- break;
- }
- }
- if (!changed) {
- for (int i = 0; i < re->nsub_; i++) {
- Regexp* newsub = child_args[i];
- newsub->Decref();
- }
+ if (!ChildArgsChanged(re, child_args)) {
re->simple_ = true;
return re->Incref();
}
Regexp* nre = new Regexp(re->op(), re->parse_flags());
- nre->AllocSub(re->nsub_);
+ nre->AllocSub(re->nsub());
Regexp** nre_subs = nre->sub();
- for (int i = 0; i <re->nsub_; i++)
+ for (int i = 0; i < re->nsub(); i++)
nre_subs[i] = child_args[i];
nre->simple_ = true;
return nre;
@@ -234,7 +501,7 @@ Regexp* SimplifyWalker::PostVisit(Regexp* re,
Regexp* nre = new Regexp(kRegexpCapture, re->parse_flags());
nre->AllocSub(1);
nre->sub()[0] = newsub;
- nre->cap_ = re->cap_;
+ nre->cap_ = re->cap();
nre->simple_ = true;
return nre;
}
@@ -323,14 +590,11 @@ Regexp* SimplifyWalker::SimplifyRepeat(Regexp* re, int min, int max,
return Regexp::Plus(re->Incref(), f);
// General case: x{4,} is xxxx+
- Regexp* nre = new Regexp(kRegexpConcat, f);
- nre->AllocSub(min);
- VLOG(1) << "Simplify " << min;
- Regexp** nre_subs = nre->sub();
+ PODArray<Regexp*> nre_subs(min);
for (int i = 0; i < min-1; i++)
nre_subs[i] = re->Incref();
nre_subs[min-1] = Regexp::Plus(re->Incref(), f);
- return nre;
+ return Regexp::Concat(nre_subs.data(), min, f);
}
// Special case: (x){0} matches only empty string.
@@ -348,11 +612,10 @@ Regexp* SimplifyWalker::SimplifyRepeat(Regexp* re, int min, int max,
// Build leading prefix: xx. Capturing only on the last one.
Regexp* nre = NULL;
if (min > 0) {
- nre = new Regexp(kRegexpConcat, f);
- nre->AllocSub(min);
- Regexp** nre_subs = nre->sub();
+ PODArray<Regexp*> nre_subs(min);
for (int i = 0; i < min; i++)
nre_subs[i] = re->Incref();
+ nre = Regexp::Concat(nre_subs.data(), min, f);
}
// Build and attach suffix: (x(x(x)?)?)?
diff --git a/re2/stringpiece.cc b/re2/stringpiece.cc
new file mode 100644
index 0000000..ef2e287
--- /dev/null
+++ b/re2/stringpiece.cc
@@ -0,0 +1,65 @@
+// Copyright 2004 The RE2 Authors. All Rights Reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "re2/stringpiece.h"
+
+#include <ostream>
+
+#include "util/util.h"
+
+namespace re2 {
+
+const StringPiece::size_type StringPiece::npos; // initialized in stringpiece.h
+
+StringPiece::size_type StringPiece::copy(char* buf, size_type n,
+ size_type pos) const {
+ size_type ret = std::min(size_ - pos, n);
+ memcpy(buf, data_ + pos, ret);
+ return ret;
+}
+
+StringPiece StringPiece::substr(size_type pos, size_type n) const {
+ if (pos > size_) pos = size_;
+ if (n > size_ - pos) n = size_ - pos;
+ return StringPiece(data_ + pos, n);
+}
+
+StringPiece::size_type StringPiece::find(const StringPiece& s,
+ size_type pos) const {
+ if (pos > size_) return npos;
+ const_pointer result = std::search(data_ + pos, data_ + size_,
+ s.data_, s.data_ + s.size_);
+ size_type xpos = result - data_;
+ return xpos + s.size_ <= size_ ? xpos : npos;
+}
+
+StringPiece::size_type StringPiece::find(char c, size_type pos) const {
+ if (size_ <= 0 || pos >= size_) return npos;
+ const_pointer result = std::find(data_ + pos, data_ + size_, c);
+ return result != data_ + size_ ? result - data_ : npos;
+}
+
+StringPiece::size_type StringPiece::rfind(const StringPiece& s,
+ size_type pos) const {
+ if (size_ < s.size_) return npos;
+ if (s.size_ == 0) return std::min(size_, pos);
+ const_pointer last = data_ + std::min(size_ - s.size_, pos) + s.size_;
+ const_pointer result = std::find_end(data_, last, s.data_, s.data_ + s.size_);
+ return result != last ? result - data_ : npos;
+}
+
+StringPiece::size_type StringPiece::rfind(char c, size_type pos) const {
+ if (size_ <= 0) return npos;
+ for (size_t i = std::min(pos + 1, size_); i != 0;) {
+ if (data_[--i] == c) return i;
+ }
+ return npos;
+}
+
+std::ostream& operator<<(std::ostream& o, const StringPiece& p) {
+ o.write(p.data(), p.size());
+ return o;
+}
+
+} // namespace re2
diff --git a/re2/stringpiece.h b/re2/stringpiece.h
index ab9297c..1d9c2d3 100644
--- a/re2/stringpiece.h
+++ b/re2/stringpiece.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_STRINGPIECE_H_
+#define RE2_STRINGPIECE_H_
+
// A string-like object that points to a sized piece of memory.
//
// Functions or methods may use const StringPiece& parameters to accept either
@@ -16,140 +19,165 @@
//
// Arghh! I wish C++ literals were "string".
-#ifndef STRINGS_STRINGPIECE_H__
-#define STRINGS_STRINGPIECE_H__
+// Doing this simplifies the logic below.
+#ifndef __has_include
+#define __has_include(x) 0
+#endif
+#include <stddef.h>
#include <string.h>
-#include <cstddef>
+#include <algorithm>
#include <iosfwd>
+#include <iterator>
#include <string>
+#if __has_include(<string_view>) && __cplusplus >= 201703L
+#include <string_view>
+#endif
namespace re2 {
class StringPiece {
- private:
- const char* ptr_;
- int length_;
-
public:
+ typedef std::char_traits<char> traits_type;
+ typedef char value_type;
+ typedef char* pointer;
+ typedef const char* const_pointer;
+ typedef char& reference;
+ typedef const char& const_reference;
+ typedef const char* const_iterator;
+ typedef const_iterator iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+ typedef const_reverse_iterator reverse_iterator;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ static const size_type npos = static_cast<size_type>(-1);
+
// We provide non-explicit singleton constructors so users can pass
// in a "const char*" or a "string" wherever a "StringPiece" is
// expected.
- StringPiece() : ptr_(NULL), length_(0) { }
- StringPiece(const char* str)
- : ptr_(str), length_((str == NULL) ? 0 : static_cast<int>(strlen(str))) { }
+ StringPiece()
+ : data_(NULL), size_(0) {}
+#if __has_include(<string_view>) && __cplusplus >= 201703L
+ StringPiece(const std::string_view& str)
+ : data_(str.data()), size_(str.size()) {}
+#endif
StringPiece(const std::string& str)
- : ptr_(str.data()), length_(static_cast<int>(str.size())) { }
- StringPiece(const char* offset, int len) : ptr_(offset), length_(len) { }
-
- // data() may return a pointer to a buffer with embedded NULs, and the
- // returned buffer may or may not be null terminated. Therefore it is
- // typically a mistake to pass data() to a routine that expects a NUL
- // terminated string.
- const char* data() const { return ptr_; }
- int size() const { return length_; }
- int length() const { return length_; }
- bool empty() const { return length_ == 0; }
-
- void clear() { ptr_ = NULL; length_ = 0; }
- void set(const char* data, int len) { ptr_ = data; length_ = len; }
- void set(const char* str) {
- ptr_ = str;
- if (str != NULL)
- length_ = static_cast<int>(strlen(str));
- else
- length_ = 0;
+ : data_(str.data()), size_(str.size()) {}
+ StringPiece(const char* str)
+ : data_(str), size_(str == NULL ? 0 : strlen(str)) {}
+ StringPiece(const char* str, size_type len)
+ : data_(str), size_(len) {}
+
+ const_iterator begin() const { return data_; }
+ const_iterator end() const { return data_ + size_; }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(data_ + size_);
}
- void set(const void* data, int len) {
- ptr_ = reinterpret_cast<const char*>(data);
- length_ = len;
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(data_);
}
- char operator[](int i) const { return ptr_[i]; }
+ size_type size() const { return size_; }
+ size_type length() const { return size_; }
+ bool empty() const { return size_ == 0; }
+
+ const_reference operator[](size_type i) const { return data_[i]; }
+ const_pointer data() const { return data_; }
- void remove_prefix(int n) {
- ptr_ += n;
- length_ -= n;
+ void remove_prefix(size_type n) {
+ data_ += n;
+ size_ -= n;
}
- void remove_suffix(int n) {
- length_ -= n;
+ void remove_suffix(size_type n) {
+ size_ -= n;
}
- int compare(const StringPiece& x) const {
- int r = memcmp(ptr_, x.ptr_, std::min(length_, x.length_));
- if (r == 0) {
- if (length_ < x.length_) r = -1;
- else if (length_ > x.length_) r = +1;
- }
- return r;
+ void set(const char* str) {
+ data_ = str;
+ size_ = str == NULL ? 0 : strlen(str);
+ }
+
+ void set(const char* str, size_type len) {
+ data_ = str;
+ size_ = len;
+ }
+
+ // Converts to `std::basic_string`.
+ template <typename A>
+ explicit operator std::basic_string<char, traits_type, A>() const {
+ if (!data_) return {};
+ return std::basic_string<char, traits_type, A>(data_, size_);
}
std::string as_string() const {
- return std::string(data(), size());
+ return std::string(data_, size_);
}
+
// We also define ToString() here, since many other string-like
// interfaces name the routine that converts to a C++ string
// "ToString", and it's confusing to have the method that does that
// for a StringPiece be called "as_string()". We also leave the
// "as_string()" method defined here for existing code.
std::string ToString() const {
- return std::string(data(), size());
+ return std::string(data_, size_);
}
- void CopyToString(std::string* target) const;
- void AppendToString(std::string* target) const;
+ void CopyToString(std::string* target) const {
+ target->assign(data_, size_);
+ }
- // Does "this" start with "x"
- bool starts_with(const StringPiece& x) const {
- return ((length_ >= x.length_) &&
- (memcmp(ptr_, x.ptr_, x.length_) == 0));
+ void AppendToString(std::string* target) const {
+ target->append(data_, size_);
}
- // Does "this" end with "x"
- bool ends_with(const StringPiece& x) const {
- return ((length_ >= x.length_) &&
- (memcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
+ size_type copy(char* buf, size_type n, size_type pos = 0) const;
+ StringPiece substr(size_type pos = 0, size_type n = npos) const;
+
+ int compare(const StringPiece& x) const {
+ size_type min_size = std::min(size(), x.size());
+ if (min_size > 0) {
+ int r = memcmp(data(), x.data(), min_size);
+ if (r < 0) return -1;
+ if (r > 0) return 1;
+ }
+ if (size() < x.size()) return -1;
+ if (size() > x.size()) return 1;
+ return 0;
}
- // standard STL container boilerplate
- typedef char value_type;
- typedef const char* pointer;
- typedef const char& reference;
- typedef const char& const_reference;
- typedef size_t size_type;
- typedef ptrdiff_t difference_type;
- static const size_type npos;
- typedef const char* const_iterator;
- typedef const char* iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
- typedef std::reverse_iterator<iterator> reverse_iterator;
- iterator begin() const { return ptr_; }
- iterator end() const { return ptr_ + length_; }
- const_reverse_iterator rbegin() const {
- return const_reverse_iterator(ptr_ + length_);
+ // Does "this" start with "x"?
+ bool starts_with(const StringPiece& x) const {
+ return x.empty() ||
+ (size() >= x.size() && memcmp(data(), x.data(), x.size()) == 0);
}
- const_reverse_iterator rend() const {
- return const_reverse_iterator(ptr_);
+
+ // Does "this" end with "x"?
+ bool ends_with(const StringPiece& x) const {
+ return x.empty() ||
+ (size() >= x.size() &&
+ memcmp(data() + (size() - x.size()), x.data(), x.size()) == 0);
}
- // STLS says return size_type, but Google says return int
- int max_size() const { return length_; }
- int capacity() const { return length_; }
- int copy(char* buf, size_type n, size_type pos = 0) const;
+ bool contains(const StringPiece& s) const {
+ return find(s) != npos;
+ }
- int find(const StringPiece& s, size_type pos = 0) const;
- int find(char c, size_type pos = 0) const;
- int rfind(const StringPiece& s, size_type pos = npos) const;
- int rfind(char c, size_type pos = npos) const;
+ size_type find(const StringPiece& s, size_type pos = 0) const;
+ size_type find(char c, size_type pos = 0) const;
+ size_type rfind(const StringPiece& s, size_type pos = npos) const;
+ size_type rfind(char c, size_type pos = npos) const;
- StringPiece substr(size_type pos, size_type n = npos) const;
-
- static bool _equal(const StringPiece&, const StringPiece&);
+ private:
+ const_pointer data_;
+ size_type size_;
};
inline bool operator==(const StringPiece& x, const StringPiece& y) {
- return StringPiece::_equal(x, y);
+ StringPiece::size_type len = x.size();
+ if (len != y.size()) return false;
+ return x.data() == y.data() || len == 0 ||
+ memcmp(x.data(), y.data(), len) == 0;
}
inline bool operator!=(const StringPiece& x, const StringPiece& y) {
@@ -157,9 +185,9 @@ inline bool operator!=(const StringPiece& x, const StringPiece& y) {
}
inline bool operator<(const StringPiece& x, const StringPiece& y) {
- const int r = memcmp(x.data(), y.data(),
- std::min(x.size(), y.size()));
- return ((r < 0) || ((r == 0) && (x.size() < y.size())));
+ StringPiece::size_type min_size = std::min(x.size(), y.size());
+ int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size);
+ return (r < 0) || (r == 0 && x.size() < y.size());
}
inline bool operator>(const StringPiece& x, const StringPiece& y) {
@@ -174,9 +202,9 @@ inline bool operator>=(const StringPiece& x, const StringPiece& y) {
return !(x < y);
}
-} // namespace re2
+// Allow StringPiece to be logged.
+std::ostream& operator<<(std::ostream& o, const StringPiece& p);
-// allow StringPiece to be logged
-extern std::ostream& operator<<(std::ostream& o, const re2::StringPiece& piece);
+} // namespace re2
-#endif // STRINGS_STRINGPIECE_H__
+#endif // RE2_STRINGPIECE_H_
diff --git a/re2/testing/backtrack.cc b/re2/testing/backtrack.cc
index b2dd6db..d535dd4 100644
--- a/re2/testing/backtrack.cc
+++ b/re2/testing/backtrack.cc
@@ -4,7 +4,7 @@
// Tested by search_test.cc, exhaustive_test.cc, tester.cc
//
-// Prog::BadSearchBacktrack is a backtracking regular expression search,
+// Prog::UnsafeSearchBacktrack is a backtracking regular expression search,
// except that it remembers where it has been, trading a lot of
// memory for a lot of time. It exists only for testing purposes.
//
@@ -23,7 +23,12 @@
// not the main library, in order to make it harder to pick up
// accidentally.
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
#include "util/util.h"
+#include "util/logging.h"
#include "re2/prog.h"
#include "re2/regexp.h"
@@ -55,10 +60,14 @@ class Backtracker {
StringPiece* submatch, int nsubmatch);
private:
- // Explores from instruction ip at string position p looking for a match.
+ // Explores from instruction id at string position p looking for a match.
// Returns true if found (so that caller can stop trying other possibilities).
bool Visit(int id, const char* p);
+ // Tries instruction id at string position p.
+ // Returns true if a match is found.
+ bool Try(int id, const char* p);
+
// Search parameters
Prog* prog_; // program being run
StringPiece text_; // text being searched
@@ -71,8 +80,8 @@ class Backtracker {
// Search state
const char* cap_[64]; // capture registers
- uint32 *visited_; // bitmap: (Inst*, char*) pairs already backtracked
- int nvisited_; // # of words in bitmap
+ uint32_t *visited_; // bitmap: (Inst*, char*) pairs already backtracked
+ size_t nvisited_; // # of words in bitmap
};
Backtracker::Backtracker(Prog* prog)
@@ -117,13 +126,13 @@ bool Backtracker::Search(const StringPiece& text, const StringPiece& context,
submatch_ = &sp0;
nsubmatch_ = 1;
}
- submatch_[0] = NULL;
+ submatch_[0] = StringPiece();
// Allocate new visited_ bitmap -- size is proportional
// to text, so have to reallocate on each call to Search.
delete[] visited_;
nvisited_ = (prog_->size()*(text.size()+1) + 31)/32;
- visited_ = new uint32[nvisited_];
+ visited_ = new uint32_t[nvisited_];
memset(visited_, 0, nvisited_*sizeof visited_[0]);
// Anchored search must start at text.begin().
@@ -143,19 +152,33 @@ bool Backtracker::Search(const StringPiece& text, const StringPiece& context,
return false;
}
-// Explores from instruction ip at string position p looking for a match.
+// Explores from instruction id at string position p looking for a match.
// Return true if found (so that caller can stop trying other possibilities).
bool Backtracker::Visit(int id, const char* p) {
// Check bitmap. If we've already explored from here,
// either it didn't match or it did but we're hoping for a better match.
// Either way, don't go down that road again.
CHECK(p <= text_.end());
- int n = id*(text_.size()+1) + (p - text_.begin());
+ size_t n = id*(text_.size()+1) + (p - text_.begin());
CHECK_LT(n/32, nvisited_);
if (visited_[n/32] & (1 << (n&31)))
return false;
visited_[n/32] |= 1 << (n&31);
+ Prog::Inst* ip = prog_->inst(id);
+ if (Try(id, p)) {
+ if (longest_ && !ip->last())
+ Visit(id+1, p);
+ return true;
+ }
+ if (!ip->last())
+ return Visit(id+1, p);
+ return false;
+}
+
+// Tries instruction id at string position p.
+// Returns true if a match is found.
+bool Backtracker::Try(int id, const char* p) {
// Pick out byte at current position. If at end of string,
// have to explore in hope of finishing a match. Use impossible byte -1.
int c = -1;
@@ -168,15 +191,9 @@ bool Backtracker::Visit(int id, const char* p) {
LOG(FATAL) << "Unexpected opcode: " << (int)ip->opcode();
return false; // not reached
- case kInstAlt:
case kInstAltMatch:
- // Try both possible next states: out is preferred to out1.
- if (Visit(ip->out(), p)) {
- if (longest_)
- Visit(ip->out1(), p);
- return true;
- }
- return Visit(ip->out1(), p);
+ // Ignored.
+ return false;
case kInstByteRange:
if (ip->Matches(c))
@@ -212,7 +229,8 @@ bool Backtracker::Visit(int id, const char* p) {
if (submatch_[0].data() == NULL || // First match so far ...
(longest_ && p > submatch_[0].end())) { // ... or better match
for (int i = 0; i < nsubmatch_; i++)
- submatch_[i] = StringPiece(cap_[2*i], cap_[2*i+1] - cap_[2*i]);
+ submatch_[i] = StringPiece(
+ cap_[2 * i], static_cast<size_t>(cap_[2 * i + 1] - cap_[2 * i]));
}
return true;
diff --git a/re2/testing/charclass_test.cc b/re2/testing/charclass_test.cc
index a3764d4..7e0169c 100644
--- a/re2/testing/charclass_test.cc
+++ b/re2/testing/charclass_test.cc
@@ -4,7 +4,10 @@
// Test character class manipulations.
+#include <stdio.h>
+
#include "util/test.h"
+#include "util/utf.h"
#include "re2/regexp.h"
namespace re2 {
diff --git a/re2/testing/compile_test.cc b/re2/testing/compile_test.cc
index 8d92105..d89d80f 100644
--- a/re2/testing/compile_test.cc
+++ b/re2/testing/compile_test.cc
@@ -5,13 +5,12 @@
// Test prog.cc, compile.cc
#include <string>
-#include <vector>
+
#include "util/test.h"
+#include "util/logging.h"
#include "re2/regexp.h"
#include "re2/prog.h"
-DEFINE_string(show, "", "regular expression to compile and dump");
-
namespace re2 {
// Simple input/output tests checking that
@@ -27,78 +26,89 @@ struct Test {
static Test tests[] = {
{ "a",
- "1. byte [61-61] -> 2\n"
- "2. match! 0\n" },
+ "3. byte [61-61] -> 4\n"
+ "4. match! 0\n" },
{ "ab",
- "1. byte [61-61] -> 2\n"
- "2. byte [62-62] -> 3\n"
- "3. match! 0\n" },
+ "3. byte [61-61] -> 4\n"
+ "4. byte [62-62] -> 5\n"
+ "5. match! 0\n" },
{ "a|c",
- "3. alt -> 1 | 2\n"
- "1. byte [61-61] -> 4\n"
- "2. byte [63-63] -> 4\n"
- "4. match! 0\n" },
+ "3+ byte [61-61] -> 5\n"
+ "4. byte [63-63] -> 5\n"
+ "5. match! 0\n" },
{ "a|b",
- "1. byte [61-62] -> 2\n"
- "2. match! 0\n" },
+ "3. byte [61-62] -> 4\n"
+ "4. match! 0\n" },
{ "[ab]",
- "1. byte [61-62] -> 2\n"
- "2. match! 0\n" },
+ "3. byte [61-62] -> 4\n"
+ "4. match! 0\n" },
{ "a+",
- "1. byte [61-61] -> 2\n"
- "2. alt -> 1 | 3\n"
- "3. match! 0\n" },
+ "3. byte [61-61] -> 4\n"
+ "4+ nop -> 3\n"
+ "5. match! 0\n" },
{ "a+?",
- "1. byte [61-61] -> 2\n"
- "2. alt -> 3 | 1\n"
- "3. match! 0\n" },
+ "3. byte [61-61] -> 4\n"
+ "4+ match! 0\n"
+ "5. nop -> 3\n" },
{ "a*",
- "2. alt -> 1 | 3\n"
- "1. byte [61-61] -> 2\n"
- "3. match! 0\n" },
+ "3+ byte [61-61] -> 3\n"
+ "4. match! 0\n" },
{ "a*?",
- "2. alt -> 3 | 1\n"
- "3. match! 0\n"
- "1. byte [61-61] -> 2\n" },
+ "3+ match! 0\n"
+ "4. byte [61-61] -> 3\n" },
{ "a?",
- "2. alt -> 1 | 3\n"
- "1. byte [61-61] -> 3\n"
- "3. match! 0\n" },
+ "3+ byte [61-61] -> 5\n"
+ "4. nop -> 5\n"
+ "5. match! 0\n" },
{ "a??",
- "2. alt -> 3 | 1\n"
- "3. match! 0\n"
- "1. byte [61-61] -> 3\n" },
+ "3+ nop -> 5\n"
+ "4. byte [61-61] -> 5\n"
+ "5. match! 0\n" },
{ "a{4}",
- "1. byte [61-61] -> 2\n"
- "2. byte [61-61] -> 3\n"
"3. byte [61-61] -> 4\n"
"4. byte [61-61] -> 5\n"
- "5. match! 0\n" },
+ "5. byte [61-61] -> 6\n"
+ "6. byte [61-61] -> 7\n"
+ "7. match! 0\n" },
{ "(a)",
- "2. capture 2 -> 1\n"
- "1. byte [61-61] -> 3\n"
- "3. capture 3 -> 4\n"
- "4. match! 0\n" },
+ "3. capture 2 -> 4\n"
+ "4. byte [61-61] -> 5\n"
+ "5. capture 3 -> 6\n"
+ "6. match! 0\n" },
{ "(?:a)",
- "1. byte [61-61] -> 2\n"
- "2. match! 0\n" },
+ "3. byte [61-61] -> 4\n"
+ "4. match! 0\n" },
{ "",
- "2. match! 0\n" },
+ "3. match! 0\n" },
{ ".",
- "3. alt -> 1 | 2\n"
- "1. byte [00-09] -> 4\n"
- "2. byte [0b-ff] -> 4\n"
- "4. match! 0\n" },
+ "3+ byte [00-09] -> 5\n"
+ "4. byte [0b-ff] -> 5\n"
+ "5. match! 0\n" },
{ "[^ab]",
- "5. alt -> 3 | 4\n"
- "3. alt -> 1 | 2\n"
- "4. byte [63-ff] -> 6\n"
- "1. byte [00-09] -> 6\n"
- "2. byte [0b-60] -> 6\n"
+ "3+ byte [00-09] -> 6\n"
+ "4+ byte [0b-60] -> 6\n"
+ "5. byte [63-ff] -> 6\n"
"6. match! 0\n" },
{ "[Aa]",
- "1. byte/i [61-61] -> 2\n"
- "2. match! 0\n" },
+ "3. byte/i [61-61] -> 4\n"
+ "4. match! 0\n" },
+ { "\\C+",
+ "3. byte [00-ff] -> 4\n"
+ "4+ altmatch -> 5 | 6\n"
+ "5+ nop -> 3\n"
+ "6. match! 0\n" },
+ { "\\C*",
+ "3+ altmatch -> 4 | 5\n"
+ "4+ byte [00-ff] -> 3\n"
+ "5. match! 0\n" },
+ { "\\C?",
+ "3+ byte [00-ff] -> 5\n"
+ "4. nop -> 5\n"
+ "5. match! 0\n" },
+ // Issue 20992936
+ { "[[-`]",
+ "3. byte [5b-60] -> 4\n"
+ "4. match! 0\n" },
};
TEST(TestRegexpCompileToProg, Simple) {
@@ -118,7 +128,7 @@ TEST(TestRegexpCompileToProg, Simple) {
failed++;
continue;
}
- CHECK(re->CompileToProg(1) == NULL);
+ ASSERT_TRUE(re->CompileToProg(1) == NULL);
string s = prog->Dump();
if (s != t.code) {
LOG(ERROR) << "Incorrect compiled code for: " << t.regexp;
@@ -132,40 +142,256 @@ TEST(TestRegexpCompileToProg, Simple) {
EXPECT_EQ(failed, 0);
}
-// The distinct byte ranges involved in the UTF-8 dot ([^\n]).
-// Once, erroneously split between 0x3f and 0x40 because it is
-// a 6-bit boundary.
-static struct UTF8ByteRange {
- int lo;
- int hi;
-} utf8ranges[] = {
- { 0x00, 0x09 },
- { 0x0A, 0x0A },
- { 0x10, 0x7F },
- { 0x80, 0x8F },
- { 0x90, 0x9F },
- { 0xA0, 0xBF },
- { 0xC0, 0xC1 },
- { 0xC2, 0xDF },
- { 0xE0, 0xE0 },
- { 0xE1, 0xEF },
- { 0xF0, 0xF0 },
- { 0xF1, 0xF3 },
- { 0xF4, 0xF4 },
- { 0xF5, 0xFF },
-};
-
-TEST(TestCompile, ByteRanges) {
- Regexp* re = Regexp::Parse(".", Regexp::PerlX, NULL);
+static void DumpByteMap(StringPiece pattern, Regexp::ParseFlags flags,
+ string* bytemap) {
+ Regexp* re = Regexp::Parse(pattern, flags, NULL);
EXPECT_TRUE(re != NULL);
+
Prog* prog = re->CompileToProg(0);
EXPECT_TRUE(prog != NULL);
- EXPECT_EQ(prog->bytemap_range(), arraysize(utf8ranges));
- for (int i = 0; i < arraysize(utf8ranges); i++)
- for (int j = utf8ranges[i].lo; j <= utf8ranges[i].hi; j++)
- EXPECT_EQ(prog->bytemap()[j], i) << " byte " << j;
+ *bytemap = prog->DumpByteMap();
delete prog;
+
re->Decref();
}
+TEST(TestCompile, Latin1Ranges) {
+ // The distinct byte ranges involved in the Latin-1 dot ([^\n]).
+
+ string bytemap;
+
+ DumpByteMap(".", Regexp::PerlX|Regexp::Latin1, &bytemap);
+ EXPECT_EQ("[00-09] -> 0\n"
+ "[0a-0a] -> 1\n"
+ "[0b-ff] -> 0\n",
+ bytemap);
+}
+
+TEST(TestCompile, OtherByteMapTests) {
+ string bytemap;
+
+ // Test that "absent" ranges are mapped to the same byte class.
+ DumpByteMap("[0-9A-Fa-f]+", Regexp::PerlX|Regexp::Latin1, &bytemap);
+ EXPECT_EQ("[00-2f] -> 0\n"
+ "[30-39] -> 1\n"
+ "[3a-40] -> 0\n"
+ "[41-46] -> 1\n"
+ "[47-60] -> 0\n"
+ "[61-66] -> 1\n"
+ "[67-ff] -> 0\n",
+ bytemap);
+
+ // Test the byte classes for \b.
+ DumpByteMap("\\b", Regexp::LikePerl|Regexp::Latin1, &bytemap);
+ EXPECT_EQ("[00-2f] -> 0\n"
+ "[30-39] -> 1\n"
+ "[3a-40] -> 0\n"
+ "[41-5a] -> 1\n"
+ "[5b-5e] -> 0\n"
+ "[5f-5f] -> 1\n"
+ "[60-60] -> 0\n"
+ "[61-7a] -> 1\n"
+ "[7b-ff] -> 0\n",
+ bytemap);
+
+ // Bug in the ASCII case-folding optimization created too many byte classes.
+ DumpByteMap("[^_]", Regexp::LikePerl|Regexp::Latin1, &bytemap);
+ EXPECT_EQ("[00-5e] -> 0\n"
+ "[5f-5f] -> 1\n"
+ "[60-ff] -> 0\n",
+ bytemap);
+}
+
+TEST(TestCompile, UTF8Ranges) {
+ // The distinct byte ranges involved in the UTF-8 dot ([^\n]).
+ // Once, erroneously split between 0x3f and 0x40 because it is
+ // a 6-bit boundary.
+
+ string bytemap;
+
+ DumpByteMap(".", Regexp::PerlX, &bytemap);
+ EXPECT_EQ("[00-09] -> 0\n"
+ "[0a-0a] -> 1\n"
+ "[0b-7f] -> 0\n"
+ "[80-8f] -> 2\n"
+ "[90-9f] -> 3\n"
+ "[a0-bf] -> 4\n"
+ "[c0-c1] -> 1\n"
+ "[c2-df] -> 5\n"
+ "[e0-e0] -> 6\n"
+ "[e1-ef] -> 7\n"
+ "[f0-f0] -> 8\n"
+ "[f1-f3] -> 9\n"
+ "[f4-f4] -> 10\n"
+ "[f5-ff] -> 1\n",
+ bytemap);
+}
+
+TEST(TestCompile, InsufficientMemory) {
+ Regexp* re = Regexp::Parse(
+ "^(?P<name1>[^\\s]+)\\s+(?P<name2>[^\\s]+)\\s+(?P<name3>.+)$",
+ Regexp::LikePerl, NULL);
+ EXPECT_TRUE(re != NULL);
+ Prog* prog = re->CompileToProg(920);
+ // If the memory budget has been exhausted, compilation should fail
+ // and return NULL instead of trying to do anything with NoMatch().
+ EXPECT_TRUE(prog == NULL);
+ re->Decref();
+}
+
+static void Dump(StringPiece pattern, Regexp::ParseFlags flags,
+ string* forward, string* reverse) {
+ Regexp* re = Regexp::Parse(pattern, flags, NULL);
+ EXPECT_TRUE(re != NULL);
+
+ if (forward != NULL) {
+ Prog* prog = re->CompileToProg(0);
+ EXPECT_TRUE(prog != NULL);
+ *forward = prog->Dump();
+ delete prog;
+ }
+
+ if (reverse != NULL) {
+ Prog* prog = re->CompileToReverseProg(0);
+ EXPECT_TRUE(prog != NULL);
+ *reverse = prog->Dump();
+ delete prog;
+ }
+
+ re->Decref();
+}
+
+TEST(TestCompile, Bug26705922) {
+ // Bug in the compiler caused inefficient bytecode to be generated for Unicode
+ // groups: common suffixes were cached, but common prefixes were not factored.
+
+ string forward, reverse;
+
+ Dump("[\\x{10000}\\x{10010}]", Regexp::LikePerl, &forward, &reverse);
+ EXPECT_EQ("3. byte [f0-f0] -> 4\n"
+ "4. byte [90-90] -> 5\n"
+ "5. byte [80-80] -> 6\n"
+ "6+ byte [80-80] -> 8\n"
+ "7. byte [90-90] -> 8\n"
+ "8. match! 0\n",
+ forward);
+ EXPECT_EQ("3+ byte [80-80] -> 5\n"
+ "4. byte [90-90] -> 5\n"
+ "5. byte [80-80] -> 6\n"
+ "6. byte [90-90] -> 7\n"
+ "7. byte [f0-f0] -> 8\n"
+ "8. match! 0\n",
+ reverse);
+
+ Dump("[\\x{8000}-\\x{10FFF}]", Regexp::LikePerl, &forward, &reverse);
+ EXPECT_EQ("3+ byte [e8-ef] -> 5\n"
+ "4. byte [f0-f0] -> 8\n"
+ "5. byte [80-bf] -> 6\n"
+ "6. byte [80-bf] -> 7\n"
+ "7. match! 0\n"
+ "8. byte [90-90] -> 5\n",
+ forward);
+ EXPECT_EQ("3. byte [80-bf] -> 4\n"
+ "4. byte [80-bf] -> 5\n"
+ "5+ byte [e8-ef] -> 7\n"
+ "6. byte [90-90] -> 8\n"
+ "7. match! 0\n"
+ "8. byte [f0-f0] -> 7\n",
+ reverse);
+
+ Dump("[\\x{80}-\\x{10FFFF}]", Regexp::LikePerl, NULL, &reverse);
+ EXPECT_EQ("3. byte [80-bf] -> 4\n"
+ "4+ byte [c2-df] -> 7\n"
+ "5+ byte [a0-bf] -> 8\n"
+ "6. byte [80-bf] -> 9\n"
+ "7. match! 0\n"
+ "8. byte [e0-e0] -> 7\n"
+ "9+ byte [e1-ef] -> 7\n"
+ "10+ byte [90-bf] -> 13\n"
+ "11+ byte [80-bf] -> 14\n"
+ "12. byte [80-8f] -> 15\n"
+ "13. byte [f0-f0] -> 7\n"
+ "14. byte [f1-f3] -> 7\n"
+ "15. byte [f4-f4] -> 7\n",
+ reverse);
+}
+
+TEST(TestCompile, Bug35237384) {
+ // Bug in the compiler caused inefficient bytecode to be generated for
+ // nested nullable subexpressions.
+
+ string forward;
+
+ Dump("a**{3,}", Regexp::Latin1|Regexp::NeverCapture, &forward, NULL);
+ EXPECT_EQ("3+ byte [61-61] -> 3\n"
+ "4. nop -> 5\n"
+ "5+ byte [61-61] -> 5\n"
+ "6. nop -> 7\n"
+ "7+ byte [61-61] -> 7\n"
+ "8. match! 0\n",
+ forward);
+
+ Dump("(a*|b*)*{3,}", Regexp::Latin1|Regexp::NeverCapture, &forward, NULL);
+ EXPECT_EQ("3+ nop -> 6\n"
+ "4+ nop -> 8\n"
+ "5. nop -> 21\n"
+ "6+ byte [61-61] -> 6\n"
+ "7. nop -> 3\n"
+ "8+ byte [62-62] -> 8\n"
+ "9. nop -> 3\n"
+ "10+ byte [61-61] -> 10\n"
+ "11. nop -> 21\n"
+ "12+ byte [62-62] -> 12\n"
+ "13. nop -> 21\n"
+ "14+ byte [61-61] -> 14\n"
+ "15. nop -> 18\n"
+ "16+ byte [62-62] -> 16\n"
+ "17. nop -> 18\n"
+ "18+ nop -> 14\n"
+ "19+ nop -> 16\n"
+ "20. match! 0\n"
+ "21+ nop -> 10\n"
+ "22+ nop -> 12\n"
+ "23. nop -> 18\n",
+ forward);
+
+ Dump("((|S.+)+|(|S.+)+|){2}", Regexp::Latin1|Regexp::NeverCapture, &forward, NULL);
+ EXPECT_EQ("3+ nop -> 36\n"
+ "4+ nop -> 31\n"
+ "5. nop -> 33\n"
+ "6+ byte [00-09] -> 8\n"
+ "7. byte [0b-ff] -> 8\n"
+ "8+ nop -> 6\n"
+ "9+ nop -> 29\n"
+ "10. nop -> 28\n"
+ "11+ byte [00-09] -> 13\n"
+ "12. byte [0b-ff] -> 13\n"
+ "13+ nop -> 11\n"
+ "14+ nop -> 26\n"
+ "15. nop -> 28\n"
+ "16+ byte [00-09] -> 18\n"
+ "17. byte [0b-ff] -> 18\n"
+ "18+ nop -> 16\n"
+ "19+ nop -> 36\n"
+ "20. nop -> 33\n"
+ "21+ byte [00-09] -> 23\n"
+ "22. byte [0b-ff] -> 23\n"
+ "23+ nop -> 21\n"
+ "24+ nop -> 31\n"
+ "25. nop -> 33\n"
+ "26+ nop -> 28\n"
+ "27. byte [53-53] -> 11\n"
+ "28. match! 0\n"
+ "29+ nop -> 28\n"
+ "30. byte [53-53] -> 6\n"
+ "31+ nop -> 33\n"
+ "32. byte [53-53] -> 21\n"
+ "33+ nop -> 29\n"
+ "34+ nop -> 26\n"
+ "35. nop -> 28\n"
+ "36+ nop -> 33\n"
+ "37. byte [53-53] -> 16\n",
+ forward);
+}
+
} // namespace re2
diff --git a/re2/testing/dfa_test.cc b/re2/testing/dfa_test.cc
index 8e95ae4..eb44b4a 100644
--- a/re2/testing/dfa_test.cc
+++ b/re2/testing/dfa_test.cc
@@ -2,15 +2,21 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include <stdint.h>
+#include <string>
+#include <thread>
+#include <vector>
+
#include "util/test.h"
-#include "util/thread.h"
+#include "util/logging.h"
+#include "util/strutil.h"
#include "re2/prog.h"
#include "re2/re2.h"
#include "re2/regexp.h"
#include "re2/testing/regexp_generator.h"
#include "re2/testing/string_generator.h"
-DECLARE_bool(re2_dfa_bail_when_slow);
+static const bool UsingMallocCounter = false;
DEFINE_int32(size, 8, "log2(number of DFA nodes)");
DEFINE_int32(repeat, 2, "Repetition count.");
@@ -20,17 +26,10 @@ namespace re2 {
// Check that multithreaded access to DFA class works.
-// Helper thread: builds entire DFA for prog.
-class BuildThread : public Thread {
- public:
- BuildThread(Prog* prog) : prog_(prog) {}
- virtual void Run() {
- CHECK(prog_->BuildEntireDFA(Prog::kFirstMatch));
- }
-
- private:
- Prog* prog_;
-};
+// Helper function: builds entire DFA for prog.
+static void DoBuild(Prog* prog) {
+ ASSERT_TRUE(prog->BuildEntireDFA(Prog::kFirstMatch, nullptr));
+}
TEST(Multithreaded, BuildEntireDFA) {
// Create regexp with 2^FLAGS_size states in DFA.
@@ -38,48 +37,37 @@ TEST(Multithreaded, BuildEntireDFA) {
for (int i = 0; i < FLAGS_size; i++)
s += "[ab]";
s += "b";
+ Regexp* re = Regexp::Parse(s, Regexp::LikePerl, NULL);
+ ASSERT_TRUE(re != NULL);
// Check that single-threaded code works.
{
- //LOG(INFO) << s;
- Regexp* re = Regexp::Parse(s.c_str(), Regexp::LikePerl, NULL);
- CHECK(re);
Prog* prog = re->CompileToProg(0);
- CHECK(prog);
- BuildThread* t = new BuildThread(prog);
- t->SetJoinable(true);
- t->Start();
- t->Join();
- delete t;
+ ASSERT_TRUE(prog != NULL);
+
+ std::thread t(DoBuild, prog);
+ t.join();
+
delete prog;
- re->Decref();
}
// Build the DFA simultaneously in a bunch of threads.
for (int i = 0; i < FLAGS_repeat; i++) {
- Regexp* re = Regexp::Parse(s.c_str(), Regexp::LikePerl, NULL);
- CHECK(re);
Prog* prog = re->CompileToProg(0);
- CHECK(prog);
+ ASSERT_TRUE(prog != NULL);
- vector<BuildThread*> threads;
- for (int j = 0; j < FLAGS_threads; j++) {
- BuildThread *t = new BuildThread(prog);
- t->SetJoinable(true);
- threads.push_back(t);
- }
+ std::vector<std::thread> threads;
for (int j = 0; j < FLAGS_threads; j++)
- threads[j]->Start();
- for (int j = 0; j < FLAGS_threads; j++) {
- threads[j]->Join();
- delete threads[j];
- }
+ threads.emplace_back(DoBuild, prog);
+ for (int j = 0; j < FLAGS_threads; j++)
+ threads[j].join();
// One more compile, to make sure everything is okay.
- prog->BuildEntireDFA(Prog::kFirstMatch);
+ prog->BuildEntireDFA(Prog::kFirstMatch, nullptr);
delete prog;
- re->Decref();
}
+
+ re->Decref();
}
// Check that DFA size requirements are followed.
@@ -87,36 +75,33 @@ TEST(Multithreaded, BuildEntireDFA) {
// the DFA once the memory limits are reached.
TEST(SingleThreaded, BuildEntireDFA) {
// Create regexp with 2^30 states in DFA.
- string s = "a";
- for (int i = 0; i < 30; i++)
- s += "[ab]";
- s += "b";
+ Regexp* re = Regexp::Parse("a[ab]{30}b", Regexp::LikePerl, NULL);
+ ASSERT_TRUE(re != NULL);
- //LOG(INFO) << s;
- Regexp* re = Regexp::Parse(s.c_str(), Regexp::LikePerl, NULL);
- CHECK(re);
- int max = 24;
- for (int i = 17; i < max; i++) {
- int limit = 1<<i;
- int usage;
- //int progusage, dfamem;
+ for (int i = 17; i < 24; i++) {
+ int64_t limit = int64_t{1}<<i;
+ int64_t usage;
+ //int64_t progusage, dfamem;
{
testing::MallocCounter m(testing::MallocCounter::THIS_THREAD_ONLY);
Prog* prog = re->CompileToProg(limit);
- CHECK(prog);
+ ASSERT_TRUE(prog != NULL);
//progusage = m.HeapGrowth();
//dfamem = prog->dfa_mem();
- prog->BuildEntireDFA(Prog::kFirstMatch);
- prog->BuildEntireDFA(Prog::kLongestMatch);
+ prog->BuildEntireDFA(Prog::kFirstMatch, nullptr);
+ prog->BuildEntireDFA(Prog::kLongestMatch, nullptr);
usage = m.HeapGrowth();
delete prog;
}
- if (!UsingMallocCounter)
- continue;
- //LOG(INFO) << StringPrintf("Limit %d: prog used %d, DFA budget %d, total %d\n",
- // limit, progusage, dfamem, usage);
- CHECK_GT(usage, limit*9/10);
- CHECK_LT(usage, limit + (16<<10)); // 16kB of slop okay
+ if (UsingMallocCounter) {
+ //LOG(INFO) << "limit " << limit << ", "
+ // << "prog usage " << progusage << ", "
+ // << "DFA budget " << dfamem << ", "
+ // << "total " << usage;
+ // Tolerate +/- 10%.
+ ASSERT_GT(usage, limit*9/10);
+ ASSERT_LT(usage, limit*11/10);
+ }
}
re->Decref();
}
@@ -132,10 +117,10 @@ TEST(SingleThreaded, BuildEntireDFA) {
// position in the input, never reusing any states until it gets to the
// end of the string. This is the worst possible case for DFA execution.
static string DeBruijnString(int n) {
- CHECK_LT(n, 8*sizeof(int));
+ CHECK_LT(n, static_cast<int>(8*sizeof(int)));
CHECK_GT(n, 0);
- vector<bool> did(1<<n);
+ std::vector<bool> did(size_t{1}<<n);
for (int i = 0; i < 1<<n; i++)
did[i] = false;
@@ -174,6 +159,14 @@ static string DeBruijnString(int n) {
// 2^n byte limit, it must be handling out-of-memory conditions
// gracefully.
TEST(SingleThreaded, SearchDFA) {
+ // The De Bruijn string is the worst case input for this regexp.
+ // By default, the DFA will notice that it is flushing its cache
+ // too frequently and will bail out early, so that RE2 can use the
+ // NFA implementation instead. (The DFA loses its speed advantage
+ // if it can't get a good cache hit rate.)
+ // Tell the DFA to trudge along instead.
+ Prog::TEST_dfa_should_bail_when_slow(false);
+
// Choice of n is mostly arbitrary, except that:
// * making n too big makes the test run for too long.
// * making n too small makes the DFA refuse to run,
@@ -183,132 +176,111 @@ TEST(SingleThreaded, SearchDFA) {
Regexp* re = Regexp::Parse(StringPrintf("0[01]{%d}$", n),
Regexp::LikePerl, NULL);
- CHECK(re);
+ ASSERT_TRUE(re != NULL);
// The De Bruijn string for n ends with a 1 followed by n 0s in a row,
// which is not a match for 0[01]{n}$. Adding one more 0 is a match.
string no_match = DeBruijnString(n);
string match = no_match + "0";
- // The De Bruijn string is the worst case input for this regexp.
- // By default, the DFA will notice that it is flushing its cache
- // too frequently and will bail out early, so that RE2 can use the
- // NFA implementation instead. (The DFA loses its speed advantage
- // if it can't get a good cache hit rate.)
- // Tell the DFA to trudge along instead.
- FLAGS_re2_dfa_bail_when_slow = false;
-
- int64 usage;
- int64 peak_usage;
+ int64_t usage;
+ int64_t peak_usage;
{
testing::MallocCounter m(testing::MallocCounter::THIS_THREAD_ONLY);
Prog* prog = re->CompileToProg(1<<n);
- CHECK(prog);
+ ASSERT_TRUE(prog != NULL);
for (int i = 0; i < 10; i++) {
- bool matched, failed = false;
- matched = prog->SearchDFA(match, NULL,
- Prog::kUnanchored, Prog::kFirstMatch,
- NULL, &failed, NULL);
- CHECK(!failed);
- CHECK(matched);
- matched = prog->SearchDFA(no_match, NULL,
- Prog::kUnanchored, Prog::kFirstMatch,
- NULL, &failed, NULL);
- CHECK(!failed);
- CHECK(!matched);
+ bool matched = false;
+ bool failed = false;
+ matched = prog->SearchDFA(match, StringPiece(), Prog::kUnanchored,
+ Prog::kFirstMatch, NULL, &failed, NULL);
+ ASSERT_FALSE(failed);
+ ASSERT_TRUE(matched);
+ matched = prog->SearchDFA(no_match, StringPiece(), Prog::kUnanchored,
+ Prog::kFirstMatch, NULL, &failed, NULL);
+ ASSERT_FALSE(failed);
+ ASSERT_FALSE(matched);
}
usage = m.HeapGrowth();
peak_usage = m.PeakHeapGrowth();
delete prog;
}
+ if (UsingMallocCounter) {
+ //LOG(INFO) << "usage " << usage << ", "
+ // << "peak usage " << peak_usage;
+ ASSERT_LT(usage, 1<<n);
+ ASSERT_LT(peak_usage, 1<<n);
+ }
re->Decref();
- if (!UsingMallocCounter)
- return;
- //LOG(INFO) << "usage " << usage << " " << peak_usage;
- CHECK_LT(usage, 1<<n);
- CHECK_LT(peak_usage, 1<<n);
+ // Reset to original behaviour.
+ Prog::TEST_dfa_should_bail_when_slow(true);
}
-// Helper thread: searches for match, which should match,
+// Helper function: searches for match, which should match,
// and no_match, which should not.
-class SearchThread : public Thread {
- public:
- SearchThread(Prog* prog, const StringPiece& match,
- const StringPiece& no_match)
- : prog_(prog), match_(match), no_match_(no_match) {}
-
- virtual void Run() {
- for (int i = 0; i < 2; i++) {
- bool matched, failed = false;
- matched = prog_->SearchDFA(match_, NULL,
- Prog::kUnanchored, Prog::kFirstMatch,
- NULL, &failed, NULL);
- CHECK(!failed);
- CHECK(matched);
- matched = prog_->SearchDFA(no_match_, NULL,
- Prog::kUnanchored, Prog::kFirstMatch,
- NULL, &failed, NULL);
- CHECK(!failed);
- CHECK(!matched);
- }
+static void DoSearch(Prog* prog, const StringPiece& match,
+ const StringPiece& no_match) {
+ for (int i = 0; i < 2; i++) {
+ bool matched = false;
+ bool failed = false;
+ matched = prog->SearchDFA(match, StringPiece(), Prog::kUnanchored,
+ Prog::kFirstMatch, NULL, &failed, NULL);
+ ASSERT_FALSE(failed);
+ ASSERT_TRUE(matched);
+ matched = prog->SearchDFA(no_match, StringPiece(), Prog::kUnanchored,
+ Prog::kFirstMatch, NULL, &failed, NULL);
+ ASSERT_FALSE(failed);
+ ASSERT_FALSE(matched);
}
-
- private:
- Prog* prog_;
- StringPiece match_;
- StringPiece no_match_;
-};
+}
TEST(Multithreaded, SearchDFA) {
+ Prog::TEST_dfa_should_bail_when_slow(false);
+
// Same as single-threaded test above.
const int n = 18;
Regexp* re = Regexp::Parse(StringPrintf("0[01]{%d}$", n),
Regexp::LikePerl, NULL);
- CHECK(re);
+ ASSERT_TRUE(re != NULL);
string no_match = DeBruijnString(n);
string match = no_match + "0";
- FLAGS_re2_dfa_bail_when_slow = false;
// Check that single-threaded code works.
{
Prog* prog = re->CompileToProg(1<<n);
- CHECK(prog);
- SearchThread* t = new SearchThread(prog, match, no_match);
- t->SetJoinable(true);
- t->Start();
- t->Join();
- delete t;
+ ASSERT_TRUE(prog != NULL);
+
+ std::thread t(DoSearch, prog, match, no_match);
+ t.join();
+
delete prog;
}
// Run the search simultaneously in a bunch of threads.
// Reuse same flags for Multithreaded.BuildDFA above.
for (int i = 0; i < FLAGS_repeat; i++) {
- //LOG(INFO) << "Search " << i;
Prog* prog = re->CompileToProg(1<<n);
- CHECK(prog);
+ ASSERT_TRUE(prog != NULL);
- vector<SearchThread*> threads;
- for (int j = 0; j < FLAGS_threads; j++) {
- SearchThread *t = new SearchThread(prog, match, no_match);
- t->SetJoinable(true);
- threads.push_back(t);
- }
+ std::vector<std::thread> threads;
for (int j = 0; j < FLAGS_threads; j++)
- threads[j]->Start();
- for (int j = 0; j < FLAGS_threads; j++) {
- threads[j]->Join();
- delete threads[j];
- }
+ threads.emplace_back(DoSearch, prog, match, no_match);
+ for (int j = 0; j < FLAGS_threads; j++)
+ threads[j].join();
+
delete prog;
}
+
re->Decref();
+
+ // Reset to original behaviour.
+ Prog::TEST_dfa_should_bail_when_slow(true);
}
struct ReverseTest {
- const char *regexp;
- const char *text;
+ const char* regexp;
+ const char* text;
bool match;
};
@@ -326,11 +298,12 @@ TEST(DFA, ReverseMatch) {
for (int i = 0; i < arraysize(reverse_tests); i++) {
const ReverseTest& t = reverse_tests[i];
Regexp* re = Regexp::Parse(t.regexp, Regexp::LikePerl, NULL);
- CHECK(re);
- Prog *prog = re->CompileToReverseProg(0);
- CHECK(prog);
+ ASSERT_TRUE(re != NULL);
+ Prog* prog = re->CompileToReverseProg(0);
+ ASSERT_TRUE(prog != NULL);
bool failed = false;
- bool matched = prog->SearchDFA(t.text, NULL, Prog::kUnanchored, Prog::kFirstMatch, NULL, &failed, NULL);
+ bool matched = prog->SearchDFA(t.text, StringPiece(), Prog::kUnanchored,
+ Prog::kFirstMatch, NULL, &failed, NULL);
if (matched != t.match) {
LOG(ERROR) << t.regexp << " on " << t.text << ": want " << t.match;
nfail++;
@@ -341,4 +314,68 @@ TEST(DFA, ReverseMatch) {
EXPECT_EQ(nfail, 0);
}
+struct CallbackTest {
+ const char* regexp;
+ const char* dump;
+};
+
+// Test that DFA::BuildAllStates() builds the expected DFA states
+// and issues the expected callbacks. These test cases reflect the
+// very compact encoding of the callbacks, but that also makes them
+// very difficult to understand, so let's work through "\\Aa\\z".
+// There are three slots per DFA state because the bytemap has two
+// equivalence classes and there is a third slot for kByteEndText:
+// 0: all bytes that are not 'a'
+// 1: the byte 'a'
+// 2: kByteEndText
+// -1 means that there is no transition from that DFA state to any
+// other DFA state for that slot. The valid transitions are thus:
+// state 0 --slot 1--> state 1
+// state 1 --slot 2--> state 2
+// The double brackets indicate that state 2 is a matching state.
+// Putting it together, this means that the DFA must consume the
+// byte 'a' and then hit end of text. Q.E.D.
+CallbackTest callback_tests[] = {
+ { "\\Aa\\z", "[-1,1,-1] [-1,-1,2] [[-1,-1,-1]]" },
+ { "\\Aab\\z", "[-1,1,-1,-1] [-1,-1,2,-1] [-1,-1,-1,3] [[-1,-1,-1,-1]]" },
+ { "\\Aa*b\\z", "[-1,0,1,-1] [-1,-1,-1,2] [[-1,-1,-1,-1]]" },
+ { "\\Aa+b\\z", "[-1,1,-1,-1] [-1,1,2,-1] [-1,-1,-1,3] [[-1,-1,-1,-1]]" },
+ { "\\Aa?b\\z", "[-1,1,2,-1] [-1,-1,2,-1] [-1,-1,-1,3] [[-1,-1,-1,-1]]" },
+ { "\\Aa\\C*\\z", "[-1,1,-1] [1,1,2] [[-1,-1,-1]]" },
+ { "\\Aa\\C*", "[-1,1,-1] [2,2,3] [[2,2,2]] [[-1,-1,-1]]" },
+ { "a\\C*", "[0,1,-1] [2,2,3] [[2,2,2]] [[-1,-1,-1]]" },
+ { "\\C*", "[1,2] [[1,1]] [[-1,-1]]" },
+ { "a", "[0,1,-1] [2,2,2] [[-1,-1,-1]]"} ,
+};
+
+TEST(DFA, Callback) {
+ int nfail = 0;
+ for (int i = 0; i < arraysize(callback_tests); i++) {
+ const CallbackTest& t = callback_tests[i];
+ Regexp* re = Regexp::Parse(t.regexp, Regexp::LikePerl, NULL);
+ ASSERT_TRUE(re != NULL);
+ Prog* prog = re->CompileToProg(0);
+ ASSERT_TRUE(prog != NULL);
+ string dump;
+ prog->BuildEntireDFA(Prog::kLongestMatch, [&](const int* next, bool match) {
+ ASSERT_TRUE(next != NULL);
+ if (!dump.empty())
+ StringAppendF(&dump, " ");
+ StringAppendF(&dump, match ? "[[" : "[");
+ for (int b = 0; b < prog->bytemap_range() + 1; b++)
+ StringAppendF(&dump, "%d,", next[b]);
+ dump.pop_back();
+ StringAppendF(&dump, match ? "]]" : "]");
+ });
+ if (dump != t.dump) {
+ LOG(ERROR) << t.regexp << " bytemap:\n" << prog->DumpByteMap();
+ LOG(ERROR) << t.regexp << " dump:\ngot " << dump << "\nwant " << t.dump;
+ nfail++;
+ }
+ delete prog;
+ re->Decref();
+ }
+ EXPECT_EQ(nfail, 0);
+}
+
} // namespace re2
diff --git a/re2/testing/dump.cc b/re2/testing/dump.cc
index 4bdf714..b60bf24 100644
--- a/re2/testing/dump.cc
+++ b/re2/testing/dump.cc
@@ -17,8 +17,11 @@
// library (see BUILD).
#include <string>
-#include <vector>
+
#include "util/test.h"
+#include "util/logging.h"
+#include "util/strutil.h"
+#include "util/utf.h"
#include "re2/stringpiece.h"
#include "re2/regexp.h"
@@ -120,6 +123,8 @@ static void DumpRegexpAppending(Regexp* re, string* s) {
DumpRegexpAppending(re->sub()[0], s);
break;
case kRegexpCapture:
+ if (re->cap() == 0)
+ LOG(DFATAL) << "kRegexpCapture cap() == 0";
if (re->name()) {
s->append(*re->name());
s->append(":");
diff --git a/re2/testing/exhaustive1_test.cc b/re2/testing/exhaustive1_test.cc
index 9e057cc..29c5def 100644
--- a/re2/testing/exhaustive1_test.cc
+++ b/re2/testing/exhaustive1_test.cc
@@ -4,6 +4,9 @@
// Exhaustive testing of regular expression matching.
+#include <string>
+#include <vector>
+
#include "util/test.h"
#include "re2/testing/exhaustive_tester.h"
@@ -13,7 +16,7 @@ namespace re2 {
// Test simple repetition operators
TEST(Repetition, Simple) {
- vector<string> ops = Split(" ",
+ std::vector<string> ops = Split(" ",
"%s{0} %s{0,} %s{1} %s{1,} %s{0,1} %s{0,2} "
"%s{1,2} %s{2} %s{2,} %s{3,4} %s{4,5} "
"%s* %s+ %s? %s*? %s+? %s??");
@@ -25,7 +28,7 @@ TEST(Repetition, Simple) {
// Test capturing parens -- (a) -- inside repetition operators
TEST(Repetition, Capturing) {
- vector<string> ops = Split(" ",
+ std::vector<string> ops = Split(" ",
"%s{0} %s{0,} %s{1} %s{1,} %s{0,1} %s{0,2} "
"%s{1,2} %s{2} %s{2,} %s{3,4} %s{4,5} "
"%s* %s+ %s? %s*? %s+? %s??");
@@ -33,10 +36,9 @@ TEST(Repetition, Capturing) {
7, Explode("ab"), "(?:%s)", "");
// This would be a great test, but it runs forever when PCRE is enabled.
- if (strstr("PCRE", FLAGS_regexp_engines.c_str()) == NULL)
- ExhaustiveTest(4, 3, Split(" ", "a (a)"), ops,
- 100, Explode("a"), "(?:%s)", "");
+ if (FLAGS_regexp_engines.find("PCRE") == string::npos)
+ ExhaustiveTest(3, 2, Split(" ", "a (a)"), ops,
+ 50, Explode("a"), "(?:%s)", "");
}
} // namespace re2
-
diff --git a/re2/testing/exhaustive2_test.cc b/re2/testing/exhaustive2_test.cc
index c5fec5b..ba38a6e 100644
--- a/re2/testing/exhaustive2_test.cc
+++ b/re2/testing/exhaustive2_test.cc
@@ -4,12 +4,15 @@
// Exhaustive testing of regular expression matching.
+#include <stddef.h>
+#include <memory>
+#include <string>
+#include <vector>
+
#include "util/test.h"
#include "re2/re2.h"
#include "re2/testing/exhaustive_tester.h"
-DECLARE_string(regexp_engines);
-
namespace re2 {
// Test empty string matches (aka "(?:)")
@@ -21,9 +24,9 @@ TEST(EmptyString, Exhaustive) {
// Test escaped versions of regexp syntax.
TEST(Punctuation, Literals) {
- vector<string> alphabet = Explode("()*+?{}[]\\^$.");
- vector<string> escaped = alphabet;
- for (int i = 0; i < escaped.size(); i++)
+ std::vector<string> alphabet = Explode("()*+?{}[]\\^$.");
+ std::vector<string> escaped = alphabet;
+ for (size_t i = 0; i < escaped.size(); i++)
escaped[i] = "\\" + escaped[i];
ExhaustiveTest(1, 1, escaped, RegexpGenerator::EgrepOps(),
2, alphabet, "", "");
@@ -60,7 +63,7 @@ TEST(LineEnds, Exhaustive) {
// provides a mechanism, and RE2 could add new syntax if needed.
//
// TEST(Newlines, Exhaustive) {
-// vector<string> empty_vector;
+// std::vector<string> empty_vector;
// ExhaustiveTest(1, 1, Split(" ", "\\n . a [^a]"),
// RegexpGenerator::EgrepOps(),
// 4, Explode("a\n"), "");
diff --git a/re2/testing/exhaustive3_test.cc b/re2/testing/exhaustive3_test.cc
index 5613fcb..cf09e18 100644
--- a/re2/testing/exhaustive3_test.cc
+++ b/re2/testing/exhaustive3_test.cc
@@ -4,14 +4,20 @@
// Exhaustive testing of regular expression matching.
+#include <stddef.h>
+#include <memory>
+#include <string>
+#include <vector>
+
#include "util/test.h"
+#include "util/utf.h"
#include "re2/testing/exhaustive_tester.h"
namespace re2 {
// Test simple character classes by themselves.
TEST(CharacterClasses, Exhaustive) {
- vector<string> atoms = Split(" ",
+ std::vector<string> atoms = Split(" ",
"[a] [b] [ab] [^bc] [b-d] [^b-d] []a] [-a] [a-] [^-a] [a-b-c] a b .");
ExhaustiveTest(2, 1, atoms, RegexpGenerator::EgrepOps(),
5, Explode("ab"), "", "");
@@ -19,7 +25,7 @@ TEST(CharacterClasses, Exhaustive) {
// Test simple character classes inside a___b (for example, a[a]b).
TEST(CharacterClasses, ExhaustiveAB) {
- vector<string> atoms = Split(" ",
+ std::vector<string> atoms = Split(" ",
"[a] [b] [ab] [^bc] [b-d] [^b-d] []a] [-a] [a-] [^-a] [a-b-c] a b .");
ExhaustiveTest(2, 1, atoms, RegexpGenerator::EgrepOps(),
5, Explode("ab"), "a%sb", "");
@@ -35,9 +41,9 @@ static string UTF8(Rune r) {
// Returns a vector of "interesting" UTF8 characters.
// Unicode is now too big to just return all of them,
// so UTF8Characters return a set likely to be good test cases.
-static const vector<string>& InterestingUTF8() {
+static const std::vector<string>& InterestingUTF8() {
static bool init;
- static vector<string> v;
+ static std::vector<string> v;
if (init)
return v;
@@ -64,12 +70,12 @@ static const vector<string>& InterestingUTF8() {
// Test interesting UTF-8 characters against character classes.
TEST(InterestingUTF8, SingleOps) {
- vector<string> atoms = Split(" ",
+ std::vector<string> atoms = Split(" ",
". ^ $ \\a \\f \\n \\r \\t \\v \\d \\D \\s \\S \\w \\W \\b \\B "
"[[:alnum:]] [[:alpha:]] [[:blank:]] [[:cntrl:]] [[:digit:]] "
"[[:graph:]] [[:lower:]] [[:print:]] [[:punct:]] [[:space:]] "
"[[:upper:]] [[:xdigit:]] [\\s\\S] [\\d\\D] [^\\w\\W] [^\\d\\D]");
- vector<string> ops; // no ops
+ std::vector<string> ops; // no ops
ExhaustiveTest(1, 0, atoms, ops,
1, InterestingUTF8(), "", "");
}
@@ -77,14 +83,14 @@ TEST(InterestingUTF8, SingleOps) {
// Test interesting UTF-8 characters against character classes,
// but wrap everything inside AB.
TEST(InterestingUTF8, AB) {
- vector<string> atoms = Split(" ",
+ std::vector<string> atoms = Split(" ",
". ^ $ \\a \\f \\n \\r \\t \\v \\d \\D \\s \\S \\w \\W \\b \\B "
"[[:alnum:]] [[:alpha:]] [[:blank:]] [[:cntrl:]] [[:digit:]] "
"[[:graph:]] [[:lower:]] [[:print:]] [[:punct:]] [[:space:]] "
"[[:upper:]] [[:xdigit:]] [\\s\\S] [\\d\\D] [^\\w\\W] [^\\d\\D]");
- vector<string> ops; // no ops
- vector<string> alpha = InterestingUTF8();
- for (int i = 0; i < alpha.size(); i++)
+ std::vector<string> ops; // no ops
+ std::vector<string> alpha = InterestingUTF8();
+ for (size_t i = 0; i < alpha.size(); i++)
alpha[i] = "a" + alpha[i] + "b";
ExhaustiveTest(1, 0, atoms, ops,
1, alpha, "a%sb", "");
diff --git a/re2/testing/exhaustive_test.cc b/re2/testing/exhaustive_test.cc
index fc40dee..514fd90 100644
--- a/re2/testing/exhaustive_test.cc
+++ b/re2/testing/exhaustive_test.cc
@@ -9,8 +9,6 @@
namespace re2 {
-DECLARE_string(regexp_engines);
-
// Test very simple expressions.
TEST(EgrepLiterals, Lowercase) {
EgrepTest(3, 2, "abc.", 3, "abc", "");
diff --git a/re2/testing/exhaustive_tester.cc b/re2/testing/exhaustive_tester.cc
index 54de857..4f6335f 100644
--- a/re2/testing/exhaustive_tester.cc
+++ b/re2/testing/exhaustive_tester.cc
@@ -11,34 +11,32 @@
// the NFA, DFA, and a trivial backtracking implementation agree about
// the location of the match.
-#include <stdlib.h>
#include <stdio.h>
-#ifndef LOGGING
-#define LOGGING 0
-#endif
-
#include "util/test.h"
+#include "util/logging.h"
+#include "util/strutil.h"
#include "re2/testing/exhaustive_tester.h"
#include "re2/testing/tester.h"
+// For target `log' in the Makefile.
+#ifndef LOGGING
+#define LOGGING 0
+#endif
+
DEFINE_bool(show_regexps, false, "show regexps during testing");
DEFINE_int32(max_bad_regexp_inputs, 1,
"Stop testing a regular expression after finding this many "
"strings that break it.");
-// Compiled in debug mode, the usual tests run for over an hour.
-// Have to cut it down to make the unit test machines happy.
-DEFINE_bool(quick_debug_mode, true, "Run fewer tests in debug mode.");
-
namespace re2 {
static char* escape(const StringPiece& sp) {
static char buf[512];
char* p = buf;
*p++ = '\"';
- for (int i = 0; i < sp.size(); i++) {
+ for (size_t i = 0; i < sp.size(); i++) {
if(p+5 >= buf+sizeof buf)
LOG(FATAL) << "ExhaustiveTester escape: too long";
if(sp[i] == '\\' || sp[i] == '\"') {
@@ -67,10 +65,11 @@ static void PrintResult(const RE2& re, const StringPiece& input, RE2::Anchor anc
if (m[i].begin() == NULL)
printf("-");
else
- printf("%d-%d", static_cast<int>(m[i].begin() - input.begin()), static_cast<int>(m[i].end() - input.begin()));
+ printf("%td-%td",
+ m[i].begin() - input.begin(), m[i].end() - input.begin());
}
}
-
+
// Processes a single generated regexp.
// Compiles it using Regexp interface and PCRE, and then
// checks that NFA, DFA, and PCRE all return the same results.
@@ -143,12 +142,13 @@ void ExhaustiveTester::HandleRegexp(const string& const_regexp) {
// Runs an exhaustive test on the given parameters.
void ExhaustiveTest(int maxatoms, int maxops,
- const vector<string>& alphabet,
- const vector<string>& ops,
- int maxstrlen, const vector<string>& stralphabet,
+ const std::vector<string>& alphabet,
+ const std::vector<string>& ops,
+ int maxstrlen,
+ const std::vector<string>& stralphabet,
const string& wrapper,
const string& topwrapper) {
- if (DEBUG_MODE && FLAGS_quick_debug_mode) {
+ if (RE2_DEBUG_MODE) {
if (maxatoms > 1)
maxatoms--;
if (maxops > 1)
diff --git a/re2/testing/exhaustive_tester.h b/re2/testing/exhaustive_tester.h
index 38a139f..769d8b5 100644
--- a/re2/testing/exhaustive_tester.h
+++ b/re2/testing/exhaustive_tester.h
@@ -2,17 +2,34 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#ifndef RE2_TESTING_EXHAUSTIVE_TESTER_H__
-#define RE2_TESTING_EXHAUSTIVE_TESTER_H__
+#ifndef RE2_TESTING_EXHAUSTIVE_TESTER_H_
+#define RE2_TESTING_EXHAUSTIVE_TESTER_H_
+#include <stdint.h>
#include <string>
#include <vector>
+
#include "util/util.h"
#include "re2/testing/regexp_generator.h"
#include "re2/testing/string_generator.h"
namespace re2 {
+// Doing this simplifies the logic below.
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if !defined(NDEBUG)
+// We are in a debug build.
+const bool RE2_DEBUG_MODE = true;
+#elif __has_feature(address_sanitizer) || __has_feature(memory_sanitizer) || __has_feature(thread_sanitizer)
+// Not a debug build, but still under sanitizers.
+const bool RE2_DEBUG_MODE = true;
+#else
+const bool RE2_DEBUG_MODE = false;
+#endif
+
// Exhaustive regular expression test: generate all regexps within parameters,
// then generate all strings of a given length over a given alphabet,
// then check that NFA, DFA, and PCRE agree about whether each regexp matches
@@ -25,10 +42,10 @@ class ExhaustiveTester : public RegexpGenerator {
public:
ExhaustiveTester(int maxatoms,
int maxops,
- const vector<string>& alphabet,
- const vector<string>& ops,
+ const std::vector<string>& alphabet,
+ const std::vector<string>& ops,
int maxstrlen,
- const vector<string>& stralphabet,
+ const std::vector<string>& stralphabet,
const string& wrapper,
const string& topwrapper)
: RegexpGenerator(maxatoms, maxops, alphabet, ops),
@@ -46,7 +63,7 @@ class ExhaustiveTester : public RegexpGenerator {
void HandleRegexp(const string& regexp);
// Causes testing to generate random input strings.
- void RandomStrings(int32 seed, int32 count) {
+ void RandomStrings(int32_t seed, int32_t count) {
randomstrings_ = true;
stringseed_ = seed;
stringcount_ = count;
@@ -61,16 +78,19 @@ class ExhaustiveTester : public RegexpGenerator {
int failures_; // Number of tests failed.
bool randomstrings_; // Whether to use random strings
- int32 stringseed_; // If so, the seed.
+ int32_t stringseed_; // If so, the seed.
int stringcount_; // If so, how many to generate.
- DISALLOW_EVIL_CONSTRUCTORS(ExhaustiveTester);
+
+ ExhaustiveTester(const ExhaustiveTester&) = delete;
+ ExhaustiveTester& operator=(const ExhaustiveTester&) = delete;
};
// Runs an exhaustive test on the given parameters.
void ExhaustiveTest(int maxatoms, int maxops,
- const vector<string>& alphabet,
- const vector<string>& ops,
- int maxstrlen, const vector<string>& stralphabet,
+ const std::vector<string>& alphabet,
+ const std::vector<string>& ops,
+ int maxstrlen,
+ const std::vector<string>& stralphabet,
const string& wrapper,
const string& topwrapper);
@@ -82,4 +102,4 @@ void EgrepTest(int maxatoms, int maxops, const string& alphabet,
} // namespace re2
-#endif // RE2_TESTING_EXHAUSTIVE_TESTER_H__
+#endif // RE2_TESTING_EXHAUSTIVE_TESTER_H_
diff --git a/re2/testing/filtered_re2_test.cc b/re2/testing/filtered_re2_test.cc
index e3a0dd1..867eac6 100644
--- a/re2/testing/filtered_re2_test.cc
+++ b/re2/testing/filtered_re2_test.cc
@@ -2,32 +2,46 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include <stddef.h>
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
#include "util/test.h"
+#include "util/logging.h"
#include "re2/filtered_re2.h"
#include "re2/re2.h"
-DECLARE_int32(filtered_re2_min_atom_len); // From prefilter_tree.cc
-
namespace re2 {
struct FilterTestVars {
- vector<string> atoms;
- vector<int> atom_indices;
- vector<int> matches;
+ FilterTestVars() {}
+ explicit FilterTestVars(int min_atom_len) : f(min_atom_len) {}
+
+ std::vector<string> atoms;
+ std::vector<int> atom_indices;
+ std::vector<int> matches;
RE2::Options opts;
FilteredRE2 f;
};
TEST(FilteredRE2Test, EmptyTest) {
FilterTestVars v;
+
+ v.f.Compile(&v.atoms);
+ EXPECT_EQ(0, v.atoms.size());
+
+ // Compile has no effect at all when called before Add: it will not
+ // record that it has been called and it will not clear the vector.
+ // The second point does not matter here, but the first point means
+ // that an error will be logged during the call to AllMatches.
v.f.AllMatches("foo", v.atom_indices, &v.matches);
EXPECT_EQ(0, v.matches.size());
}
TEST(FilteredRE2Test, SmallOrTest) {
- FLAGS_filtered_re2_min_atom_len = 4;
-
- FilterTestVars v;
+ FilterTestVars v(4); // override the minimum atom length
int id;
v.f.Add("(foo|bar)", v.opts, &id);
@@ -40,11 +54,10 @@ TEST(FilteredRE2Test, SmallOrTest) {
}
TEST(FilteredRE2Test, SmallLatinTest) {
- FLAGS_filtered_re2_min_atom_len = 3;
FilterTestVars v;
int id;
- v.opts.set_utf8(false);
+ v.opts.set_encoding(RE2::Options::EncodingLatin1);
v.f.Add("\xde\xadQ\xbe\xef", v.opts, &id);
v.f.Compile(&v.atoms);
EXPECT_EQ(1, v.atoms.size());
@@ -144,33 +157,31 @@ bool CheckExpectedAtoms(const char* atoms[],
int n,
const char* testname,
struct FilterTestVars* v) {
- vector<string> expected;
+ std::vector<string> expected;
for (int i = 0; i < n; i++)
expected.push_back(atoms[i]);
bool pass = expected.size() == v->atoms.size();
- sort(v->atoms.begin(), v->atoms.end());
- sort(expected.begin(), expected.end());
+ std::sort(v->atoms.begin(), v->atoms.end());
+ std::sort(expected.begin(), expected.end());
for (int i = 0; pass && i < n; i++)
pass = pass && expected[i] == v->atoms[i];
if (!pass) {
- LOG(WARNING) << "Failed " << testname;
- LOG(WARNING) << "Expected #atoms = " << expected.size();
- for (int i = 0; i < expected.size(); i++)
- LOG(WARNING) << expected[i];
- LOG(WARNING) << "Found #atoms = " << v->atoms.size();
- for (int i = 0; i < v->atoms.size(); i++)
- LOG(WARNING) << v->atoms[i];
+ LOG(ERROR) << "Failed " << testname;
+ LOG(ERROR) << "Expected #atoms = " << expected.size();
+ for (size_t i = 0; i < expected.size(); i++)
+ LOG(ERROR) << expected[i];
+ LOG(ERROR) << "Found #atoms = " << v->atoms.size();
+ for (size_t i = 0; i < v->atoms.size(); i++)
+ LOG(ERROR) << v->atoms[i];
}
return pass;
}
TEST(FilteredRE2Test, AtomTests) {
- FLAGS_filtered_re2_min_atom_len = 3;
-
int nfail = 0;
for (int i = 0; i < arraysize(atom_tests); i++) {
FilterTestVars v;
@@ -189,25 +200,21 @@ TEST(FilteredRE2Test, AtomTests) {
EXPECT_EQ(0, nfail);
}
-void FindAtomIndices(const vector<string> atoms,
- const vector<string> matched_atoms,
- vector<int>* atom_indices) {
+void FindAtomIndices(const std::vector<string>& atoms,
+ const std::vector<string>& matched_atoms,
+ std::vector<int>* atom_indices) {
atom_indices->clear();
- for (int i = 0; i < matched_atoms.size(); i++) {
- int j = 0;
- for (; j < atoms.size(); j++) {
+ for (size_t i = 0; i < matched_atoms.size(); i++) {
+ for (size_t j = 0; j < atoms.size(); j++) {
if (matched_atoms[i] == atoms[j]) {
- atom_indices->push_back(j);
+ atom_indices->push_back(static_cast<int>(j));
break;
}
- EXPECT_LT(j, atoms.size());
}
}
}
TEST(FilteredRE2Test, MatchEmptyPattern) {
- FLAGS_filtered_re2_min_atom_len = 3;
-
FilterTestVars v;
AtomTest* t = &atom_tests[0];
// We are using the regexps used in one of the atom tests
@@ -220,14 +227,12 @@ TEST(FilteredRE2Test, MatchEmptyPattern) {
break;
AddRegexpsAndCompile(t->regexps, nregexp, &v);
string text = "0123";
- vector<int> atom_ids;
- vector<int> matching_regexps;
+ std::vector<int> atom_ids;
+ std::vector<int> matching_regexps;
EXPECT_EQ(0, v.f.FirstMatch(text, atom_ids));
}
TEST(FilteredRE2Test, MatchTests) {
- FLAGS_filtered_re2_min_atom_len = 3;
-
FilterTestVars v;
AtomTest* t = &atom_tests[2];
// We are using the regexps used in one of the atom tests
@@ -241,11 +246,11 @@ TEST(FilteredRE2Test, MatchTests) {
string text = "abc121212xyz";
// atoms = abc
- vector<int> atom_ids;
- vector<string> atoms;
+ std::vector<int> atom_ids;
+ std::vector<string> atoms;
atoms.push_back("abc");
FindAtomIndices(v.atoms, atoms, &atom_ids);
- vector<int> matching_regexps;
+ std::vector<int> matching_regexps;
v.f.AllMatches(text, atom_ids, &matching_regexps);
EXPECT_EQ(1, matching_regexps.size());
@@ -266,7 +271,7 @@ TEST(FilteredRE2Test, MatchTests) {
atoms.push_back("yyyzzz");
FindAtomIndices(v.atoms, atoms, &atom_ids);
LOG(INFO) << "S: " << atom_ids.size();
- for (int i = 0; i < atom_ids.size(); i++)
+ for (size_t i = 0; i < atom_ids.size(); i++)
LOG(INFO) << "i: " << i << " : " << atom_ids[i];
v.f.AllMatches(text, atom_ids, &matching_regexps);
EXPECT_EQ(2, matching_regexps.size());
diff --git a/re2/testing/mimics_pcre_test.cc b/re2/testing/mimics_pcre_test.cc
index f965092..2dbbfa1 100644
--- a/re2/testing/mimics_pcre_test.cc
+++ b/re2/testing/mimics_pcre_test.cc
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
#include "util/test.h"
+#include "util/logging.h"
#include "re2/prog.h"
#include "re2/regexp.h"
@@ -64,8 +65,8 @@ TEST(MimicsPCRE, SimpleTests) {
if (j == 0)
flags = flags | Regexp::Latin1;
Regexp* re = Regexp::Parse(t.regexp, flags, NULL);
- CHECK(re) << " " << t.regexp;
- CHECK_EQ(t.should_match, re->MimicsPCRE())
+ ASSERT_TRUE(re != NULL) << " " << t.regexp;
+ ASSERT_EQ(t.should_match, re->MimicsPCRE())
<< " " << t.regexp << " "
<< (j==0 ? "latin1" : "utf");
re->Decref();
diff --git a/re2/testing/null_walker.cc b/re2/testing/null_walker.cc
index 09b53cb..77fa723 100644
--- a/re2/testing/null_walker.cc
+++ b/re2/testing/null_walker.cc
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
#include "util/test.h"
+#include "util/logging.h"
#include "re2/regexp.h"
#include "re2/walker-inl.h"
@@ -23,7 +24,8 @@ class NullWalker : public Regexp::Walker<bool> {
}
private:
- DISALLOW_EVIL_CONSTRUCTORS(NullWalker);
+ NullWalker(const NullWalker&) = delete;
+ NullWalker& operator=(const NullWalker&) = delete;
};
// Called after visiting re's children. child_args contains the return
diff --git a/re2/testing/parse_test.cc b/re2/testing/parse_test.cc
index f67b477..d2b04fc 100644
--- a/re2/testing/parse_test.cc
+++ b/re2/testing/parse_test.cc
@@ -5,13 +5,17 @@
// Test parse.cc, dump.cc, and tostring.cc.
#include <string>
-#include <vector>
+
#include "util/test.h"
+#include "util/logging.h"
#include "re2/regexp.h"
namespace re2 {
-static const Regexp::ParseFlags TestZeroFlags = Regexp::ParseFlags(1<<30);
+// In the past, we used 1<<30 here and zeroed the bit later, but that
+// has undefined behaviour, so now we use an internal-only flag because
+// otherwise we would have to introduce a new flag value just for this.
+static const Regexp::ParseFlags TestZeroFlags = Regexp::WasDollar;
struct Test {
const char* regexp;
@@ -49,7 +53,7 @@ static Test tests[] = {
{ "a{2,3}?", "nrep{2,3 lit{a}}" },
{ "a{2,}?", "nrep{2,-1 lit{a}}" },
{ "", "emp{}" },
- { "|", "emp{}" }, // alt{emp{}emp{}} but got factored
+ { "|", "alt{emp{}emp{}}" },
{ "|x|", "alt{emp{}lit{x}emp{}}" },
{ ".", "dot{}" },
{ "^", "bol{}" },
@@ -114,18 +118,39 @@ static Test tests[] = {
{ "ab|cd", "alt{str{ab}str{cd}}" },
{ "a(b|c)d", "cat{lit{a}cap{cc{0x62-0x63}}lit{d}}" },
+ // Test squashing of **, ++, ?? et cetera.
+ { "(?:(?:a)*)*", "star{lit{a}}" },
+ { "(?:(?:a)+)+", "plus{lit{a}}" },
+ { "(?:(?:a)?)?", "que{lit{a}}" },
+ { "(?:(?:a)*)+", "star{lit{a}}" },
+ { "(?:(?:a)*)?", "star{lit{a}}" },
+ { "(?:(?:a)+)*", "star{lit{a}}" },
+ { "(?:(?:a)+)?", "star{lit{a}}" },
+ { "(?:(?:a)?)*", "star{lit{a}}" },
+ { "(?:(?:a)?)+", "star{lit{a}}" },
+
// Test flattening.
{ "(?:a)", "lit{a}" },
{ "(?:ab)(?:cd)", "str{abcd}" },
{ "(?:a|b)|(?:c|d)", "cc{0x61-0x64}" },
+ { "a|c", "cc{0x61 0x63}" },
+ { "a|[cd]", "cc{0x61 0x63-0x64}" },
{ "a|.", "dot{}" },
- { ".|a", "dot{}" },
+ { "[ab]|c", "cc{0x61-0x63}" },
+ { "[ab]|[cd]", "cc{0x61-0x64}" },
+ { "[ab]|.", "dot{}" },
+ { ".|c", "dot{}" },
+ { ".|[cd]", "dot{}" },
+ { ".|.", "dot{}" },
// Test Perl quoted literals
{ "\\Q+|*?{[\\E", "str{+|*?{[}" },
{ "\\Q+\\E+", "plus{lit{+}}" },
{ "\\Q\\\\E", "lit{\\}" },
{ "\\Q\\\\\\E", "str{\\\\}" },
+ { "\\Qa\\E*", "star{lit{a}}" },
+ { "\\Qab\\E*", "cat{lit{a}star{lit{b}}}" },
+ { "\\Qabc\\E*", "cat{str{ab}star{lit{c}}}" },
// Test Perl \A and \z
{ "(?m)^", "bol{}" },
@@ -208,16 +233,16 @@ void TestParse(const Test* tests, int ntests, Regexp::ParseFlags flags,
f = tests[i].flags & ~TestZeroFlags;
}
re[i] = Regexp::Parse(tests[i].regexp, f, &status);
- CHECK(re[i] != NULL) << " " << tests[i].regexp << " "
- << status.Text();
+ ASSERT_TRUE(re[i] != NULL)
+ << " " << tests[i].regexp << " " << status.Text();
string s = re[i]->Dump();
EXPECT_EQ(string(tests[i].parse), s) << "Regexp: " << tests[i].regexp
- << "\nparse: " << tests[i].parse << " s: " << s << " flag=" << f;
+ << "\nparse: " << string(tests[i].parse) << " s: " << s << " flag=" << f;
}
for (int i = 0; i < ntests; i++) {
for (int j = 0; j < ntests; j++) {
- EXPECT_EQ(string(tests[i].parse) == tests[j].parse,
+ EXPECT_EQ(string(tests[i].parse) == string(tests[j].parse),
RegexpEqualTestingOnly(re[i], re[j]))
<< "Regexp: " << tests[i].regexp << " " << tests[j].regexp;
}
@@ -293,12 +318,34 @@ Test prefix_tests[] = {
{ "abc|x|abd", "alt{str{abc}lit{x}str{abd}}" },
{ "(?i)abc|ABD", "cat{strfold{ab}cc{0x43-0x44 0x63-0x64}}" },
{ "[ab]c|[ab]d", "cat{cc{0x61-0x62}cc{0x63-0x64}}" },
- { "(?:xx|yy)c|(?:xx|yy)d",
- "cat{alt{str{xx}str{yy}}cc{0x63-0x64}}" },
+ { ".c|.d", "cat{cc{0-0x9 0xb-0x10ffff}cc{0x63-0x64}}" },
+ { "\\Cc|\\Cd", "cat{byte{}cc{0x63-0x64}}" },
{ "x{2}|x{2}[0-9]",
"cat{rep{2,2 lit{x}}alt{emp{}cc{0x30-0x39}}}" },
{ "x{2}y|x{2}[0-9]y",
"cat{rep{2,2 lit{x}}alt{lit{y}cat{cc{0x30-0x39}lit{y}}}}" },
+ { "n|r|rs",
+ "alt{lit{n}cat{lit{r}alt{emp{}lit{s}}}}" },
+ { "n|rs|r",
+ "alt{lit{n}cat{lit{r}alt{lit{s}emp{}}}}" },
+ { "r|rs|n",
+ "alt{cat{lit{r}alt{emp{}lit{s}}}lit{n}}" },
+ { "rs|r|n",
+ "alt{cat{lit{r}alt{lit{s}emp{}}}lit{n}}" },
+ { "a\\C*?c|a\\C*?b",
+ "cat{lit{a}alt{cat{nstar{byte{}}lit{c}}cat{nstar{byte{}}lit{b}}}}" },
+ { "^/a/bc|^/a/de",
+ "cat{bol{}cat{str{/a/}alt{str{bc}str{de}}}}" },
+ // In the past, factoring was limited to kFactorAlternationMaxDepth (8).
+ { "a|aa|aaa|aaaa|aaaaa|aaaaaa|aaaaaaa|aaaaaaaa|aaaaaaaaa|aaaaaaaaaa",
+ "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}"
+ "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}"
+ "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}"
+ "lit{a}}}}}}}}}}}}}}}}}}}" },
+ { "a|aardvark|aardvarks|abaci|aback|abacus|abacuses|abaft|abalone|abalones",
+ "cat{lit{a}alt{emp{}cat{str{ardvark}alt{emp{}lit{s}}}"
+ "cat{str{ba}alt{cat{lit{c}alt{cc{0x69 0x6b}cat{str{us}alt{emp{}str{es}}}}}"
+ "str{ft}cat{str{lone}alt{emp{}lit{s}}}}}}}" },
};
// Test that prefix factoring works.
@@ -306,6 +353,22 @@ TEST(TestParse, Prefix) {
TestParse(prefix_tests, arraysize(prefix_tests), Regexp::PerlX, "prefix");
}
+Test nested_tests[] = {
+ { "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}))",
+ "cap{cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 lit{x}}}}}}}}}}}}}}}}}}}}" },
+ { "((((((((((x{1}){2}){2}){2}){2}){2}){2}){2}){2}){2})",
+ "cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{1,1 lit{x}}}}}}}}}}}}}}}}}}}}}" },
+ { "((((((((((x{0}){2}){2}){2}){2}){2}){2}){2}){2}){2})",
+ "cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{0,0 lit{x}}}}}}}}}}}}}}}}}}}}}" },
+ { "((((((x{2}){2}){2}){5}){5}){5})",
+ "cap{rep{5,5 cap{rep{5,5 cap{rep{5,5 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 lit{x}}}}}}}}}}}}}" },
+};
+
+// Test that nested repetition works.
+TEST(TestParse, Nested) {
+ TestParse(nested_tests, arraysize(nested_tests), Regexp::PerlX, "nested");
+}
+
// Invalid regular expressions
const char* badtests[] = {
"(",
@@ -329,6 +392,9 @@ const char* badtests[] = {
"(?i)[a-Z]",
"a{100000}",
"a{100000,}",
+ "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})",
+ "(((x{7}){11}){13})",
+ "\\Q\\E*",
};
// Valid in Perl, bad in POSIX
@@ -356,23 +422,23 @@ const char* only_posix[] = {
// Test that parser rejects bad regexps.
TEST(TestParse, InvalidRegexps) {
for (int i = 0; i < arraysize(badtests); i++) {
- CHECK(Regexp::Parse(badtests[i], Regexp::PerlX, NULL) == NULL)
+ ASSERT_TRUE(Regexp::Parse(badtests[i], Regexp::PerlX, NULL) == NULL)
<< " " << badtests[i];
- CHECK(Regexp::Parse(badtests[i], Regexp::NoParseFlags, NULL) == NULL)
+ ASSERT_TRUE(Regexp::Parse(badtests[i], Regexp::NoParseFlags, NULL) == NULL)
<< " " << badtests[i];
}
for (int i = 0; i < arraysize(only_posix); i++) {
- CHECK(Regexp::Parse(only_posix[i], Regexp::PerlX, NULL) == NULL)
+ ASSERT_TRUE(Regexp::Parse(only_posix[i], Regexp::PerlX, NULL) == NULL)
<< " " << only_posix[i];
Regexp* re = Regexp::Parse(only_posix[i], Regexp::NoParseFlags, NULL);
- CHECK(re) << " " << only_posix[i];
+ ASSERT_TRUE(re != NULL) << " " << only_posix[i];
re->Decref();
}
for (int i = 0; i < arraysize(only_perl); i++) {
- CHECK(Regexp::Parse(only_perl[i], Regexp::NoParseFlags, NULL) == NULL)
+ ASSERT_TRUE(Regexp::Parse(only_perl[i], Regexp::NoParseFlags, NULL) == NULL)
<< " " << only_perl[i];
Regexp* re = Regexp::Parse(only_perl[i], Regexp::PerlX, NULL);
- CHECK(re) << " " << only_perl[i];
+ ASSERT_TRUE(re != NULL) << " " << only_perl[i];
re->Decref();
}
}
@@ -386,7 +452,7 @@ TEST(TestToString, EquivalentParse) {
f = tests[i].flags & ~TestZeroFlags;
}
Regexp* re = Regexp::Parse(tests[i].regexp, f, &status);
- CHECK(re != NULL) << " " << tests[i].regexp << " " << status.Text();
+ ASSERT_TRUE(re != NULL) << " " << tests[i].regexp << " " << status.Text();
string s = re->Dump();
EXPECT_EQ(string(tests[i].parse), s) << " " << tests[i].regexp << " " << string(tests[i].parse) << " " << s;
string t = re->ToString();
@@ -396,12 +462,12 @@ TEST(TestToString, EquivalentParse) {
// Unfortunately we can't check the length here, because
// ToString produces "\\{" for a literal brace,
// but "{" is a shorter equivalent.
- // CHECK_LT(t.size(), strlen(tests[i].regexp))
+ // ASSERT_LT(t.size(), strlen(tests[i].regexp))
// << " t=" << t << " regexp=" << tests[i].regexp;
// Test that if we parse the new regexp we get the same structure.
Regexp* nre = Regexp::Parse(t, Regexp::MatchNL | Regexp::PerlX, &status);
- CHECK(nre != NULL) << " reparse " << t << " " << status.Text();
+ ASSERT_TRUE(nre != NULL) << " reparse " << t << " " << status.Text();
string ss = nre->Dump();
string tt = nre->ToString();
if (s != ss || t != tt)
diff --git a/re2/testing/possible_match_test.cc b/re2/testing/possible_match_test.cc
index 7c2400e..f43a78b 100644
--- a/re2/testing/possible_match_test.cc
+++ b/re2/testing/possible_match_test.cc
@@ -2,11 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include <string.h>
+#include <string>
#include <vector>
+
#include "util/test.h"
+#include "util/logging.h"
+#include "util/strutil.h"
#include "re2/prog.h"
#include "re2/re2.h"
#include "re2/regexp.h"
+#include "re2/testing/exhaustive_tester.h"
#include "re2/testing/regexp_generator.h"
#include "re2/testing/string_generator.h"
@@ -108,15 +114,15 @@ TEST(PossibleMatchRange, HandWritten) {
if (j == 0) {
LOG(INFO) << "Checking regexp=" << CEscape(t.regexp);
Regexp* re = Regexp::Parse(t.regexp, Regexp::LikePerl, NULL);
- CHECK(re);
+ ASSERT_TRUE(re != NULL);
Prog* prog = re->CompileToProg(0);
- CHECK(prog);
- CHECK(prog->PossibleMatchRange(&min, &max, t.maxlen))
+ ASSERT_TRUE(prog != NULL);
+ ASSERT_TRUE(prog->PossibleMatchRange(&min, &max, t.maxlen))
<< " " << t.regexp;
delete prog;
re->Decref();
} else {
- CHECK(RE2(t.regexp).PossibleMatchRange(&min, &max, t.maxlen));
+ ASSERT_TRUE(RE2(t.regexp).PossibleMatchRange(&min, &max, t.maxlen));
}
EXPECT_EQ(t.min, min) << t.regexp;
EXPECT_EQ(t.max, max) << t.regexp;
@@ -136,26 +142,26 @@ TEST(PossibleMatchRange, Failures) {
// are no valid UTF-8 strings beginning with byte 0xFF.
EXPECT_FALSE(RE2("[\\s\\S]+", RE2::Latin1).
PossibleMatchRange(&min, &max, 10))
- << "min=" << CEscape(min) << ", max=" << CEscape(max);
+ << "min=" << CEscape(min) << ", max=" << CEscape(max);
EXPECT_FALSE(RE2("[\\0-\xFF]+", RE2::Latin1).
PossibleMatchRange(&min, &max, 10))
- << "min=" << CEscape(min) << ", max=" << CEscape(max);
+ << "min=" << CEscape(min) << ", max=" << CEscape(max);
EXPECT_FALSE(RE2(".+hello", RE2::Latin1).
PossibleMatchRange(&min, &max, 10))
- << "min=" << CEscape(min) << ", max=" << CEscape(max);
+ << "min=" << CEscape(min) << ", max=" << CEscape(max);
EXPECT_FALSE(RE2(".*hello", RE2::Latin1).
PossibleMatchRange(&min, &max, 10))
- << "min=" << CEscape(min) << ", max=" << CEscape(max);
+ << "min=" << CEscape(min) << ", max=" << CEscape(max);
EXPECT_FALSE(RE2(".*", RE2::Latin1).
PossibleMatchRange(&min, &max, 10))
- << "min=" << CEscape(min) << ", max=" << CEscape(max);
+ << "min=" << CEscape(min) << ", max=" << CEscape(max);
EXPECT_FALSE(RE2("\\C*").
PossibleMatchRange(&min, &max, 10))
- << "min=" << CEscape(min) << ", max=" << CEscape(max);
+ << "min=" << CEscape(min) << ", max=" << CEscape(max);
// Fails because it's a malformed regexp.
EXPECT_FALSE(RE2("*hello").PossibleMatchRange(&min, &max, 10))
- << "min=" << CEscape(min) << ", max=" << CEscape(max);
+ << "min=" << CEscape(min) << ", max=" << CEscape(max);
}
// Exhaustive test: generate all regexps within parameters,
@@ -166,10 +172,10 @@ class PossibleMatchTester : public RegexpGenerator {
public:
PossibleMatchTester(int maxatoms,
int maxops,
- const vector<string>& alphabet,
- const vector<string>& ops,
+ const std::vector<string>& alphabet,
+ const std::vector<string>& ops,
int maxstrlen,
- const vector<string>& stralphabet)
+ const std::vector<string>& stralphabet)
: RegexpGenerator(maxatoms, maxops, alphabet, ops),
strgen_(maxstrlen, stralphabet),
regexps_(0), tests_(0) { }
@@ -186,7 +192,8 @@ class PossibleMatchTester : public RegexpGenerator {
int regexps_; // Number of HandleRegexp calls
int tests_; // Number of regexp tests.
- DISALLOW_EVIL_CONSTRUCTORS(PossibleMatchTester);
+ PossibleMatchTester(const PossibleMatchTester&) = delete;
+ PossibleMatchTester& operator=(const PossibleMatchTester&) = delete;
};
// Processes a single generated regexp.
@@ -197,7 +204,7 @@ void PossibleMatchTester::HandleRegexp(const string& regexp) {
VLOG(3) << CEscape(regexp);
RE2 re(regexp, RE2::Latin1);
- CHECK_EQ(re.error(), "");
+ ASSERT_EQ(re.error(), "");
string min, max;
if(!re.PossibleMatchRange(&min, &max, 10)) {
@@ -215,8 +222,8 @@ void PossibleMatchTester::HandleRegexp(const string& regexp) {
tests_++;
if (!RE2::FullMatch(s, re))
continue;
- CHECK_GE(s, min) << " regexp: " << regexp << " max: " << max;
- CHECK_LE(s, max) << " regexp: " << regexp << " min: " << min;
+ ASSERT_GE(s, min) << " regexp: " << regexp << " max: " << max;
+ ASSERT_LE(s, max) << " regexp: " << regexp << " min: " << min;
}
}
@@ -224,7 +231,7 @@ TEST(PossibleMatchRange, Exhaustive) {
int natom = 3;
int noperator = 3;
int stringlen = 5;
- if (DEBUG_MODE) {
+ if (RE2_DEBUG_MODE) {
natom = 2;
noperator = 3;
stringlen = 3;
diff --git a/re2/testing/random_test.cc b/re2/testing/random_test.cc
index 91d2b32..bd0842f 100644
--- a/re2/testing/random_test.cc
+++ b/re2/testing/random_test.cc
@@ -5,6 +5,9 @@
// Random testing of regular expression matching.
#include <stdio.h>
+#include <string>
+#include <vector>
+
#include "util/test.h"
#include "re2/testing/exhaustive_tester.h"
@@ -19,13 +22,14 @@ namespace re2 {
// (Always uses the same random seeds for reproducibility.
// Can give different seeds on command line.)
static void RandomTest(int maxatoms, int maxops,
- const vector<string>& alphabet,
- const vector<string>& ops,
- int maxstrlen, const vector<string>& stralphabet,
+ const std::vector<string>& alphabet,
+ const std::vector<string>& ops,
+ int maxstrlen,
+ const std::vector<string>& stralphabet,
const string& wrapper) {
// Limit to smaller test cases in debug mode,
// because everything is so much slower.
- if (DEBUG_MODE) {
+ if (RE2_DEBUG_MODE) {
maxatoms--;
maxops--;
maxstrlen /= 2;
@@ -75,7 +79,7 @@ TEST(Random, BigEgrepCaptures) {
// character classes like \d. (Adding larger character classes would
// make for too many possibilities.)
TEST(Random, Complicated) {
- vector<string> ops = Split(" ",
+ std::vector<string> ops = Split(" ",
"%s%s %s|%s %s* %s*? %s+ %s+? %s? %s?? "
"%s{0} %s{0,} %s{1} %s{1,} %s{0,1} %s{0,2} %s{1,2} "
"%s{2} %s{2,} %s{3,4} %s{4,5}");
@@ -83,11 +87,11 @@ TEST(Random, Complicated) {
// Use (?:\b) and (?:\B) instead of \b and \B,
// because PCRE rejects \b* but accepts (?:\b)*.
// Ditto ^ and $.
- vector<string> atoms = Split(" ",
+ std::vector<string> atoms = Split(" ",
". (?:^) (?:$) \\a \\f \\n \\r \\t \\v "
"\\d \\D \\s \\S \\w \\W (?:\\b) (?:\\B) "
"a (a) b c - \\\\");
- vector<string> alphabet = Explode("abc123\001\002\003\t\r\n\v\f\a");
+ std::vector<string> alphabet = Explode("abc123\001\002\003\t\r\n\v\f\a");
RandomTest(10, 10, atoms, ops, 20, alphabet, "");
}
diff --git a/re2/testing/re2_arg_test.cc b/re2/testing/re2_arg_test.cc
index ae7a7b0..7a38de7 100644
--- a/re2/testing/re2_arg_test.cc
+++ b/re2/testing/re2_arg_test.cc
@@ -7,6 +7,9 @@
// Todo: Expand the test to validate strings parsed to the other types
// supported by RE2::Arg class
+#include <stdint.h>
+#include <string.h>
+
#include "util/test.h"
#include "re2/re2.h"
@@ -14,7 +17,7 @@ namespace re2 {
struct SuccessTable {
const char * value_string;
- int64 value;
+ int64_t value;
bool success[6];
};
@@ -25,7 +28,7 @@ struct SuccessTable {
// the various integral types and has entries for whether or not each
// type can contain the given value.
const SuccessTable kSuccessTable[] = {
-// string integer value short ushort int uint int64 uint64
+// string integer value i16 u16 i32 u32 i64 u64
// 0 to 2^7-1
{ "0", 0, { true, true, true, true, true, true }},
{ "127", 127, { true, true, true, true, true, true }},
@@ -56,9 +59,8 @@ const SuccessTable kSuccessTable[] = {
// -2^15-1 to -2^31
{ "-32769", -32769, { false, false, true, false, true, false }},
-{ "-2147483648",
- static_cast<int64>(0xFFFFFFFF80000000LL),
-{ false, false, true, false, true, false }},
+{ "-2147483648", static_cast<int64_t>(0xFFFFFFFF80000000LL),
+ { false, false, true, false, true, false }},
// 2^31 to 2^32-1
{ "2147483648", 2147483648U, { false, false, false, true, true, true }},
@@ -71,63 +73,63 @@ const SuccessTable kSuccessTable[] = {
// -2^31-1 to -2^63
{ "-2147483649", -2147483649LL, { false, false, false, false, true, false }},
-{ "-9223372036854775808", static_cast<int64>(0x8000000000000000LL),
+{ "-9223372036854775808", static_cast<int64_t>(0x8000000000000000LL),
{ false, false, false, false, true, false }},
// 2^63 to 2^64-1
-{ "9223372036854775808", static_cast<int64>(9223372036854775808ULL),
+{ "9223372036854775808", static_cast<int64_t>(9223372036854775808ULL),
{ false, false, false, false, false, true }},
-{ "18446744073709551615", static_cast<int64>(18446744073709551615ULL),
+{ "18446744073709551615", static_cast<int64_t>(18446744073709551615ULL),
{ false, false, false, false, false, true }},
// >= 2^64
{ "18446744073709551616", 0, { false, false, false, false, false, false }},
};
-const int kNumStrings = ARRAYSIZE(kSuccessTable);
+const int kNumStrings = arraysize(kSuccessTable);
-// It's ugly to use a macro, but we apparently can't use the ASSERT_TRUE_M
+// It's ugly to use a macro, but we apparently can't use the EXPECT_EQ
// macro outside of a TEST block and this seems to be the only way to
// avoid code duplication. I can also pull off a couple nice tricks
// using concatenation for the type I'm checking against.
#define PARSE_FOR_TYPE(type, column) { \
type r; \
- for ( int i = 0; i < kNumStrings; ++i ) { \
+ for (int i = 0; i < kNumStrings; ++i) { \
RE2::Arg arg(&r); \
const char* const p = kSuccessTable[i].value_string; \
bool retval = arg.Parse(p, strlen(p)); \
bool success = kSuccessTable[i].success[column]; \
- ASSERT_TRUE_M(retval == success, \
- StringPrintf("Parsing '%s' for type " #type " should return %d", \
- p, success).c_str()); \
- if ( success ) { \
- ASSERT_EQUALS(r, kSuccessTable[i].value); \
+ EXPECT_EQ(retval, success) \
+ << "Parsing '" << p << "' for type " #type " should return " \
+ << success; \
+ if (success) { \
+ EXPECT_EQ(r, (type)kSuccessTable[i].value); \
} \
} \
}
-TEST(REArgTest, Int16Test) {
- PARSE_FOR_TYPE(int16, 0);
+TEST(RE2ArgTest, Int16Test) {
+ PARSE_FOR_TYPE(int16_t, 0);
}
-TEST(REArgTest, Uint16Test) {
- PARSE_FOR_TYPE(uint16, 1);
+TEST(RE2ArgTest, Uint16Test) {
+ PARSE_FOR_TYPE(uint16_t, 1);
}
-TEST(REArgTest, IntTest) {
- PARSE_FOR_TYPE(int, 2);
+TEST(RE2ArgTest, Int32Test) {
+ PARSE_FOR_TYPE(int32_t, 2);
}
-TEST(REArgTest, UInt32Test) {
- PARSE_FOR_TYPE(uint32, 3);
+TEST(RE2ArgTest, Uint32Test) {
+ PARSE_FOR_TYPE(uint32_t, 3);
}
-TEST(REArgTest, Iint64Test) {
- PARSE_FOR_TYPE(int64, 4);
+TEST(RE2ArgTest, Int64Test) {
+ PARSE_FOR_TYPE(int64_t, 4);
}
-TEST(REArgTest, Uint64Test) {
- PARSE_FOR_TYPE(uint64, 5);
+TEST(RE2ArgTest, Uint64Test) {
+ PARSE_FOR_TYPE(uint64_t, 5);
}
} // namespace re2
diff --git a/re2/testing/re2_test.cc b/re2/testing/re2_test.cc
index b99cacf..cae956c 100644
--- a/re2/testing/re2_test.cc
+++ b/re2/testing/re2_test.cc
@@ -5,95 +5,97 @@
// TODO: Test extractions for PartialMatch/Consume
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
#include <errno.h>
-#include <vector>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <map>
+#include <string>
+#include <utility>
+#if !defined(_MSC_VER) && !defined(__CYGWIN__) && !defined(__MINGW32__)
+#include <sys/mman.h>
+#include <unistd.h> /* for sysconf */
+#endif
+
#include "util/test.h"
+#include "util/logging.h"
+#include "util/strutil.h"
#include "re2/re2.h"
#include "re2/regexp.h"
-DECLARE_bool(logtostderr);
-
namespace re2 {
TEST(RE2, HexTests) {
-
- VLOG(1) << "hex tests";
-
-#define CHECK_HEX(type, value) \
- do { \
- type v; \
- CHECK(RE2::FullMatch(#value, "([0-9a-fA-F]+)[uUlL]*", RE2::Hex(&v))); \
- CHECK_EQ(v, 0x ## value); \
- CHECK(RE2::FullMatch("0x" #value, "([0-9a-fA-FxX]+)[uUlL]*", RE2::CRadix(&v))); \
- CHECK_EQ(v, 0x ## value); \
- } while(0)
-
- CHECK_HEX(short, 2bad);
- CHECK_HEX(unsigned short, 2badU);
- CHECK_HEX(int, dead);
- CHECK_HEX(unsigned int, deadU);
- CHECK_HEX(long, 7eadbeefL);
- CHECK_HEX(unsigned long, deadbeefUL);
- CHECK_HEX(long long, 12345678deadbeefLL);
- CHECK_HEX(unsigned long long, cafebabedeadbeefULL);
-
-#undef CHECK_HEX
+#define ASSERT_HEX(type, value) \
+ do { \
+ type v; \
+ ASSERT_TRUE( \
+ RE2::FullMatch(#value, "([0-9a-fA-F]+)[uUlL]*", RE2::Hex(&v))); \
+ ASSERT_EQ(v, 0x##value); \
+ ASSERT_TRUE(RE2::FullMatch("0x" #value, "([0-9a-fA-FxX]+)[uUlL]*", \
+ RE2::CRadix(&v))); \
+ ASSERT_EQ(v, 0x##value); \
+ } while (0)
+
+ ASSERT_HEX(short, 2bad);
+ ASSERT_HEX(unsigned short, 2badU);
+ ASSERT_HEX(int, dead);
+ ASSERT_HEX(unsigned int, deadU);
+ ASSERT_HEX(long, 7eadbeefL);
+ ASSERT_HEX(unsigned long, deadbeefUL);
+ ASSERT_HEX(long long, 12345678deadbeefLL);
+ ASSERT_HEX(unsigned long long, cafebabedeadbeefULL);
+
+#undef ASSERT_HEX
}
TEST(RE2, OctalTests) {
- VLOG(1) << "octal tests";
-
-#define CHECK_OCTAL(type, value) \
- do { \
- type v; \
- CHECK(RE2::FullMatch(#value, "([0-7]+)[uUlL]*", RE2::Octal(&v))); \
- CHECK_EQ(v, 0 ## value); \
- CHECK(RE2::FullMatch("0" #value, "([0-9a-fA-FxX]+)[uUlL]*", RE2::CRadix(&v))); \
- CHECK_EQ(v, 0 ## value); \
- } while(0)
-
- CHECK_OCTAL(short, 77777);
- CHECK_OCTAL(unsigned short, 177777U);
- CHECK_OCTAL(int, 17777777777);
- CHECK_OCTAL(unsigned int, 37777777777U);
- CHECK_OCTAL(long, 17777777777L);
- CHECK_OCTAL(unsigned long, 37777777777UL);
- CHECK_OCTAL(long long, 777777777777777777777LL);
- CHECK_OCTAL(unsigned long long, 1777777777777777777777ULL);
-
-#undef CHECK_OCTAL
+#define ASSERT_OCTAL(type, value) \
+ do { \
+ type v; \
+ ASSERT_TRUE(RE2::FullMatch(#value, "([0-7]+)[uUlL]*", RE2::Octal(&v))); \
+ ASSERT_EQ(v, 0##value); \
+ ASSERT_TRUE(RE2::FullMatch("0" #value, "([0-9a-fA-FxX]+)[uUlL]*", \
+ RE2::CRadix(&v))); \
+ ASSERT_EQ(v, 0##value); \
+ } while (0)
+
+ ASSERT_OCTAL(short, 77777);
+ ASSERT_OCTAL(unsigned short, 177777U);
+ ASSERT_OCTAL(int, 17777777777);
+ ASSERT_OCTAL(unsigned int, 37777777777U);
+ ASSERT_OCTAL(long, 17777777777L);
+ ASSERT_OCTAL(unsigned long, 37777777777UL);
+ ASSERT_OCTAL(long long, 777777777777777777777LL);
+ ASSERT_OCTAL(unsigned long long, 1777777777777777777777ULL);
+
+#undef ASSERT_OCTAL
}
TEST(RE2, DecimalTests) {
- VLOG(1) << "decimal tests";
-
-#define CHECK_DECIMAL(type, value) \
- do { \
- type v; \
- CHECK(RE2::FullMatch(#value, "(-?[0-9]+)[uUlL]*", &v)); \
- CHECK_EQ(v, value); \
- CHECK(RE2::FullMatch(#value, "(-?[0-9a-fA-FxX]+)[uUlL]*", RE2::CRadix(&v))); \
- CHECK_EQ(v, value); \
- } while(0)
-
- CHECK_DECIMAL(short, -1);
- CHECK_DECIMAL(unsigned short, 9999);
- CHECK_DECIMAL(int, -1000);
- CHECK_DECIMAL(unsigned int, 12345U);
- CHECK_DECIMAL(long, -10000000L);
- CHECK_DECIMAL(unsigned long, 3083324652U);
- CHECK_DECIMAL(long long, -100000000000000LL);
- CHECK_DECIMAL(unsigned long long, 1234567890987654321ULL);
-
-#undef CHECK_DECIMAL
+#define ASSERT_DECIMAL(type, value) \
+ do { \
+ type v; \
+ ASSERT_TRUE(RE2::FullMatch(#value, "(-?[0-9]+)[uUlL]*", &v)); \
+ ASSERT_EQ(v, value); \
+ ASSERT_TRUE( \
+ RE2::FullMatch(#value, "(-?[0-9a-fA-FxX]+)[uUlL]*", RE2::CRadix(&v))); \
+ ASSERT_EQ(v, value); \
+ } while (0)
+
+ ASSERT_DECIMAL(short, -1);
+ ASSERT_DECIMAL(unsigned short, 9999);
+ ASSERT_DECIMAL(int, -1000);
+ ASSERT_DECIMAL(unsigned int, 12345U);
+ ASSERT_DECIMAL(long, -10000000L);
+ ASSERT_DECIMAL(unsigned long, 3083324652U);
+ ASSERT_DECIMAL(long long, -100000000000000LL);
+ ASSERT_DECIMAL(unsigned long long, 1234567890987654321ULL);
+
+#undef ASSERT_DECIMAL
}
TEST(RE2, Replace) {
- VLOG(1) << "TestReplace";
-
struct ReplaceTest {
const char *regexp;
const char *rewrite;
@@ -173,15 +175,14 @@ TEST(RE2, Replace) {
{ "", NULL, NULL, NULL, NULL, 0 }
};
- for (const ReplaceTest *t = tests; t->original != NULL; ++t) {
- VLOG(1) << StringPrintf("\"%s\" =~ s/%s/%s/g", t->original, t->regexp, t->rewrite);
+ for (const ReplaceTest* t = tests; t->original != NULL; t++) {
string one(t->original);
- CHECK(RE2::Replace(&one, t->regexp, t->rewrite));
- CHECK_EQ(one, t->single);
+ ASSERT_TRUE(RE2::Replace(&one, t->regexp, t->rewrite));
+ ASSERT_EQ(one, t->single);
string all(t->original);
- CHECK_EQ(RE2::GlobalReplace(&all, t->regexp, t->rewrite), t->greplace_count)
+ ASSERT_EQ(RE2::GlobalReplace(&all, t->regexp, t->rewrite), t->greplace_count)
<< "Got: " << all;
- CHECK_EQ(all, t->global);
+ ASSERT_EQ(all, t->global);
}
}
@@ -210,34 +211,30 @@ TEST(CheckRewriteString, all) {
}
TEST(RE2, Extract) {
- VLOG(1) << "TestExtract";
-
string s;
- CHECK(RE2::Extract("boris@kremvax.ru", "(.*)@([^.]*)", "\\2!\\1", &s));
- CHECK_EQ(s, "kremvax!boris");
+ ASSERT_TRUE(RE2::Extract("boris@kremvax.ru", "(.*)@([^.]*)", "\\2!\\1", &s));
+ ASSERT_EQ(s, "kremvax!boris");
- CHECK(RE2::Extract("foo", ".*", "'\\0'", &s));
- CHECK_EQ(s, "'foo'");
+ ASSERT_TRUE(RE2::Extract("foo", ".*", "'\\0'", &s));
+ ASSERT_EQ(s, "'foo'");
// check that false match doesn't overwrite
- CHECK(!RE2::Extract("baz", "bar", "'\\0'", &s));
- CHECK_EQ(s, "'foo'");
+ ASSERT_FALSE(RE2::Extract("baz", "bar", "'\\0'", &s));
+ ASSERT_EQ(s, "'foo'");
}
TEST(RE2, Consume) {
- VLOG(1) << "TestConsume";
-
RE2 r("\\s*(\\w+)"); // matches a word, possibly proceeded by whitespace
string word;
string s(" aaa b!@#$@#$cccc");
StringPiece input(s);
- CHECK(RE2::Consume(&input, r, &word));
- CHECK_EQ(word, "aaa") << " input: " << input;
- CHECK(RE2::Consume(&input, r, &word));
- CHECK_EQ(word, "b") << " input: " << input;
- CHECK(! RE2::Consume(&input, r, &word)) << " input: " << input;
+ ASSERT_TRUE(RE2::Consume(&input, r, &word));
+ ASSERT_EQ(word, "aaa") << " input: " << input;
+ ASSERT_TRUE(RE2::Consume(&input, r, &word));
+ ASSERT_EQ(word, "b") << " input: " << input;
+ ASSERT_FALSE(RE2::Consume(&input, r, &word)) << " input: " << input;
}
TEST(RE2, ConsumeN) {
@@ -265,28 +262,26 @@ TEST(RE2, ConsumeN) {
}
TEST(RE2, FindAndConsume) {
- VLOG(1) << "TestFindAndConsume";
-
RE2 r("(\\w+)"); // matches a word
string word;
string s(" aaa b!@#$@#$cccc");
StringPiece input(s);
- CHECK(RE2::FindAndConsume(&input, r, &word));
- CHECK_EQ(word, "aaa");
- CHECK(RE2::FindAndConsume(&input, r, &word));
- CHECK_EQ(word, "b");
- CHECK(RE2::FindAndConsume(&input, r, &word));
- CHECK_EQ(word, "cccc");
- CHECK(! RE2::FindAndConsume(&input, r, &word));
+ ASSERT_TRUE(RE2::FindAndConsume(&input, r, &word));
+ ASSERT_EQ(word, "aaa");
+ ASSERT_TRUE(RE2::FindAndConsume(&input, r, &word));
+ ASSERT_EQ(word, "b");
+ ASSERT_TRUE(RE2::FindAndConsume(&input, r, &word));
+ ASSERT_EQ(word, "cccc");
+ ASSERT_FALSE(RE2::FindAndConsume(&input, r, &word));
// Check that FindAndConsume works without any submatches.
// Earlier version used uninitialized data for
// length to consume.
input = "aaa";
- CHECK(RE2::FindAndConsume(&input, "aaa"));
- CHECK_EQ(input, "");
+ ASSERT_TRUE(RE2::FindAndConsume(&input, "aaa"));
+ ASSERT_EQ(input, "");
}
TEST(RE2, FindAndConsumeN) {
@@ -314,30 +309,28 @@ TEST(RE2, FindAndConsumeN) {
}
TEST(RE2, MatchNumberPeculiarity) {
- VLOG(1) << "TestMatchNumberPeculiarity";
-
RE2 r("(foo)|(bar)|(baz)");
string word1;
string word2;
string word3;
- CHECK(RE2::PartialMatch("foo", r, &word1, &word2, &word3));
- CHECK_EQ(word1, "foo");
- CHECK_EQ(word2, "");
- CHECK_EQ(word3, "");
- CHECK(RE2::PartialMatch("bar", r, &word1, &word2, &word3));
- CHECK_EQ(word1, "");
- CHECK_EQ(word2, "bar");
- CHECK_EQ(word3, "");
- CHECK(RE2::PartialMatch("baz", r, &word1, &word2, &word3));
- CHECK_EQ(word1, "");
- CHECK_EQ(word2, "");
- CHECK_EQ(word3, "baz");
- CHECK(!RE2::PartialMatch("f", r, &word1, &word2, &word3));
+ ASSERT_TRUE(RE2::PartialMatch("foo", r, &word1, &word2, &word3));
+ ASSERT_EQ(word1, "foo");
+ ASSERT_EQ(word2, "");
+ ASSERT_EQ(word3, "");
+ ASSERT_TRUE(RE2::PartialMatch("bar", r, &word1, &word2, &word3));
+ ASSERT_EQ(word1, "");
+ ASSERT_EQ(word2, "bar");
+ ASSERT_EQ(word3, "");
+ ASSERT_TRUE(RE2::PartialMatch("baz", r, &word1, &word2, &word3));
+ ASSERT_EQ(word1, "");
+ ASSERT_EQ(word2, "");
+ ASSERT_EQ(word3, "baz");
+ ASSERT_FALSE(RE2::PartialMatch("f", r, &word1, &word2, &word3));
string a;
- CHECK(RE2::FullMatch("hello", "(foo)|hello", &a));
- CHECK_EQ(a, "");
+ ASSERT_TRUE(RE2::FullMatch("hello", "(foo)|hello", &a));
+ ASSERT_EQ(a, "");
}
TEST(RE2, Match) {
@@ -346,32 +339,32 @@ TEST(RE2, Match) {
// No match.
StringPiece s = "zyzzyva";
- CHECK(!re.Match(s, 0, s.size(), RE2::UNANCHORED,
- group, arraysize(group)));
+ ASSERT_FALSE(
+ re.Match(s, 0, s.size(), RE2::UNANCHORED, group, arraysize(group)));
// Matches and extracts.
s = "a chrisr:9000 here";
- CHECK(re.Match(s, 0, s.size(), RE2::UNANCHORED,
- group, arraysize(group)));
- CHECK_EQ(group[0], "chrisr:9000");
- CHECK_EQ(group[1], "chrisr:9000");
- CHECK_EQ(group[2], "chrisr");
- CHECK_EQ(group[3], "9000");
+ ASSERT_TRUE(
+ re.Match(s, 0, s.size(), RE2::UNANCHORED, group, arraysize(group)));
+ ASSERT_EQ(group[0], "chrisr:9000");
+ ASSERT_EQ(group[1], "chrisr:9000");
+ ASSERT_EQ(group[2], "chrisr");
+ ASSERT_EQ(group[3], "9000");
string all, host;
int port;
- CHECK(RE2::PartialMatch("a chrisr:9000 here", re, &all, &host, &port));
- CHECK_EQ(all, "chrisr:9000");
- CHECK_EQ(host, "chrisr");
- CHECK_EQ(port, 9000);
+ ASSERT_TRUE(RE2::PartialMatch("a chrisr:9000 here", re, &all, &host, &port));
+ ASSERT_EQ(all, "chrisr:9000");
+ ASSERT_EQ(host, "chrisr");
+ ASSERT_EQ(port, 9000);
}
-static void TestRecursion(int size, const char *pattern) {
+static void TestRecursion(int size, const char* pattern) {
// Fill up a string repeating the pattern given
string domain;
domain.resize(size);
- int patlen = strlen(pattern);
- for (int i = 0; i < size; ++i) {
+ size_t patlen = strlen(pattern);
+ for (int i = 0; i < size; i++) {
domain[i] = pattern[i % patlen];
}
// Just make sure it doesn't crash due to too much recursion.
@@ -381,22 +374,23 @@ static void TestRecursion(int size, const char *pattern) {
// A meta-quoted string, interpreted as a pattern, should always match
// the original unquoted string.
-static void TestQuoteMeta(string unquoted,
+static void TestQuoteMeta(const string& unquoted,
const RE2::Options& options = RE2::DefaultOptions) {
string quoted = RE2::QuoteMeta(unquoted);
RE2 re(quoted, options);
- EXPECT_TRUE_M(RE2::FullMatch(unquoted, re),
- "Unquoted='" + unquoted + "', quoted='" + quoted + "'.");
+ EXPECT_TRUE(RE2::FullMatch(unquoted, re))
+ << "Unquoted='" << unquoted << "', quoted='" << quoted << "'.";
}
// A meta-quoted string, interpreted as a pattern, should always match
// the original unquoted string.
-static void NegativeTestQuoteMeta(string unquoted, string should_not_match,
- const RE2::Options& options = RE2::DefaultOptions) {
+static void NegativeTestQuoteMeta(
+ const string& unquoted, const string& should_not_match,
+ const RE2::Options& options = RE2::DefaultOptions) {
string quoted = RE2::QuoteMeta(unquoted);
RE2 re(quoted, options);
- EXPECT_FALSE_M(RE2::FullMatch(should_not_match, re),
- "Unquoted='" + unquoted + "', quoted='" + quoted + "'.");
+ EXPECT_FALSE(RE2::FullMatch(should_not_match, re))
+ << "Unquoted='" << unquoted << "', quoted='" << quoted << "'.";
}
// Tests that quoted meta characters match their original strings,
@@ -462,11 +456,57 @@ TEST(QuoteMeta, HasNull) {
TEST(ProgramSize, BigProgram) {
RE2 re_simple("simple regexp");
RE2 re_medium("medium.*regexp");
- RE2 re_complex("hard.{1,128}regexp");
+ RE2 re_complex("complex.{1,128}regexp");
+
+ ASSERT_GT(re_simple.ProgramSize(), 0);
+ ASSERT_GT(re_medium.ProgramSize(), re_simple.ProgramSize());
+ ASSERT_GT(re_complex.ProgramSize(), re_medium.ProgramSize());
+
+ ASSERT_GT(re_simple.ReverseProgramSize(), 0);
+ ASSERT_GT(re_medium.ReverseProgramSize(), re_simple.ReverseProgramSize());
+ ASSERT_GT(re_complex.ReverseProgramSize(), re_medium.ReverseProgramSize());
+}
- CHECK_GT(re_simple.ProgramSize(), 0);
- CHECK_GT(re_medium.ProgramSize(), re_simple.ProgramSize());
- CHECK_GT(re_complex.ProgramSize(), re_medium.ProgramSize());
+TEST(ProgramFanout, BigProgram) {
+ RE2 re1("(?:(?:(?:(?:(?:.)?){1})*)+)");
+ RE2 re10("(?:(?:(?:(?:(?:.)?){10})*)+)");
+ RE2 re100("(?:(?:(?:(?:(?:.)?){100})*)+)");
+ RE2 re1000("(?:(?:(?:(?:(?:.)?){1000})*)+)");
+
+ std::map<int, int> histogram;
+
+ // 3 is the largest non-empty bucket and has 1 element.
+ ASSERT_EQ(3, re1.ProgramFanout(&histogram));
+ ASSERT_EQ(1, histogram[3]);
+
+ // 7 is the largest non-empty bucket and has 10 elements.
+ ASSERT_EQ(7, re10.ProgramFanout(&histogram));
+ ASSERT_EQ(10, histogram[7]);
+
+ // 10 is the largest non-empty bucket and has 100 elements.
+ ASSERT_EQ(10, re100.ProgramFanout(&histogram));
+ ASSERT_EQ(100, histogram[10]);
+
+ // 13 is the largest non-empty bucket and has 1000 elements.
+ ASSERT_EQ(13, re1000.ProgramFanout(&histogram));
+ ASSERT_EQ(1000, histogram[13]);
+
+ // 2 is the largest non-empty bucket and has 3 elements.
+ // This differs from the others due to how reverse `.' works.
+ ASSERT_EQ(2, re1.ReverseProgramFanout(&histogram));
+ ASSERT_EQ(3, histogram[2]);
+
+ // 5 is the largest non-empty bucket and has 10 elements.
+ ASSERT_EQ(5, re10.ReverseProgramFanout(&histogram));
+ ASSERT_EQ(10, histogram[5]);
+
+ // 9 is the largest non-empty bucket and has 100 elements.
+ ASSERT_EQ(9, re100.ReverseProgramFanout(&histogram));
+ ASSERT_EQ(100, histogram[9]);
+
+ // 12 is the largest non-empty bucket and has 1000 elements.
+ ASSERT_EQ(12, re1000.ReverseProgramFanout(&histogram));
+ ASSERT_EQ(1000, histogram[12]);
}
// Issue 956519: handling empty character sets was
@@ -480,44 +520,87 @@ TEST(EmptyCharset, Fuzz) {
"[^\\D[:digit:]]"
};
for (int i = 0; i < arraysize(empties); i++)
- CHECK(!RE2(empties[i]).Match("abc", 0, 3, RE2::UNANCHORED, NULL, 0));
+ ASSERT_FALSE(RE2(empties[i]).Match("abc", 0, 3, RE2::UNANCHORED, NULL, 0));
+}
+
+// Bitstate assumes that kInstFail instructions in
+// alternations or capture groups have been "compiled away".
+TEST(EmptyCharset, BitstateAssumptions) {
+ // Captures trigger use of Bitstate.
+ static const char *nop_empties[] = {
+ "((((()))))" "[^\\S\\s]?",
+ "((((()))))" "([^\\S\\s])?",
+ "((((()))))" "([^\\S\\s]|[^\\S\\s])?",
+ "((((()))))" "(([^\\S\\s]|[^\\S\\s])|)"
+ };
+ StringPiece group[6];
+ for (int i = 0; i < arraysize(nop_empties); i++)
+ ASSERT_TRUE(RE2(nop_empties[i]).Match("", 0, 0, RE2::UNANCHORED, group, 6));
}
// Test that named groups work correctly.
TEST(Capture, NamedGroups) {
{
RE2 re("(hello world)");
- CHECK_EQ(re.NumberOfCapturingGroups(), 1);
- const map<string, int>& m = re.NamedCapturingGroups();
- CHECK_EQ(m.size(), 0);
+ ASSERT_EQ(re.NumberOfCapturingGroups(), 1);
+ const std::map<string, int>& m = re.NamedCapturingGroups();
+ ASSERT_EQ(m.size(), 0);
}
{
RE2 re("(?P<A>expr(?P<B>expr)(?P<C>expr))((expr)(?P<D>expr))");
- CHECK_EQ(re.NumberOfCapturingGroups(), 6);
- const map<string, int>& m = re.NamedCapturingGroups();
- CHECK_EQ(m.size(), 4);
- CHECK_EQ(m.find("A")->second, 1);
- CHECK_EQ(m.find("B")->second, 2);
- CHECK_EQ(m.find("C")->second, 3);
- CHECK_EQ(m.find("D")->second, 6); // $4 and $5 are anonymous
+ ASSERT_EQ(re.NumberOfCapturingGroups(), 6);
+ const std::map<string, int>& m = re.NamedCapturingGroups();
+ ASSERT_EQ(m.size(), 4);
+ ASSERT_EQ(m.find("A")->second, 1);
+ ASSERT_EQ(m.find("B")->second, 2);
+ ASSERT_EQ(m.find("C")->second, 3);
+ ASSERT_EQ(m.find("D")->second, 6); // $4 and $5 are anonymous
}
}
+TEST(RE2, CapturedGroupTest) {
+ RE2 re("directions from (?P<S>.*) to (?P<D>.*)");
+ int num_groups = re.NumberOfCapturingGroups();
+ EXPECT_EQ(2, num_groups);
+ string args[4];
+ RE2::Arg arg0(&args[0]);
+ RE2::Arg arg1(&args[1]);
+ RE2::Arg arg2(&args[2]);
+ RE2::Arg arg3(&args[3]);
+
+ const RE2::Arg* const matches[4] = {&arg0, &arg1, &arg2, &arg3};
+ EXPECT_TRUE(RE2::FullMatchN("directions from mountain view to san jose",
+ re, matches, num_groups));
+ const std::map<string, int>& named_groups = re.NamedCapturingGroups();
+ EXPECT_TRUE(named_groups.find("S") != named_groups.end());
+ EXPECT_TRUE(named_groups.find("D") != named_groups.end());
+
+ // The named group index is 1-based.
+ int source_group_index = named_groups.find("S")->second;
+ int destination_group_index = named_groups.find("D")->second;
+ EXPECT_EQ(1, source_group_index);
+ EXPECT_EQ(2, destination_group_index);
+
+ // The args is zero-based.
+ EXPECT_EQ("mountain view", args[source_group_index - 1]);
+ EXPECT_EQ("san jose", args[destination_group_index - 1]);
+}
+
TEST(RE2, FullMatchWithNoArgs) {
- CHECK(RE2::FullMatch("h", "h"));
- CHECK(RE2::FullMatch("hello", "hello"));
- CHECK(RE2::FullMatch("hello", "h.*o"));
- CHECK(!RE2::FullMatch("othello", "h.*o")); // Must be anchored at front
- CHECK(!RE2::FullMatch("hello!", "h.*o")); // Must be anchored at end
+ ASSERT_TRUE(RE2::FullMatch("h", "h"));
+ ASSERT_TRUE(RE2::FullMatch("hello", "hello"));
+ ASSERT_TRUE(RE2::FullMatch("hello", "h.*o"));
+ ASSERT_FALSE(RE2::FullMatch("othello", "h.*o")); // Must be anchored at front
+ ASSERT_FALSE(RE2::FullMatch("hello!", "h.*o")); // Must be anchored at end
}
TEST(RE2, PartialMatch) {
- CHECK(RE2::PartialMatch("x", "x"));
- CHECK(RE2::PartialMatch("hello", "h.*o"));
- CHECK(RE2::PartialMatch("othello", "h.*o"));
- CHECK(RE2::PartialMatch("hello!", "h.*o"));
- CHECK(RE2::PartialMatch("x", "((((((((((((((((((((x))))))))))))))))))))"));
+ ASSERT_TRUE(RE2::PartialMatch("x", "x"));
+ ASSERT_TRUE(RE2::PartialMatch("hello", "h.*o"));
+ ASSERT_TRUE(RE2::PartialMatch("othello", "h.*o"));
+ ASSERT_TRUE(RE2::PartialMatch("hello!", "h.*o"));
+ ASSERT_TRUE(RE2::PartialMatch("x", "((((((((((((((((((((x))))))))))))))))))))"));
}
TEST(RE2, PartialMatchN) {
@@ -546,62 +629,62 @@ TEST(RE2, PartialMatchN) {
TEST(RE2, FullMatchZeroArg) {
// Zero-arg
- CHECK(RE2::FullMatch("1001", "\\d+"));
+ ASSERT_TRUE(RE2::FullMatch("1001", "\\d+"));
}
TEST(RE2, FullMatchOneArg) {
int i;
// Single-arg
- CHECK(RE2::FullMatch("1001", "(\\d+)", &i));
- CHECK_EQ(i, 1001);
- CHECK(RE2::FullMatch("-123", "(-?\\d+)", &i));
- CHECK_EQ(i, -123);
- CHECK(!RE2::FullMatch("10", "()\\d+", &i));
- CHECK(!RE2::FullMatch("1234567890123456789012345678901234567890",
- "(\\d+)", &i));
+ ASSERT_TRUE(RE2::FullMatch("1001", "(\\d+)", &i));
+ ASSERT_EQ(i, 1001);
+ ASSERT_TRUE(RE2::FullMatch("-123", "(-?\\d+)", &i));
+ ASSERT_EQ(i, -123);
+ ASSERT_FALSE(RE2::FullMatch("10", "()\\d+", &i));
+ ASSERT_FALSE(
+ RE2::FullMatch("1234567890123456789012345678901234567890", "(\\d+)", &i));
}
TEST(RE2, FullMatchIntegerArg) {
int i;
// Digits surrounding integer-arg
- CHECK(RE2::FullMatch("1234", "1(\\d*)4", &i));
- CHECK_EQ(i, 23);
- CHECK(RE2::FullMatch("1234", "(\\d)\\d+", &i));
- CHECK_EQ(i, 1);
- CHECK(RE2::FullMatch("-1234", "(-\\d)\\d+", &i));
- CHECK_EQ(i, -1);
- CHECK(RE2::PartialMatch("1234", "(\\d)", &i));
- CHECK_EQ(i, 1);
- CHECK(RE2::PartialMatch("-1234", "(-\\d)", &i));
- CHECK_EQ(i, -1);
+ ASSERT_TRUE(RE2::FullMatch("1234", "1(\\d*)4", &i));
+ ASSERT_EQ(i, 23);
+ ASSERT_TRUE(RE2::FullMatch("1234", "(\\d)\\d+", &i));
+ ASSERT_EQ(i, 1);
+ ASSERT_TRUE(RE2::FullMatch("-1234", "(-\\d)\\d+", &i));
+ ASSERT_EQ(i, -1);
+ ASSERT_TRUE(RE2::PartialMatch("1234", "(\\d)", &i));
+ ASSERT_EQ(i, 1);
+ ASSERT_TRUE(RE2::PartialMatch("-1234", "(-\\d)", &i));
+ ASSERT_EQ(i, -1);
}
TEST(RE2, FullMatchStringArg) {
string s;
// String-arg
- CHECK(RE2::FullMatch("hello", "h(.*)o", &s));
- CHECK_EQ(s, string("ell"));
+ ASSERT_TRUE(RE2::FullMatch("hello", "h(.*)o", &s));
+ ASSERT_EQ(s, string("ell"));
}
TEST(RE2, FullMatchStringPieceArg) {
int i;
// StringPiece-arg
StringPiece sp;
- CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &sp, &i));
- CHECK_EQ(sp.size(), 4);
- CHECK(memcmp(sp.data(), "ruby", 4) == 0);
- CHECK_EQ(i, 1234);
+ ASSERT_TRUE(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &sp, &i));
+ ASSERT_EQ(sp.size(), 4);
+ ASSERT_TRUE(memcmp(sp.data(), "ruby", 4) == 0);
+ ASSERT_EQ(i, 1234);
}
TEST(RE2, FullMatchMultiArg) {
int i;
string s;
// Multi-arg
- CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s, &i));
- CHECK_EQ(s, string("ruby"));
- CHECK_EQ(i, 1234);
+ ASSERT_TRUE(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s, &i));
+ ASSERT_EQ(s, string("ruby"));
+ ASSERT_EQ(i, 1234);
}
TEST(RE2, FullMatchN) {
@@ -631,35 +714,45 @@ TEST(RE2, FullMatchN) {
TEST(RE2, FullMatchIgnoredArg) {
int i;
string s;
- // Ignored arg
- CHECK(RE2::FullMatch("ruby:1234", "(\\w+)(:)(\\d+)", &s, (void*)NULL, &i));
- CHECK_EQ(s, string("ruby"));
- CHECK_EQ(i, 1234);
+
+ // Old-school NULL should be ignored.
+ ASSERT_TRUE(
+ RE2::FullMatch("ruby:1234", "(\\w+)(:)(\\d+)", &s, (void*)NULL, &i));
+ ASSERT_EQ(s, string("ruby"));
+ ASSERT_EQ(i, 1234);
+
+ // C++11 nullptr should also be ignored.
+ ASSERT_TRUE(RE2::FullMatch("rubz:1235", "(\\w+)(:)(\\d+)", &s, nullptr, &i));
+ ASSERT_EQ(s, string("rubz"));
+ ASSERT_EQ(i, 1235);
}
TEST(RE2, FullMatchTypedNullArg) {
string s;
// Ignore non-void* NULL arg
- CHECK(RE2::FullMatch("hello", "he(.*)lo", (char*)NULL));
- CHECK(RE2::FullMatch("hello", "h(.*)o", (string*)NULL));
- CHECK(RE2::FullMatch("hello", "h(.*)o", (StringPiece*)NULL));
- CHECK(RE2::FullMatch("1234", "(.*)", (int*)NULL));
- CHECK(RE2::FullMatch("1234567890123456", "(.*)", (long long*)NULL));
- CHECK(RE2::FullMatch("123.4567890123456", "(.*)", (double*)NULL));
- CHECK(RE2::FullMatch("123.4567890123456", "(.*)", (float*)NULL));
+ ASSERT_TRUE(RE2::FullMatch("hello", "he(.*)lo", (char*)NULL));
+ ASSERT_TRUE(RE2::FullMatch("hello", "h(.*)o", (string*)NULL));
+ ASSERT_TRUE(RE2::FullMatch("hello", "h(.*)o", (StringPiece*)NULL));
+ ASSERT_TRUE(RE2::FullMatch("1234", "(.*)", (int*)NULL));
+ ASSERT_TRUE(RE2::FullMatch("1234567890123456", "(.*)", (long long*)NULL));
+ ASSERT_TRUE(RE2::FullMatch("123.4567890123456", "(.*)", (double*)NULL));
+ ASSERT_TRUE(RE2::FullMatch("123.4567890123456", "(.*)", (float*)NULL));
// Fail on non-void* NULL arg if the match doesn't parse for the given type.
- CHECK(!RE2::FullMatch("hello", "h(.*)lo", &s, (char*)NULL));
- CHECK(!RE2::FullMatch("hello", "(.*)", (int*)NULL));
- CHECK(!RE2::FullMatch("1234567890123456", "(.*)", (int*)NULL));
- CHECK(!RE2::FullMatch("hello", "(.*)", (double*)NULL));
- CHECK(!RE2::FullMatch("hello", "(.*)", (float*)NULL));
+ ASSERT_FALSE(RE2::FullMatch("hello", "h(.*)lo", &s, (char*)NULL));
+ ASSERT_FALSE(RE2::FullMatch("hello", "(.*)", (int*)NULL));
+ ASSERT_FALSE(RE2::FullMatch("1234567890123456", "(.*)", (int*)NULL));
+ ASSERT_FALSE(RE2::FullMatch("hello", "(.*)", (double*)NULL));
+ ASSERT_FALSE(RE2::FullMatch("hello", "(.*)", (float*)NULL));
}
// Check that numeric parsing code does not read past the end of
// the number being parsed.
+// This implementation requires mmap(2) et al. and thus cannot
+// be used unless they are available.
TEST(RE2, NULTerminated) {
+#if defined(_POSIX_MAPPED_FILES) && _POSIX_MAPPED_FILES > 0
char *v;
int x;
long pagesize = sysconf(_SC_PAGE_SIZE);
@@ -669,129 +762,131 @@ TEST(RE2, NULTerminated) {
#endif
v = static_cast<char*>(mmap(NULL, 2*pagesize, PROT_READ|PROT_WRITE,
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0));
- CHECK(v != reinterpret_cast<char*>(-1));
+ ASSERT_TRUE(v != reinterpret_cast<char*>(-1));
LOG(INFO) << "Memory at " << (void*)v;
- CHECK_EQ(munmap(v + pagesize, pagesize), 0) << " error " << errno;
+ ASSERT_EQ(munmap(v + pagesize, pagesize), 0) << " error " << errno;
v[pagesize - 1] = '1';
x = 0;
- CHECK(RE2::FullMatch(StringPiece(v + pagesize - 1, 1), "(.*)", &x));
- CHECK_EQ(x, 1);
+ ASSERT_TRUE(RE2::FullMatch(StringPiece(v + pagesize - 1, 1), "(.*)", &x));
+ ASSERT_EQ(x, 1);
+#endif
}
TEST(RE2, FullMatchTypeTests) {
// Type tests
- string zeros(100, '0');
+ string zeros(1000, '0');
{
char c;
- CHECK(RE2::FullMatch("Hello", "(H)ello", &c));
- CHECK_EQ(c, 'H');
+ ASSERT_TRUE(RE2::FullMatch("Hello", "(H)ello", &c));
+ ASSERT_EQ(c, 'H');
}
{
unsigned char c;
- CHECK(RE2::FullMatch("Hello", "(H)ello", &c));
- CHECK_EQ(c, static_cast<unsigned char>('H'));
+ ASSERT_TRUE(RE2::FullMatch("Hello", "(H)ello", &c));
+ ASSERT_EQ(c, static_cast<unsigned char>('H'));
}
{
- int16 v;
- CHECK(RE2::FullMatch("100", "(-?\\d+)", &v)); CHECK_EQ(v, 100);
- CHECK(RE2::FullMatch("-100", "(-?\\d+)", &v)); CHECK_EQ(v, -100);
- CHECK(RE2::FullMatch("32767", "(-?\\d+)", &v)); CHECK_EQ(v, 32767);
- CHECK(RE2::FullMatch("-32768", "(-?\\d+)", &v)); CHECK_EQ(v, -32768);
- CHECK(!RE2::FullMatch("-32769", "(-?\\d+)", &v));
- CHECK(!RE2::FullMatch("32768", "(-?\\d+)", &v));
+ int16_t v;
+ ASSERT_TRUE(RE2::FullMatch("100", "(-?\\d+)", &v)); ASSERT_EQ(v, 100);
+ ASSERT_TRUE(RE2::FullMatch("-100", "(-?\\d+)", &v)); ASSERT_EQ(v, -100);
+ ASSERT_TRUE(RE2::FullMatch("32767", "(-?\\d+)", &v)); ASSERT_EQ(v, 32767);
+ ASSERT_TRUE(RE2::FullMatch("-32768", "(-?\\d+)", &v)); ASSERT_EQ(v, -32768);
+ ASSERT_FALSE(RE2::FullMatch("-32769", "(-?\\d+)", &v));
+ ASSERT_FALSE(RE2::FullMatch("32768", "(-?\\d+)", &v));
}
{
- uint16 v;
- CHECK(RE2::FullMatch("100", "(\\d+)", &v)); CHECK_EQ(v, 100);
- CHECK(RE2::FullMatch("32767", "(\\d+)", &v)); CHECK_EQ(v, 32767);
- CHECK(RE2::FullMatch("65535", "(\\d+)", &v)); CHECK_EQ(v, 65535);
- CHECK(!RE2::FullMatch("65536", "(\\d+)", &v));
+ uint16_t v;
+ ASSERT_TRUE(RE2::FullMatch("100", "(\\d+)", &v)); ASSERT_EQ(v, 100);
+ ASSERT_TRUE(RE2::FullMatch("32767", "(\\d+)", &v)); ASSERT_EQ(v, 32767);
+ ASSERT_TRUE(RE2::FullMatch("65535", "(\\d+)", &v)); ASSERT_EQ(v, 65535);
+ ASSERT_FALSE(RE2::FullMatch("65536", "(\\d+)", &v));
}
{
- int32 v;
- static const int32 max = 0x7fffffff;
- static const int32 min = -max - 1;
- CHECK(RE2::FullMatch("100", "(-?\\d+)", &v)); CHECK_EQ(v, 100);
- CHECK(RE2::FullMatch("-100", "(-?\\d+)", &v)); CHECK_EQ(v, -100);
- CHECK(RE2::FullMatch("2147483647", "(-?\\d+)", &v)); CHECK_EQ(v, max);
- CHECK(RE2::FullMatch("-2147483648", "(-?\\d+)", &v)); CHECK_EQ(v, min);
- CHECK(!RE2::FullMatch("-2147483649", "(-?\\d+)", &v));
- CHECK(!RE2::FullMatch("2147483648", "(-?\\d+)", &v));
-
- CHECK(RE2::FullMatch(zeros + "2147483647", "(-?\\d+)", &v));
- CHECK_EQ(v, max);
- CHECK(RE2::FullMatch("-" + zeros + "2147483648", "(-?\\d+)", &v));
- CHECK_EQ(v, min);
-
- CHECK(!RE2::FullMatch("-" + zeros + "2147483649", "(-?\\d+)", &v));
- CHECK(RE2::FullMatch("0x7fffffff", "(.*)", RE2::CRadix(&v)));
- CHECK_EQ(v, max);
- CHECK(!RE2::FullMatch("000x7fffffff", "(.*)", RE2::CRadix(&v)));
+ int32_t v;
+ static const int32_t max = INT32_C(0x7fffffff);
+ static const int32_t min = -max - 1;
+ ASSERT_TRUE(RE2::FullMatch("100", "(-?\\d+)", &v)); ASSERT_EQ(v, 100);
+ ASSERT_TRUE(RE2::FullMatch("-100", "(-?\\d+)", &v)); ASSERT_EQ(v, -100);
+ ASSERT_TRUE(RE2::FullMatch("2147483647", "(-?\\d+)", &v)); ASSERT_EQ(v, max);
+ ASSERT_TRUE(RE2::FullMatch("-2147483648", "(-?\\d+)", &v)); ASSERT_EQ(v, min);
+ ASSERT_FALSE(RE2::FullMatch("-2147483649", "(-?\\d+)", &v));
+ ASSERT_FALSE(RE2::FullMatch("2147483648", "(-?\\d+)", &v));
+
+ ASSERT_TRUE(RE2::FullMatch(zeros + "2147483647", "(-?\\d+)", &v));
+ ASSERT_EQ(v, max);
+ ASSERT_TRUE(RE2::FullMatch("-" + zeros + "2147483648", "(-?\\d+)", &v));
+ ASSERT_EQ(v, min);
+
+ ASSERT_FALSE(RE2::FullMatch("-" + zeros + "2147483649", "(-?\\d+)", &v));
+ ASSERT_TRUE(RE2::FullMatch("0x7fffffff", "(.*)", RE2::CRadix(&v)));
+ ASSERT_EQ(v, max);
+ ASSERT_FALSE(RE2::FullMatch("000x7fffffff", "(.*)", RE2::CRadix(&v)));
}
{
- uint32 v;
- static const uint32 max = 0xfffffffful;
- CHECK(RE2::FullMatch("100", "(\\d+)", &v)); CHECK_EQ(v, 100);
- CHECK(RE2::FullMatch("4294967295", "(\\d+)", &v)); CHECK_EQ(v, max);
- CHECK(!RE2::FullMatch("4294967296", "(\\d+)", &v));
- CHECK(!RE2::FullMatch("-1", "(\\d+)", &v));
-
- CHECK(RE2::FullMatch(zeros + "4294967295", "(\\d+)", &v)); CHECK_EQ(v, max);
+ uint32_t v;
+ static const uint32_t max = UINT32_C(0xffffffff);
+ ASSERT_TRUE(RE2::FullMatch("100", "(\\d+)", &v)); ASSERT_EQ(v, 100);
+ ASSERT_TRUE(RE2::FullMatch("4294967295", "(\\d+)", &v)); ASSERT_EQ(v, max);
+ ASSERT_FALSE(RE2::FullMatch("4294967296", "(\\d+)", &v));
+ ASSERT_FALSE(RE2::FullMatch("-1", "(\\d+)", &v));
+
+ ASSERT_TRUE(RE2::FullMatch(zeros + "4294967295", "(\\d+)", &v)); ASSERT_EQ(v, max);
}
{
- int64 v;
- static const int64 max = 0x7fffffffffffffffull;
- static const int64 min = -max - 1;
- char buf[32];
+ int64_t v;
+ static const int64_t max = INT64_C(0x7fffffffffffffff);
+ static const int64_t min = -max - 1;
+ string str;
- CHECK(RE2::FullMatch("100", "(-?\\d+)", &v)); CHECK_EQ(v, 100);
- CHECK(RE2::FullMatch("-100", "(-?\\d+)", &v)); CHECK_EQ(v, -100);
+ ASSERT_TRUE(RE2::FullMatch("100", "(-?\\d+)", &v)); ASSERT_EQ(v, 100);
+ ASSERT_TRUE(RE2::FullMatch("-100", "(-?\\d+)", &v)); ASSERT_EQ(v, -100);
- snprintf(buf, sizeof(buf), "%lld", (long long int)max);
- CHECK(RE2::FullMatch(buf, "(-?\\d+)", &v)); CHECK_EQ(v, max);
+ str = std::to_string(max);
+ ASSERT_TRUE(RE2::FullMatch(str, "(-?\\d+)", &v)); ASSERT_EQ(v, max);
- snprintf(buf, sizeof(buf), "%lld", (long long int)min);
- CHECK(RE2::FullMatch(buf, "(-?\\d+)", &v)); CHECK_EQ(v, min);
+ str = std::to_string(min);
+ ASSERT_TRUE(RE2::FullMatch(str, "(-?\\d+)", &v)); ASSERT_EQ(v, min);
- snprintf(buf, sizeof(buf), "%lld", (long long int)max);
- assert(buf[strlen(buf)-1] != '9');
- buf[strlen(buf)-1]++;
- CHECK(!RE2::FullMatch(buf, "(-?\\d+)", &v));
+ str = std::to_string(max);
+ ASSERT_NE(str.back(), '9');
+ str.back()++;
+ ASSERT_FALSE(RE2::FullMatch(str, "(-?\\d+)", &v));
- snprintf(buf, sizeof(buf), "%lld", (long long int)min);
- assert(buf[strlen(buf)-1] != '9');
- buf[strlen(buf)-1]++;
- CHECK(!RE2::FullMatch(buf, "(-?\\d+)", &v));
+ str = std::to_string(min);
+ ASSERT_NE(str.back(), '9');
+ str.back()++;
+ ASSERT_FALSE(RE2::FullMatch(str, "(-?\\d+)", &v));
}
{
- uint64 v;
- int64 v2;
- static const uint64 max = 0xffffffffffffffffull;
- char buf[32];
+ uint64_t v;
+ int64_t v2;
+ static const uint64_t max = UINT64_C(0xffffffffffffffff);
+ string str;
- CHECK(RE2::FullMatch("100", "(-?\\d+)", &v)); CHECK_EQ(v, 100);
- CHECK(RE2::FullMatch("-100", "(-?\\d+)", &v2)); CHECK_EQ(v2, -100);
+ ASSERT_TRUE(RE2::FullMatch("100", "(-?\\d+)", &v)); ASSERT_EQ(v, 100);
+ ASSERT_TRUE(RE2::FullMatch("-100", "(-?\\d+)", &v2)); ASSERT_EQ(v2, -100);
- snprintf(buf, sizeof(buf), "%llu", (long long unsigned)max);
- CHECK(RE2::FullMatch(buf, "(-?\\d+)", &v)); CHECK_EQ(v, max);
+ str = std::to_string(max);
+ ASSERT_TRUE(RE2::FullMatch(str, "(-?\\d+)", &v)); ASSERT_EQ(v, max);
- assert(buf[strlen(buf)-1] != '9');
- buf[strlen(buf)-1]++;
- CHECK(!RE2::FullMatch(buf, "(-?\\d+)", &v));
+ ASSERT_NE(str.back(), '9');
+ str.back()++;
+ ASSERT_FALSE(RE2::FullMatch(str, "(-?\\d+)", &v));
}
}
TEST(RE2, FloatingPointFullMatchTypes) {
- string zeros(100, '0');
+ string zeros(1000, '0');
{
float v;
- CHECK(RE2::FullMatch("100", "(.*)", &v)); CHECK_EQ(v, 100);
- CHECK(RE2::FullMatch("-100.", "(.*)", &v)); CHECK_EQ(v, -100);
- CHECK(RE2::FullMatch("1e23", "(.*)", &v)); CHECK_EQ(v, float(1e23));
+ ASSERT_TRUE(RE2::FullMatch("100", "(.*)", &v)); ASSERT_EQ(v, 100);
+ ASSERT_TRUE(RE2::FullMatch("-100.", "(.*)", &v)); ASSERT_EQ(v, -100);
+ ASSERT_TRUE(RE2::FullMatch("1e23", "(.*)", &v)); ASSERT_EQ(v, float(1e23));
+ ASSERT_TRUE(RE2::FullMatch(" 100", "(.*)", &v)); ASSERT_EQ(v, 100);
- CHECK(RE2::FullMatch(zeros + "1e23", "(.*)", &v));
- CHECK_EQ(v, float(1e23));
+ ASSERT_TRUE(RE2::FullMatch(zeros + "1e23", "(.*)", &v));
+ ASSERT_EQ(v, float(1e23));
// 6700000000081920.1 is an edge case.
// 6700000000081920 is exactly halfway between
@@ -805,24 +900,29 @@ TEST(RE2, FloatingPointFullMatchTypes) {
// number, since C does not guarantee to get the correctly
// rounded answer for strtod and strtof unless the input is
// short.
- CHECK(RE2::FullMatch("0.1", "(.*)", &v));
- CHECK_EQ(v, 0.1f) << StringPrintf("%.8g != %.8g", v, 0.1f);
- CHECK(RE2::FullMatch("6700000000081920.1", "(.*)", &v));
- CHECK_EQ(v, 6700000000081920.1f)
+ //
+ // This is known to fail on Cygwin and MinGW due to a broken
+ // implementation of strtof(3). And apparently MSVC too. Sigh.
+#if !defined(_MSC_VER) && !defined(__CYGWIN__) && !defined(__MINGW32__)
+ ASSERT_TRUE(RE2::FullMatch("0.1", "(.*)", &v));
+ ASSERT_EQ(v, 0.1f) << StringPrintf("%.8g != %.8g", v, 0.1f);
+ ASSERT_TRUE(RE2::FullMatch("6700000000081920.1", "(.*)", &v));
+ ASSERT_EQ(v, 6700000000081920.1f)
<< StringPrintf("%.8g != %.8g", v, 6700000000081920.1f);
+#endif
}
{
double v;
- CHECK(RE2::FullMatch("100", "(.*)", &v)); CHECK_EQ(v, 100);
- CHECK(RE2::FullMatch("-100.", "(.*)", &v)); CHECK_EQ(v, -100);
- CHECK(RE2::FullMatch("1e23", "(.*)", &v)); CHECK_EQ(v, 1e23);
- CHECK(RE2::FullMatch(zeros + "1e23", "(.*)", &v));
- CHECK_EQ(v, double(1e23));
-
- CHECK(RE2::FullMatch("0.1", "(.*)", &v));
- CHECK_EQ(v, 0.1) << StringPrintf("%.17g != %.17g", v, 0.1);
- CHECK(RE2::FullMatch("1.00000005960464485", "(.*)", &v));
- CHECK_EQ(v, 1.0000000596046448)
+ ASSERT_TRUE(RE2::FullMatch("100", "(.*)", &v)); ASSERT_EQ(v, 100);
+ ASSERT_TRUE(RE2::FullMatch("-100.", "(.*)", &v)); ASSERT_EQ(v, -100);
+ ASSERT_TRUE(RE2::FullMatch("1e23", "(.*)", &v)); ASSERT_EQ(v, 1e23);
+ ASSERT_TRUE(RE2::FullMatch(zeros + "1e23", "(.*)", &v));
+ ASSERT_EQ(v, double(1e23));
+
+ ASSERT_TRUE(RE2::FullMatch("0.1", "(.*)", &v));
+ ASSERT_EQ(v, 0.1) << StringPrintf("%.17g != %.17g", v, 0.1);
+ ASSERT_TRUE(RE2::FullMatch("1.00000005960464485", "(.*)", &v));
+ ASSERT_EQ(v, 1.0000000596046448)
<< StringPrintf("%.17g != %.17g", v, 1.0000000596046448);
}
}
@@ -830,141 +930,127 @@ TEST(RE2, FloatingPointFullMatchTypes) {
TEST(RE2, FullMatchAnchored) {
int i;
// Check that matching is fully anchored
- CHECK(!RE2::FullMatch("x1001", "(\\d+)", &i));
- CHECK(!RE2::FullMatch("1001x", "(\\d+)", &i));
- CHECK(RE2::FullMatch("x1001", "x(\\d+)", &i)); CHECK_EQ(i, 1001);
- CHECK(RE2::FullMatch("1001x", "(\\d+)x", &i)); CHECK_EQ(i, 1001);
+ ASSERT_FALSE(RE2::FullMatch("x1001", "(\\d+)", &i));
+ ASSERT_FALSE(RE2::FullMatch("1001x", "(\\d+)", &i));
+ ASSERT_TRUE(RE2::FullMatch("x1001", "x(\\d+)", &i)); ASSERT_EQ(i, 1001);
+ ASSERT_TRUE(RE2::FullMatch("1001x", "(\\d+)x", &i)); ASSERT_EQ(i, 1001);
}
TEST(RE2, FullMatchBraces) {
// Braces
- CHECK(RE2::FullMatch("0abcd", "[0-9a-f+.-]{5,}"));
- CHECK(RE2::FullMatch("0abcde", "[0-9a-f+.-]{5,}"));
- CHECK(!RE2::FullMatch("0abc", "[0-9a-f+.-]{5,}"));
+ ASSERT_TRUE(RE2::FullMatch("0abcd", "[0-9a-f+.-]{5,}"));
+ ASSERT_TRUE(RE2::FullMatch("0abcde", "[0-9a-f+.-]{5,}"));
+ ASSERT_FALSE(RE2::FullMatch("0abc", "[0-9a-f+.-]{5,}"));
}
TEST(RE2, Complicated) {
// Complicated RE2
- CHECK(RE2::FullMatch("foo", "foo|bar|[A-Z]"));
- CHECK(RE2::FullMatch("bar", "foo|bar|[A-Z]"));
- CHECK(RE2::FullMatch("X", "foo|bar|[A-Z]"));
- CHECK(!RE2::FullMatch("XY", "foo|bar|[A-Z]"));
+ ASSERT_TRUE(RE2::FullMatch("foo", "foo|bar|[A-Z]"));
+ ASSERT_TRUE(RE2::FullMatch("bar", "foo|bar|[A-Z]"));
+ ASSERT_TRUE(RE2::FullMatch("X", "foo|bar|[A-Z]"));
+ ASSERT_FALSE(RE2::FullMatch("XY", "foo|bar|[A-Z]"));
}
TEST(RE2, FullMatchEnd) {
// Check full-match handling (needs '$' tacked on internally)
- CHECK(RE2::FullMatch("fo", "fo|foo"));
- CHECK(RE2::FullMatch("foo", "fo|foo"));
- CHECK(RE2::FullMatch("fo", "fo|foo$"));
- CHECK(RE2::FullMatch("foo", "fo|foo$"));
- CHECK(RE2::FullMatch("foo", "foo$"));
- CHECK(!RE2::FullMatch("foo$bar", "foo\\$"));
- CHECK(!RE2::FullMatch("fox", "fo|bar"));
+ ASSERT_TRUE(RE2::FullMatch("fo", "fo|foo"));
+ ASSERT_TRUE(RE2::FullMatch("foo", "fo|foo"));
+ ASSERT_TRUE(RE2::FullMatch("fo", "fo|foo$"));
+ ASSERT_TRUE(RE2::FullMatch("foo", "fo|foo$"));
+ ASSERT_TRUE(RE2::FullMatch("foo", "foo$"));
+ ASSERT_FALSE(RE2::FullMatch("foo$bar", "foo\\$"));
+ ASSERT_FALSE(RE2::FullMatch("fox", "fo|bar"));
// Uncomment the following if we change the handling of '$' to
// prevent it from matching a trailing newline
if (false) {
// Check that we don't get bitten by pcre's special handling of a
// '\n' at the end of the string matching '$'
- CHECK(!RE2::PartialMatch("foo\n", "foo$"));
+ ASSERT_FALSE(RE2::PartialMatch("foo\n", "foo$"));
}
}
TEST(RE2, FullMatchArgCount) {
// Number of args
int a[16];
- CHECK(RE2::FullMatch("", ""));
+ ASSERT_TRUE(RE2::FullMatch("", ""));
memset(a, 0, sizeof(0));
- CHECK(RE2::FullMatch("1",
- "(\\d){1}",
- &a[0]));
- CHECK_EQ(a[0], 1);
+ ASSERT_TRUE(RE2::FullMatch("1", "(\\d){1}", &a[0]));
+ ASSERT_EQ(a[0], 1);
memset(a, 0, sizeof(0));
- CHECK(RE2::FullMatch("12",
- "(\\d)(\\d)",
- &a[0], &a[1]));
- CHECK_EQ(a[0], 1);
- CHECK_EQ(a[1], 2);
+ ASSERT_TRUE(RE2::FullMatch("12", "(\\d)(\\d)", &a[0], &a[1]));
+ ASSERT_EQ(a[0], 1);
+ ASSERT_EQ(a[1], 2);
memset(a, 0, sizeof(0));
- CHECK(RE2::FullMatch("123",
- "(\\d)(\\d)(\\d)",
- &a[0], &a[1], &a[2]));
- CHECK_EQ(a[0], 1);
- CHECK_EQ(a[1], 2);
- CHECK_EQ(a[2], 3);
+ ASSERT_TRUE(RE2::FullMatch("123", "(\\d)(\\d)(\\d)", &a[0], &a[1], &a[2]));
+ ASSERT_EQ(a[0], 1);
+ ASSERT_EQ(a[1], 2);
+ ASSERT_EQ(a[2], 3);
memset(a, 0, sizeof(0));
- CHECK(RE2::FullMatch("1234",
- "(\\d)(\\d)(\\d)(\\d)",
- &a[0], &a[1], &a[2], &a[3]));
- CHECK_EQ(a[0], 1);
- CHECK_EQ(a[1], 2);
- CHECK_EQ(a[2], 3);
- CHECK_EQ(a[3], 4);
+ ASSERT_TRUE(RE2::FullMatch("1234", "(\\d)(\\d)(\\d)(\\d)", &a[0], &a[1],
+ &a[2], &a[3]));
+ ASSERT_EQ(a[0], 1);
+ ASSERT_EQ(a[1], 2);
+ ASSERT_EQ(a[2], 3);
+ ASSERT_EQ(a[3], 4);
memset(a, 0, sizeof(0));
- CHECK(RE2::FullMatch("12345",
- "(\\d)(\\d)(\\d)(\\d)(\\d)",
- &a[0], &a[1], &a[2], &a[3],
- &a[4]));
- CHECK_EQ(a[0], 1);
- CHECK_EQ(a[1], 2);
- CHECK_EQ(a[2], 3);
- CHECK_EQ(a[3], 4);
- CHECK_EQ(a[4], 5);
+ ASSERT_TRUE(RE2::FullMatch("12345", "(\\d)(\\d)(\\d)(\\d)(\\d)", &a[0], &a[1],
+ &a[2], &a[3], &a[4]));
+ ASSERT_EQ(a[0], 1);
+ ASSERT_EQ(a[1], 2);
+ ASSERT_EQ(a[2], 3);
+ ASSERT_EQ(a[3], 4);
+ ASSERT_EQ(a[4], 5);
memset(a, 0, sizeof(0));
- CHECK(RE2::FullMatch("123456",
- "(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)",
- &a[0], &a[1], &a[2], &a[3],
- &a[4], &a[5]));
- CHECK_EQ(a[0], 1);
- CHECK_EQ(a[1], 2);
- CHECK_EQ(a[2], 3);
- CHECK_EQ(a[3], 4);
- CHECK_EQ(a[4], 5);
- CHECK_EQ(a[5], 6);
+ ASSERT_TRUE(RE2::FullMatch("123456", "(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)", &a[0],
+ &a[1], &a[2], &a[3], &a[4], &a[5]));
+ ASSERT_EQ(a[0], 1);
+ ASSERT_EQ(a[1], 2);
+ ASSERT_EQ(a[2], 3);
+ ASSERT_EQ(a[3], 4);
+ ASSERT_EQ(a[4], 5);
+ ASSERT_EQ(a[5], 6);
memset(a, 0, sizeof(0));
- CHECK(RE2::FullMatch("1234567",
- "(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)",
- &a[0], &a[1], &a[2], &a[3],
- &a[4], &a[5], &a[6]));
- CHECK_EQ(a[0], 1);
- CHECK_EQ(a[1], 2);
- CHECK_EQ(a[2], 3);
- CHECK_EQ(a[3], 4);
- CHECK_EQ(a[4], 5);
- CHECK_EQ(a[5], 6);
- CHECK_EQ(a[6], 7);
+ ASSERT_TRUE(RE2::FullMatch("1234567", "(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)",
+ &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6]));
+ ASSERT_EQ(a[0], 1);
+ ASSERT_EQ(a[1], 2);
+ ASSERT_EQ(a[2], 3);
+ ASSERT_EQ(a[3], 4);
+ ASSERT_EQ(a[4], 5);
+ ASSERT_EQ(a[5], 6);
+ ASSERT_EQ(a[6], 7);
memset(a, 0, sizeof(0));
- CHECK(RE2::FullMatch("1234567890123456",
- "(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)"
- "(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)",
- &a[0], &a[1], &a[2], &a[3],
- &a[4], &a[5], &a[6], &a[7],
- &a[8], &a[9], &a[10], &a[11],
- &a[12], &a[13], &a[14], &a[15]));
- CHECK_EQ(a[0], 1);
- CHECK_EQ(a[1], 2);
- CHECK_EQ(a[2], 3);
- CHECK_EQ(a[3], 4);
- CHECK_EQ(a[4], 5);
- CHECK_EQ(a[5], 6);
- CHECK_EQ(a[6], 7);
- CHECK_EQ(a[7], 8);
- CHECK_EQ(a[8], 9);
- CHECK_EQ(a[9], 0);
- CHECK_EQ(a[10], 1);
- CHECK_EQ(a[11], 2);
- CHECK_EQ(a[12], 3);
- CHECK_EQ(a[13], 4);
- CHECK_EQ(a[14], 5);
- CHECK_EQ(a[15], 6);
+ ASSERT_TRUE(RE2::FullMatch("1234567890123456",
+ "(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)"
+ "(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)",
+ &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6],
+ &a[7], &a[8], &a[9], &a[10], &a[11], &a[12],
+ &a[13], &a[14], &a[15]));
+ ASSERT_EQ(a[0], 1);
+ ASSERT_EQ(a[1], 2);
+ ASSERT_EQ(a[2], 3);
+ ASSERT_EQ(a[3], 4);
+ ASSERT_EQ(a[4], 5);
+ ASSERT_EQ(a[5], 6);
+ ASSERT_EQ(a[6], 7);
+ ASSERT_EQ(a[7], 8);
+ ASSERT_EQ(a[8], 9);
+ ASSERT_EQ(a[9], 0);
+ ASSERT_EQ(a[10], 1);
+ ASSERT_EQ(a[11], 2);
+ ASSERT_EQ(a[12], 3);
+ ASSERT_EQ(a[13], 4);
+ ASSERT_EQ(a[14], 5);
+ ASSERT_EQ(a[15], 6);
}
TEST(RE2, Accessors) {
@@ -972,15 +1058,15 @@ TEST(RE2, Accessors) {
{
const string kPattern = "http://([^/]+)/.*";
const RE2 re(kPattern);
- CHECK_EQ(kPattern, re.pattern());
+ ASSERT_EQ(kPattern, re.pattern());
}
// Check RE2 error field.
{
RE2 re("foo");
- CHECK(re.error().empty()); // Must have no error
- CHECK(re.ok());
- CHECK(re.error_code() == RE2::NoError);
+ ASSERT_TRUE(re.error().empty()); // Must have no error
+ ASSERT_TRUE(re.ok());
+ ASSERT_EQ(re.error_code(), RE2::NoError);
}
}
@@ -988,45 +1074,45 @@ TEST(RE2, UTF8) {
// Check UTF-8 handling
// Three Japanese characters (nihongo)
const char utf8_string[] = {
- 0xe6, 0x97, 0xa5, // 65e5
- 0xe6, 0x9c, 0xac, // 627c
- 0xe8, 0xaa, 0x9e, // 8a9e
+ (char)0xe6, (char)0x97, (char)0xa5, // 65e5
+ (char)0xe6, (char)0x9c, (char)0xac, // 627c
+ (char)0xe8, (char)0xaa, (char)0x9e, // 8a9e
0
};
const char utf8_pattern[] = {
'.',
- 0xe6, 0x9c, 0xac, // 627c
+ (char)0xe6, (char)0x9c, (char)0xac, // 627c
'.',
0
};
// Both should match in either mode, bytes or UTF-8
RE2 re_test1(".........", RE2::Latin1);
- CHECK(RE2::FullMatch(utf8_string, re_test1));
+ ASSERT_TRUE(RE2::FullMatch(utf8_string, re_test1));
RE2 re_test2("...");
- CHECK(RE2::FullMatch(utf8_string, re_test2));
+ ASSERT_TRUE(RE2::FullMatch(utf8_string, re_test2));
// Check that '.' matches one byte or UTF-8 character
// according to the mode.
string s;
RE2 re_test3("(.)", RE2::Latin1);
- CHECK(RE2::PartialMatch(utf8_string, re_test3, &s));
- CHECK_EQ(s, string("\xe6"));
+ ASSERT_TRUE(RE2::PartialMatch(utf8_string, re_test3, &s));
+ ASSERT_EQ(s, string("\xe6"));
RE2 re_test4("(.)");
- CHECK(RE2::PartialMatch(utf8_string, re_test4, &s));
- CHECK_EQ(s, string("\xe6\x97\xa5"));
+ ASSERT_TRUE(RE2::PartialMatch(utf8_string, re_test4, &s));
+ ASSERT_EQ(s, string("\xe6\x97\xa5"));
// Check that string matches itself in either mode
RE2 re_test5(utf8_string, RE2::Latin1);
- CHECK(RE2::FullMatch(utf8_string, re_test5));
+ ASSERT_TRUE(RE2::FullMatch(utf8_string, re_test5));
RE2 re_test6(utf8_string);
- CHECK(RE2::FullMatch(utf8_string, re_test6));
+ ASSERT_TRUE(RE2::FullMatch(utf8_string, re_test6));
// Check that pattern matches string only in UTF8 mode
RE2 re_test7(utf8_pattern, RE2::Latin1);
- CHECK(!RE2::FullMatch(utf8_string, re_test7));
+ ASSERT_FALSE(RE2::FullMatch(utf8_string, re_test7));
RE2 re_test8(utf8_pattern);
- CHECK(RE2::FullMatch(utf8_string, re_test8));
+ ASSERT_TRUE(RE2::FullMatch(utf8_string, re_test8));
}
TEST(RE2, UngreedyUTF8) {
@@ -1039,42 +1125,44 @@ TEST(RE2, UngreedyUTF8) {
RE2 match_sentence(pattern, RE2::Latin1);
RE2 match_sentence_re(pattern);
- CHECK(!RE2::FullMatch(target, match_sentence));
- CHECK(!RE2::FullMatch(target, match_sentence_re));
+ ASSERT_FALSE(RE2::FullMatch(target, match_sentence));
+ ASSERT_FALSE(RE2::FullMatch(target, match_sentence_re));
}
{
const char* pattern = "(?U)\\w+X";
const string target = "a aX";
RE2 match_sentence(pattern, RE2::Latin1);
- CHECK_EQ(match_sentence.error(), "");
+ ASSERT_EQ(match_sentence.error(), "");
RE2 match_sentence_re(pattern);
- CHECK(!RE2::FullMatch(target, match_sentence));
- CHECK(!RE2::FullMatch(target, match_sentence_re));
+ ASSERT_FALSE(RE2::FullMatch(target, match_sentence));
+ ASSERT_FALSE(RE2::FullMatch(target, match_sentence_re));
}
}
TEST(RE2, Rejects) {
- { RE2 re("a\\1", RE2::Quiet); CHECK(!re.ok()); }
+ {
+ RE2 re("a\\1", RE2::Quiet);
+ ASSERT_FALSE(re.ok()); }
{
RE2 re("a[x", RE2::Quiet);
- CHECK(!re.ok());
+ ASSERT_FALSE(re.ok());
}
{
RE2 re("a[z-a]", RE2::Quiet);
- CHECK(!re.ok());
+ ASSERT_FALSE(re.ok());
}
{
RE2 re("a[[:foobar:]]", RE2::Quiet);
- CHECK(!re.ok());
+ ASSERT_FALSE(re.ok());
}
{
RE2 re("a(b", RE2::Quiet);
- CHECK(!re.ok());
+ ASSERT_FALSE(re.ok());
}
{
RE2 re("a\\", RE2::Quiet);
- CHECK(!re.ok());
+ ASSERT_FALSE(re.ok());
}
}
@@ -1082,25 +1170,25 @@ TEST(RE2, NoCrash) {
// Test that using a bad regexp doesn't crash.
{
RE2 re("a\\", RE2::Quiet);
- CHECK(!re.ok());
- CHECK(!RE2::PartialMatch("a\\b", re));
+ ASSERT_FALSE(re.ok());
+ ASSERT_FALSE(RE2::PartialMatch("a\\b", re));
}
// Test that using an enormous regexp doesn't crash
{
RE2 re("(((.{100}){100}){100}){100}", RE2::Quiet);
- CHECK(!re.ok());
- CHECK(!RE2::PartialMatch("aaa", re));
+ ASSERT_FALSE(re.ok());
+ ASSERT_FALSE(RE2::PartialMatch("aaa", re));
}
// Test that a crazy regexp still compiles and runs.
{
RE2 re(".{512}x", RE2::Quiet);
- CHECK(re.ok());
+ ASSERT_TRUE(re.ok());
string s;
s.append(515, 'c');
s.append("x");
- CHECK(RE2::PartialMatch(s, re));
+ ASSERT_TRUE(RE2::PartialMatch(s, re));
}
}
@@ -1121,11 +1209,11 @@ TEST(RE2, BigCountedRepetition) {
opt.set_max_mem(256<<20);
RE2 re(".{512}x", opt);
- CHECK(re.ok());
+ ASSERT_TRUE(re.ok());
string s;
s.append(515, 'c');
s.append("x");
- CHECK(RE2::PartialMatch(s, re));
+ ASSERT_TRUE(RE2::PartialMatch(s, re));
}
TEST(RE2, DeepRecursion) {
@@ -1138,7 +1226,7 @@ TEST(RE2, DeepRecursion) {
comment += a;
comment += "*x";
RE2 re("((?:\\s|xx.*\n|x[*](?:\n|.)*?[*]x)*)");
- CHECK(RE2::FullMatch(comment, re));
+ ASSERT_TRUE(RE2::FullMatch(comment, re));
}
// Suggested by Josh Hyman. Failed when SearchOnePass was
@@ -1244,6 +1332,16 @@ TEST(RE2, NeverNewline) {
}
}
+// Check that dot_nl option works.
+TEST(RE2, DotNL) {
+ RE2::Options opt;
+ opt.set_dot_nl(true);
+ EXPECT_TRUE(RE2::PartialMatch("\n", RE2(".", opt)));
+ EXPECT_FALSE(RE2::PartialMatch("\n", RE2("(?-s).", opt)));
+ opt.set_never_nl(true);
+ EXPECT_FALSE(RE2::PartialMatch("\n", RE2(".", opt)));
+}
+
// Check that there are no capturing groups in "never capture" mode.
TEST(RE2, NeverCapture) {
RE2::Options opt;
@@ -1322,14 +1420,70 @@ TEST(RE2, UnicodeClasses) {
EXPECT_EQ("鋒", c);
}
+TEST(RE2, LazyRE2) {
+ // Test with and without options.
+ static LazyRE2 a = {"a"};
+ static LazyRE2 b = {"b", RE2::Latin1};
+
+ EXPECT_EQ("a", a->pattern());
+ EXPECT_EQ(RE2::Options::EncodingUTF8, a->options().encoding());
+
+ EXPECT_EQ("b", b->pattern());
+ EXPECT_EQ(RE2::Options::EncodingLatin1, b->options().encoding());
+}
+
// Bug reported by saito. 2009/02/17
TEST(RE2, NullVsEmptyString) {
- RE2 re2(".*");
- StringPiece v1("");
- EXPECT_TRUE(RE2::FullMatch(v1, re2));
+ RE2 re(".*");
+ EXPECT_TRUE(re.ok());
- StringPiece v2;
- EXPECT_TRUE(RE2::FullMatch(v2, re2));
+ StringPiece null;
+ EXPECT_TRUE(RE2::FullMatch(null, re));
+
+ StringPiece empty("");
+ EXPECT_TRUE(RE2::FullMatch(empty, re));
+}
+
+// Similar to the previous test, check that the null string and the empty
+// string both match, but also that the null string can only provide null
+// submatches whereas the empty string can also provide empty submatches.
+TEST(RE2, NullVsEmptyStringSubmatches) {
+ RE2 re("()|(foo)");
+ EXPECT_TRUE(re.ok());
+
+ // matches[0] is overall match, [1] is (), [2] is (foo), [3] is nonexistent.
+ StringPiece matches[4];
+
+ for (int i = 0; i < arraysize(matches); i++)
+ matches[i] = "bar";
+
+ StringPiece null;
+ EXPECT_TRUE(re.Match(null, 0, null.size(), RE2::UNANCHORED,
+ matches, arraysize(matches)));
+ for (int i = 0; i < arraysize(matches); i++) {
+ EXPECT_TRUE(matches[i] == StringPiece());
+ EXPECT_TRUE(matches[i].data() == NULL); // always null
+ EXPECT_TRUE(matches[i] == "");
+ }
+
+ for (int i = 0; i < arraysize(matches); i++)
+ matches[i] = "bar";
+
+ StringPiece empty("");
+ EXPECT_TRUE(re.Match(empty, 0, empty.size(), RE2::UNANCHORED,
+ matches, arraysize(matches)));
+ EXPECT_TRUE(matches[0] == StringPiece());
+ EXPECT_TRUE(matches[0].data() != NULL); // empty, not null
+ EXPECT_TRUE(matches[0] == "");
+ EXPECT_TRUE(matches[1] == StringPiece());
+ EXPECT_TRUE(matches[1].data() != NULL); // empty, not null
+ EXPECT_TRUE(matches[1] == "");
+ EXPECT_TRUE(matches[2] == StringPiece());
+ EXPECT_TRUE(matches[2].data() == NULL);
+ EXPECT_TRUE(matches[2] == "");
+ EXPECT_TRUE(matches[3] == StringPiece());
+ EXPECT_TRUE(matches[3].data() == NULL);
+ EXPECT_TRUE(matches[3] == "");
}
// Issue 1816809
@@ -1353,8 +1507,8 @@ TEST(RE2, CapturingGroupNames) {
// 12 3 45 6 7
RE2 re("((abc)(?P<G2>)|((e+)(?P<G2>.*)(?P<G1>u+)))");
EXPECT_TRUE(re.ok());
- const map<int, string>& have = re.CapturingGroupNames();
- map<int, string> want;
+ const std::map<int, string>& have = re.CapturingGroupNames();
+ std::map<int, string> want;
want[3] = "G2";
want[6] = "G2";
want[7] = "G1";
@@ -1368,4 +1522,115 @@ TEST(RE2, RegexpToStringLossOfAnchor) {
EXPECT_EQ(RE2("ca[t-z]$").Regexp()->ToString(), "ca[t-z](?-m:$)");
}
+// Issue 10131674
+TEST(RE2, Bug10131674) {
+ // Some of these escapes describe values that do not fit in a byte.
+ RE2 re("\\140\\440\\174\\271\\150\\656\\106\\201\\004\\332", RE2::Latin1);
+ EXPECT_FALSE(re.ok());
+ EXPECT_FALSE(RE2::FullMatch("hello world", re));
+}
+
+TEST(RE2, Bug18391750) {
+ // Stray write past end of match_ in nfa.cc, caught by fuzzing + address sanitizer.
+ const char t[] = {
+ (char)0x28, (char)0x28, (char)0xfc, (char)0xfc, (char)0x08, (char)0x08,
+ (char)0x26, (char)0x26, (char)0x28, (char)0xc2, (char)0x9b, (char)0xc5,
+ (char)0xc5, (char)0xd4, (char)0x8f, (char)0x8f, (char)0x69, (char)0x69,
+ (char)0xe7, (char)0x29, (char)0x7b, (char)0x37, (char)0x31, (char)0x31,
+ (char)0x7d, (char)0xae, (char)0x7c, (char)0x7c, (char)0xf3, (char)0x29,
+ (char)0xae, (char)0xae, (char)0x2e, (char)0x2a, (char)0x29, (char)0x00,
+ };
+ RE2::Options opt;
+ opt.set_encoding(RE2::Options::EncodingLatin1);
+ opt.set_longest_match(true);
+ opt.set_dot_nl(true);
+ opt.set_case_sensitive(false);
+ RE2 re(t, opt);
+ ASSERT_TRUE(re.ok());
+ RE2::PartialMatch(t, re);
+}
+
+TEST(RE2, Bug18458852) {
+ // Bug in parser accepting invalid (too large) rune,
+ // causing compiler to fail in DCHECK in UTF-8
+ // character class code.
+ const char b[] = {
+ (char)0x28, (char)0x05, (char)0x05, (char)0x41, (char)0x41, (char)0x28,
+ (char)0x24, (char)0x5b, (char)0x5e, (char)0xf5, (char)0x87, (char)0x87,
+ (char)0x90, (char)0x29, (char)0x5d, (char)0x29, (char)0x29, (char)0x00,
+ };
+ RE2 re(b);
+ ASSERT_FALSE(re.ok());
+}
+
+TEST(RE2, Bug18523943) {
+ // Bug in BitState: case kFailInst failed the match entirely.
+
+ RE2::Options opt;
+ const char a[] = {
+ (char)0x29, (char)0x29, (char)0x24, (char)0x00,
+ };
+ const char b[] = {
+ (char)0x28, (char)0x0a, (char)0x2a, (char)0x2a, (char)0x29, (char)0x00,
+ };
+ opt.set_log_errors(false);
+ opt.set_encoding(RE2::Options::EncodingLatin1);
+ opt.set_posix_syntax(true);
+ opt.set_longest_match(true);
+ opt.set_literal(false);
+ opt.set_never_nl(true);
+
+ RE2 re((const char*)b, opt);
+ ASSERT_TRUE(re.ok());
+ string s1;
+ ASSERT_TRUE(RE2::PartialMatch((const char*)a, re, &s1));
+}
+
+TEST(RE2, Bug21371806) {
+ // Bug in parser accepting Unicode groups in Latin-1 mode,
+ // causing compiler to fail in DCHECK in prog.cc.
+
+ RE2::Options opt;
+ opt.set_encoding(RE2::Options::EncodingLatin1);
+
+ RE2 re("g\\p{Zl}]", opt);
+ ASSERT_TRUE(re.ok());
+}
+
+TEST(RE2, Bug26356109) {
+ // Bug in parser caused by factoring of common prefixes in alternations.
+
+ // In the past, this was factored to "a\\C*?[bc]". Thus, the automaton would
+ // consume "ab" and then stop (when unanchored) whereas it should consume all
+ // of "abc" as per first-match semantics.
+ RE2 re("a\\C*?c|a\\C*?b");
+ ASSERT_TRUE(re.ok());
+
+ string s = "abc";
+ StringPiece m;
+
+ ASSERT_TRUE(re.Match(s, 0, s.size(), RE2::UNANCHORED, &m, 1));
+ ASSERT_EQ(m, s) << " (UNANCHORED) got m='" << m << "', want '" << s << "'";
+
+ ASSERT_TRUE(re.Match(s, 0, s.size(), RE2::ANCHOR_BOTH, &m, 1));
+ ASSERT_EQ(m, s) << " (ANCHOR_BOTH) got m='" << m << "', want '" << s << "'";
+}
+
+TEST(RE2, Issue104) {
+ // RE2::GlobalReplace always advanced by one byte when the empty string was
+ // matched, which would clobber any rune that is longer than one byte.
+
+ string s = "bc";
+ ASSERT_EQ(3, RE2::GlobalReplace(&s, "a*", "d"));
+ ASSERT_EQ("dbdcd", s);
+
+ s = "ąć";
+ ASSERT_EQ(3, RE2::GlobalReplace(&s, "Ć*", "Ĉ"));
+ ASSERT_EQ("ĈąĈćĈ", s);
+
+ s = "人类";
+ ASSERT_EQ(3, RE2::GlobalReplace(&s, "大*", "小"));
+ ASSERT_EQ("小人小类小", s);
+}
+
} // namespace re2
diff --git a/re2/testing/regexp_benchmark.cc b/re2/testing/regexp_benchmark.cc
index ca7627f..8b82e0b 100644
--- a/re2/testing/regexp_benchmark.cc
+++ b/re2/testing/regexp_benchmark.cc
@@ -4,7 +4,15 @@
// Benchmarks for regular expression implementations.
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <utility>
+
#include "util/test.h"
+#include "util/logging.h"
+#include "util/strutil.h"
#include "re2/prog.h"
#include "re2/re2.h"
#include "re2/regexp.h"
@@ -91,11 +99,8 @@ void MemoryUsage() {
fprintf(stderr, "RE2: %7lld bytes (peak=%lld)\n", mc.HeapGrowth(), mc.PeakHeapGrowth());
}
- fprintf(stderr, "sizeof: PCRE=%d RE2=%d Prog=%d Inst=%d\n",
- static_cast<int>(sizeof(PCRE)),
- static_cast<int>(sizeof(RE2)),
- static_cast<int>(sizeof(Prog)),
- static_cast<int>(sizeof(Prog::Inst)));
+ fprintf(stderr, "sizeof: PCRE=%zd RE2=%zd Prog=%zd Inst=%zd\n",
+ sizeof(PCRE), sizeof(RE2), sizeof(Prog), sizeof(Prog::Inst));
}
// Regular expression implementation wrappers.
@@ -135,13 +140,15 @@ ParseImpl SearchParse1CachedPCRE, SearchParse1CachedRE2;
// Generate random text that won't contain the search string,
// to test worst-case search behavior.
void MakeText(string* text, int nbytes) {
+ srand(1);
text->resize(nbytes);
- srand(0);
for (int i = 0; i < nbytes; i++) {
- if (!rand()%30)
- (*text)[i] = '\n';
- else
- (*text)[i] = rand()%(0x7E + 1 - 0x20)+0x20;
+ // Generate a one-byte rune that isn't a control character (e.g. '\n').
+ // Clipping to 0x20 introduces some bias, but we don't need uniformity.
+ int byte = rand() & 0x7F;
+ if (byte < 0x20)
+ byte = 0x20;
+ (*text)[i] = byte;
}
}
@@ -154,7 +161,7 @@ void Search(int iters, int nbytes, const char* regexp, SearchImpl* search) {
BenchmarkMemoryUsage();
StartBenchmarkTiming();
search(iters, regexp, s, Prog::kUnanchored, false);
- SetBenchmarkBytesProcessed(static_cast<int64>(iters)*nbytes);
+ SetBenchmarkBytesProcessed(static_cast<int64_t>(iters)*nbytes);
}
// These two are easy because they start with an A,
@@ -172,6 +179,10 @@ void Search(int iters, int nbytes, const char* regexp, SearchImpl* search) {
// figure out there's no match.
#define HARD "[ -~]*ABCDEFGHIJKLMNOPQRSTUVWXYZ$"
+// This has quite a high degree of fanout.
+// NFA execution will be particularly slow.
+#define FANOUT "(?:[\\x{80}-\\x{10FFFF}]?){100}[\\x{80}-\\x{10FFFF}]"
+
// This stresses engines that are trying to track parentheses.
#define PARENS "([ -~])*(A)(B)(C)(D)(E)(F)(G)(H)(I)(J)(K)(L)(M)" \
"(N)(O)(P)(Q)(R)(S)(T)(U)(V)(W)(X)(Y)(Z)$"
@@ -224,6 +235,18 @@ BENCHMARK_RANGE(Search_Hard_CachedPCRE, 8, 4<<10)->ThreadRange(1, NumCPUs());
#endif
BENCHMARK_RANGE(Search_Hard_CachedRE2, 8, 16<<20)->ThreadRange(1, NumCPUs());
+void Search_Fanout_CachedDFA(int i, int n) { Search(i, n, FANOUT, SearchCachedDFA); }
+void Search_Fanout_CachedNFA(int i, int n) { Search(i, n, FANOUT, SearchCachedNFA); }
+void Search_Fanout_CachedPCRE(int i, int n) { Search(i, n, FANOUT, SearchCachedPCRE); }
+void Search_Fanout_CachedRE2(int i, int n) { Search(i, n, FANOUT, SearchCachedRE2); }
+
+BENCHMARK_RANGE(Search_Fanout_CachedDFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_Fanout_CachedNFA, 8, 256<<10)->ThreadRange(1, NumCPUs());
+#ifdef USEPCRE
+BENCHMARK_RANGE(Search_Fanout_CachedPCRE, 8, 4<<10)->ThreadRange(1, NumCPUs());
+#endif
+BENCHMARK_RANGE(Search_Fanout_CachedRE2, 8, 16<<20)->ThreadRange(1, NumCPUs());
+
void Search_Parens_CachedDFA(int i, int n) { Search(i, n, PARENS, SearchCachedDFA); }
void Search_Parens_CachedNFA(int i, int n) { Search(i, n, PARENS, SearchCachedNFA); }
void Search_Parens_CachedPCRE(int i, int n) { Search(i, n, PARENS, SearchCachedPCRE); }
@@ -247,7 +270,7 @@ void SearchBigFixed(int iters, int nbytes, SearchImpl* search) {
BenchmarkMemoryUsage();
StartBenchmarkTiming();
search(iters, regexp.c_str(), s, Prog::kUnanchored, true);
- SetBenchmarkBytesProcessed(static_cast<int64>(iters)*nbytes);
+ SetBenchmarkBytesProcessed(static_cast<int64_t>(iters)*nbytes);
}
void Search_BigFixed_CachedDFA(int i, int n) { SearchBigFixed(i, n, SearchCachedDFA); }
@@ -263,6 +286,7 @@ BENCHMARK_RANGE(Search_BigFixed_CachedPCRE, 8, 32<<10)->ThreadRange(1, NumCPU
BENCHMARK_RANGE(Search_BigFixed_CachedRE2, 8, 1<<20)->ThreadRange(1, NumCPUs());
// Benchmark: FindAndConsume
+
void FindAndConsume(int iters, int nbytes) {
StopBenchmarkTiming();
string s;
@@ -276,7 +300,7 @@ void FindAndConsume(int iters, int nbytes) {
CHECK(RE2::FindAndConsume(&t, re, &u));
CHECK_EQ(u, "Hello World");
}
- SetBenchmarkBytesProcessed(static_cast<int64>(iters)*nbytes);
+ SetBenchmarkBytesProcessed(static_cast<int64_t>(iters)*nbytes);
}
BENCHMARK_RANGE(FindAndConsume, 8, 16<<20)->ThreadRange(1, NumCPUs());
@@ -284,21 +308,25 @@ BENCHMARK_RANGE(FindAndConsume, 8, 16<<20)->ThreadRange(1, NumCPUs());
// Benchmark: successful anchored search.
void SearchSuccess(int iters, int nbytes, const char* regexp, SearchImpl* search) {
+ StopBenchmarkTiming();
string s;
MakeText(&s, nbytes);
BenchmarkMemoryUsage();
+ StartBenchmarkTiming();
search(iters, regexp, s, Prog::kAnchored, true);
- SetBenchmarkBytesProcessed(static_cast<int64>(iters)*nbytes);
+ SetBenchmarkBytesProcessed(static_cast<int64_t>(iters)*nbytes);
}
// Unambiguous search (RE2 can use OnePass).
void Search_Success_DFA(int i, int n) { SearchSuccess(i, n, ".*$", SearchDFA); }
-void Search_Success_OnePass(int i, int n) { SearchSuccess(i, n, ".*$", SearchOnePass); }
+void Search_Success_NFA(int i, int n) { SearchSuccess(i, n, ".*$", SearchNFA); }
void Search_Success_PCRE(int i, int n) { SearchSuccess(i, n, ".*$", SearchPCRE); }
void Search_Success_RE2(int i, int n) { SearchSuccess(i, n, ".*$", SearchRE2); }
+void Search_Success_OnePass(int i, int n) { SearchSuccess(i, n, ".*$", SearchOnePass); }
BENCHMARK_RANGE(Search_Success_DFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_Success_NFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
#ifdef USEPCRE
BENCHMARK_RANGE(Search_Success_PCRE, 8, 16<<20)->ThreadRange(1, NumCPUs());
#endif
@@ -306,11 +334,13 @@ BENCHMARK_RANGE(Search_Success_RE2, 8, 16<<20)->ThreadRange(1, NumCPUs());
BENCHMARK_RANGE(Search_Success_OnePass, 8, 2<<20)->ThreadRange(1, NumCPUs());
void Search_Success_CachedDFA(int i, int n) { SearchSuccess(i, n, ".*$", SearchCachedDFA); }
-void Search_Success_CachedOnePass(int i, int n) { SearchSuccess(i, n, ".*$", SearchCachedOnePass); }
+void Search_Success_CachedNFA(int i, int n) { SearchSuccess(i, n, ".*$", SearchCachedNFA); }
void Search_Success_CachedPCRE(int i, int n) { SearchSuccess(i, n, ".*$", SearchCachedPCRE); }
void Search_Success_CachedRE2(int i, int n) { SearchSuccess(i, n, ".*$", SearchCachedRE2); }
+void Search_Success_CachedOnePass(int i, int n) { SearchSuccess(i, n, ".*$", SearchCachedOnePass); }
BENCHMARK_RANGE(Search_Success_CachedDFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_Success_CachedNFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
#ifdef USEPCRE
BENCHMARK_RANGE(Search_Success_CachedPCRE, 8, 16<<20)->ThreadRange(1, NumCPUs());
#endif
@@ -318,37 +348,87 @@ BENCHMARK_RANGE(Search_Success_CachedRE2, 8, 16<<20)->ThreadRange(1, NumCPUs
BENCHMARK_RANGE(Search_Success_CachedOnePass, 8, 2<<20)->ThreadRange(1, NumCPUs());
// Ambiguous search (RE2 cannot use OnePass).
+// Used to be ".*.$", but that is coalesced to ".+$" these days.
-void Search_Success1_DFA(int i, int n) { SearchSuccess(i, n, ".*.$", SearchDFA); }
-void Search_Success1_PCRE(int i, int n) { SearchSuccess(i, n, ".*.$", SearchPCRE); }
-void Search_Success1_RE2(int i, int n) { SearchSuccess(i, n, ".*.$", SearchRE2); }
-void Search_Success1_BitState(int i, int n) { SearchSuccess(i, n, ".*.$", SearchBitState); }
+void Search_Success1_DFA(int i, int n) { SearchSuccess(i, n, ".*\\C$", SearchDFA); }
+void Search_Success1_NFA(int i, int n) { SearchSuccess(i, n, ".*\\C$", SearchNFA); }
+void Search_Success1_PCRE(int i, int n) { SearchSuccess(i, n, ".*\\C$", SearchPCRE); }
+void Search_Success1_RE2(int i, int n) { SearchSuccess(i, n, ".*\\C$", SearchRE2); }
+void Search_Success1_BitState(int i, int n) { SearchSuccess(i, n, ".*\\C$", SearchBitState); }
-BENCHMARK_RANGE(Search_Success1_DFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_Success1_DFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_Success1_NFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
#ifdef USEPCRE
-BENCHMARK_RANGE(Search_Success1_PCRE, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_Success1_PCRE, 8, 16<<20)->ThreadRange(1, NumCPUs());
#endif
-BENCHMARK_RANGE(Search_Success1_RE2, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_Success1_RE2, 8, 16<<20)->ThreadRange(1, NumCPUs());
BENCHMARK_RANGE(Search_Success1_BitState, 8, 2<<20)->ThreadRange(1, NumCPUs());
-void Search_Success1_Cached_DFA(int i, int n) { SearchSuccess(i, n, ".*.$", SearchCachedDFA); }
-void Search_Success1_Cached_PCRE(int i, int n) { SearchSuccess(i, n, ".*.$", SearchCachedPCRE); }
-void Search_Success1_Cached_RE2(int i, int n) { SearchSuccess(i, n, ".*.$", SearchCachedRE2); }
+void Search_Success1_CachedDFA(int i, int n) { SearchSuccess(i, n, ".*\\C$", SearchCachedDFA); }
+void Search_Success1_CachedNFA(int i, int n) { SearchSuccess(i, n, ".*\\C$", SearchCachedNFA); }
+void Search_Success1_CachedPCRE(int i, int n) { SearchSuccess(i, n, ".*\\C$", SearchCachedPCRE); }
+void Search_Success1_CachedRE2(int i, int n) { SearchSuccess(i, n, ".*\\C$", SearchCachedRE2); }
+void Search_Success1_CachedBitState(int i, int n) { SearchSuccess(i, n, ".*\\C$", SearchCachedBitState); }
+
+BENCHMARK_RANGE(Search_Success1_CachedDFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_Success1_CachedNFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+#ifdef USEPCRE
+BENCHMARK_RANGE(Search_Success1_CachedPCRE, 8, 16<<20)->ThreadRange(1, NumCPUs());
+#endif
+BENCHMARK_RANGE(Search_Success1_CachedRE2, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_Success1_CachedBitState, 8, 2<<20)->ThreadRange(1, NumCPUs());
+
+// Benchmark: AltMatch optimisation (just to verify that it works)
+// Note that OnePass doesn't implement it!
+
+void SearchAltMatch(int iters, int nbytes, SearchImpl* search) {
+ StopBenchmarkTiming();
+ string s;
+ MakeText(&s, nbytes);
+ BenchmarkMemoryUsage();
+ StartBenchmarkTiming();
+ search(iters, "\\C*", s, Prog::kAnchored, true);
+ SetBenchmarkBytesProcessed(static_cast<int64_t>(iters)*nbytes);
+}
+
+void Search_AltMatch_DFA(int i, int n) { SearchAltMatch(i, n, SearchDFA); }
+void Search_AltMatch_NFA(int i, int n) { SearchAltMatch(i, n, SearchNFA); }
+void Search_AltMatch_OnePass(int i, int n) { SearchAltMatch(i, n, SearchOnePass); }
+void Search_AltMatch_BitState(int i, int n) { SearchAltMatch(i, n, SearchBitState); }
+void Search_AltMatch_PCRE(int i, int n) { SearchAltMatch(i, n, SearchPCRE); }
+void Search_AltMatch_RE2(int i, int n) { SearchAltMatch(i, n, SearchRE2); }
-BENCHMARK_RANGE(Search_Success1_Cached_DFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_AltMatch_DFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_AltMatch_NFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_AltMatch_OnePass, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_AltMatch_BitState, 8, 16<<20)->ThreadRange(1, NumCPUs());
+#ifdef USEPCRE
+BENCHMARK_RANGE(Search_AltMatch_PCRE, 8, 16<<20)->ThreadRange(1, NumCPUs());
+#endif
+BENCHMARK_RANGE(Search_AltMatch_RE2, 8, 16<<20)->ThreadRange(1, NumCPUs());
+
+void Search_AltMatch_CachedDFA(int i, int n) { SearchAltMatch(i, n, SearchCachedDFA); }
+void Search_AltMatch_CachedNFA(int i, int n) { SearchAltMatch(i, n, SearchCachedNFA); }
+void Search_AltMatch_CachedOnePass(int i, int n) { SearchAltMatch(i, n, SearchCachedOnePass); }
+void Search_AltMatch_CachedBitState(int i, int n) { SearchAltMatch(i, n, SearchCachedBitState); }
+void Search_AltMatch_CachedPCRE(int i, int n) { SearchAltMatch(i, n, SearchCachedPCRE); }
+void Search_AltMatch_CachedRE2(int i, int n) { SearchAltMatch(i, n, SearchCachedRE2); }
+
+BENCHMARK_RANGE(Search_AltMatch_CachedDFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_AltMatch_CachedNFA, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_AltMatch_CachedOnePass, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_AltMatch_CachedBitState, 8, 16<<20)->ThreadRange(1, NumCPUs());
#ifdef USEPCRE
-BENCHMARK_RANGE(Search_Success1_Cached_PCRE, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_AltMatch_CachedPCRE, 8, 16<<20)->ThreadRange(1, NumCPUs());
#endif
-BENCHMARK_RANGE(Search_Success1_Cached_RE2, 8, 16<<20)->ThreadRange(1, NumCPUs());
+BENCHMARK_RANGE(Search_AltMatch_CachedRE2, 8, 16<<20)->ThreadRange(1, NumCPUs());
// Benchmark: use regexp to find phone number.
void SearchDigits(int iters, SearchImpl* search) {
- const char *text = "650-253-0001";
- int len = strlen(text);
+ StringPiece s("650-253-0001");
BenchmarkMemoryUsage();
- search(iters, "([0-9]+)-([0-9]+)-([0-9]+)",
- StringPiece(text, len), Prog::kAnchored, true);
+ search(iters, "([0-9]+)-([0-9]+)-([0-9]+)", s, Prog::kAnchored, true);
SetBenchmarkItemsProcessed(iters);
}
@@ -686,7 +766,6 @@ BENCHMARK(BM_Regexp_SimplifyCompile)->ThreadRange(1, NumCPUs());
BENCHMARK(BM_Regexp_NullWalk)->ThreadRange(1, NumCPUs());
BENCHMARK(BM_RE2_Compile)->ThreadRange(1, NumCPUs());
-
// Makes text of size nbytes, then calls run to search
// the text for regexp iters times.
void SearchPhone(int iters, int nbytes, ParseImpl* search) {
@@ -697,7 +776,7 @@ void SearchPhone(int iters, int nbytes, ParseImpl* search) {
BenchmarkMemoryUsage();
StartBenchmarkTiming();
search(iters, "(\\d{3}-|\\(\\d{3}\\)\\s+)(\\d{3}-\\d{4})", s);
- SetBenchmarkBytesProcessed(static_cast<int64>(iters)*nbytes);
+ SetBenchmarkBytesProcessed(static_cast<int64_t>(iters)*nbytes);
}
void SearchPhone_CachedPCRE(int i, int n) {
@@ -724,7 +803,7 @@ static string DeBruijnString(int n) {
CHECK_LT(n, 8*sizeof(int));
CHECK_GT(n, 0);
- vector<bool> did(1<<n);
+ std::vector<bool> did(1<<n);
for (int i = 0; i < 1<<n; i++)
did[i] = false;
@@ -753,11 +832,12 @@ void CacheFill(int iters, int n, SearchImpl *srch) {
string t;
for (int i = n+1; i < 20; i++) {
t = s + s;
+ using std::swap;
swap(s, t);
}
srch(iters, StringPrintf("0[01]{%d}$", n).c_str(), s,
Prog::kUnanchored, true);
- SetBenchmarkBytesProcessed(static_cast<int64>(iters)*s.size());
+ SetBenchmarkBytesProcessed(static_cast<int64_t>(iters)*s.size());
}
void CacheFillPCRE(int i, int n) { CacheFill(i, n, SearchCachedPCRE); }
@@ -806,7 +886,7 @@ void SearchDFA(int iters, const char* regexp, const StringPiece& text,
Prog* prog = re->CompileToProg(0);
CHECK(prog);
bool failed = false;
- CHECK_EQ(prog->SearchDFA(text, NULL, anchor, Prog::kFirstMatch,
+ CHECK_EQ(prog->SearchDFA(text, StringPiece(), anchor, Prog::kFirstMatch,
NULL, &failed, NULL),
expect_match);
CHECK(!failed);
@@ -822,7 +902,8 @@ void SearchNFA(int iters, const char* regexp, const StringPiece& text,
CHECK(re);
Prog* prog = re->CompileToProg(0);
CHECK(prog);
- CHECK_EQ(prog->SearchNFA(text, NULL, anchor, Prog::kFirstMatch, NULL, 0),
+ CHECK_EQ(prog->SearchNFA(text, StringPiece(), anchor, Prog::kFirstMatch,
+ NULL, 0),
expect_match);
delete prog;
re->Decref();
@@ -894,8 +975,8 @@ void SearchCachedDFA(int iters, const char* regexp, const StringPiece& text,
CHECK(prog);
for (int i = 0; i < iters; i++) {
bool failed = false;
- CHECK_EQ(prog->SearchDFA(text, NULL, anchor,
- Prog::kFirstMatch, NULL, &failed, NULL),
+ CHECK_EQ(prog->SearchDFA(text, StringPiece(), anchor, Prog::kFirstMatch,
+ NULL, &failed, NULL),
expect_match);
CHECK(!failed);
}
@@ -910,7 +991,8 @@ void SearchCachedNFA(int iters, const char* regexp, const StringPiece& text,
Prog* prog = re->CompileToProg(0);
CHECK(prog);
for (int i = 0; i < iters; i++) {
- CHECK_EQ(prog->SearchNFA(text, NULL, anchor, Prog::kFirstMatch, NULL, 0),
+ CHECK_EQ(prog->SearchNFA(text, StringPiece(), anchor, Prog::kFirstMatch,
+ NULL, 0),
expect_match);
}
delete prog;
@@ -979,7 +1061,8 @@ void Parse3NFA(int iters, const char* regexp, const StringPiece& text) {
Prog* prog = re->CompileToProg(0);
CHECK(prog);
StringPiece sp[4]; // 4 because sp[0] is whole match.
- CHECK(prog->SearchNFA(text, NULL, Prog::kAnchored, Prog::kFullMatch, sp, 4));
+ CHECK(prog->SearchNFA(text, StringPiece(), Prog::kAnchored,
+ Prog::kFullMatch, sp, 4));
delete prog;
re->Decref();
}
@@ -1050,7 +1133,8 @@ void Parse3CachedNFA(int iters, const char* regexp, const StringPiece& text) {
CHECK(prog);
StringPiece sp[4]; // 4 because sp[0] is whole match.
for (int i = 0; i < iters; i++) {
- CHECK(prog->SearchNFA(text, NULL, Prog::kAnchored, Prog::kFullMatch, sp, 4));
+ CHECK(prog->SearchNFA(text, StringPiece(), Prog::kAnchored,
+ Prog::kFullMatch, sp, 4));
}
delete prog;
re->Decref();
@@ -1122,7 +1206,8 @@ void Parse1NFA(int iters, const char* regexp, const StringPiece& text) {
Prog* prog = re->CompileToProg(0);
CHECK(prog);
StringPiece sp[2]; // 2 because sp[0] is whole match.
- CHECK(prog->SearchNFA(text, NULL, Prog::kAnchored, Prog::kFullMatch, sp, 2));
+ CHECK(prog->SearchNFA(text, StringPiece(), Prog::kAnchored,
+ Prog::kFullMatch, sp, 2));
delete prog;
re->Decref();
}
@@ -1180,7 +1265,8 @@ void Parse1CachedNFA(int iters, const char* regexp, const StringPiece& text) {
CHECK(prog);
StringPiece sp[2]; // 2 because sp[0] is whole match.
for (int i = 0; i < iters; i++) {
- CHECK(prog->SearchNFA(text, NULL, Prog::kAnchored, Prog::kFullMatch, sp, 2));
+ CHECK(prog->SearchNFA(text, StringPiece(), Prog::kAnchored,
+ Prog::kFullMatch, sp, 2));
}
delete prog;
re->Decref();
@@ -1342,14 +1428,14 @@ BENCHMARK(HTTPPartialMatchPCRE)->ThreadRange(1, NumCPUs());
#endif
BENCHMARK(HTTPPartialMatchRE2)->ThreadRange(1, NumCPUs());
-static string http_smalltext =
+static string smallhttp_text =
"GET /abc HTTP/1.1";
void SmallHTTPPartialMatchPCRE(int n) {
StringPiece a;
PCRE re("(?-s)^(?:GET|POST) +([^ ]+) HTTP");
for (int i = 0; i < n; i++) {
- PCRE::PartialMatch(http_text, re, &a);
+ PCRE::PartialMatch(smallhttp_text, re, &a);
}
}
@@ -1357,7 +1443,7 @@ void SmallHTTPPartialMatchRE2(int n) {
StringPiece a;
RE2 re("(?-s)^(?:GET|POST) +([^ ]+) HTTP");
for (int i = 0; i < n; i++) {
- RE2::PartialMatch(http_text, re, &a);
+ RE2::PartialMatch(smallhttp_text, re, &a);
}
}
@@ -1418,7 +1504,7 @@ void FullMatchPCRE(int iter, int n, const char *regexp) {
StartBenchmarkTiming();
for (int i = 0; i < iter; i++)
CHECK(PCRE::FullMatch(s, re));
- SetBenchmarkBytesProcessed(static_cast<int64>(iter)*n);
+ SetBenchmarkBytesProcessed(static_cast<int64_t>(iter)*n);
}
void FullMatchRE2(int iter, int n, const char *regexp) {
@@ -1431,7 +1517,7 @@ void FullMatchRE2(int iter, int n, const char *regexp) {
StartBenchmarkTiming();
for (int i = 0; i < iter; i++)
CHECK(RE2::FullMatch(s, re));
- SetBenchmarkBytesProcessed(static_cast<int64>(iter)*n);
+ SetBenchmarkBytesProcessed(static_cast<int64_t>(iter)*n);
}
void FullMatch_DotStar_CachedPCRE(int i, int n) { FullMatchPCRE(i, n, "(?s).*"); }
@@ -1458,4 +1544,34 @@ BENCHMARK_RANGE(FullMatch_DotStarCapture_CachedPCRE, 8, 2<<20);
#endif
BENCHMARK_RANGE(FullMatch_DotStarCapture_CachedRE2, 8, 2<<20);
+void PossibleMatchRangeCommon(int iter, const char* regexp) {
+ StopBenchmarkTiming();
+ RE2 re(regexp);
+ StartBenchmarkTiming();
+ string min;
+ string max;
+ const int kMaxLen = 16;
+ for (int i = 0; i < iter; i++) {
+ CHECK(re.PossibleMatchRange(&min, &max, kMaxLen));
+ }
+}
+
+void PossibleMatchRange_Trivial(int i) {
+ PossibleMatchRangeCommon(i, ".*");
+}
+void PossibleMatchRange_Complex(int i) {
+ PossibleMatchRangeCommon(i, "^abc[def]?[gh]{1,2}.*");
+}
+void PossibleMatchRange_Prefix(int i) {
+ PossibleMatchRangeCommon(i, "^some_random_prefix.*");
+}
+void PossibleMatchRange_NoProg(int i) {
+ PossibleMatchRangeCommon(i, "^some_random_string$");
+}
+
+BENCHMARK(PossibleMatchRange_Trivial);
+BENCHMARK(PossibleMatchRange_Complex);
+BENCHMARK(PossibleMatchRange_Prefix);
+BENCHMARK(PossibleMatchRange_NoProg);
+
} // namespace re2
diff --git a/re2/testing/regexp_generator.cc b/re2/testing/regexp_generator.cc
index cf2db11..c0f26fe 100644
--- a/re2/testing/regexp_generator.cc
+++ b/re2/testing/regexp_generator.cc
@@ -20,17 +20,25 @@
// Then RunPostfix turns each sequence into a regular expression
// and passes the regexp to HandleRegexp.
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
#include <string.h>
-#include <string>
+#include <memory>
#include <stack>
+#include <string>
#include <vector>
+
#include "util/test.h"
+#include "util/logging.h"
+#include "util/strutil.h"
+#include "util/utf.h"
#include "re2/testing/regexp_generator.h"
namespace re2 {
// Returns a vector of the egrep regexp operators.
-const vector<string>& RegexpGenerator::EgrepOps() {
+const std::vector<string>& RegexpGenerator::EgrepOps() {
static const char *ops[] = {
"%s%s",
"%s|%s",
@@ -39,39 +47,36 @@ const vector<string>& RegexpGenerator::EgrepOps() {
"%s?",
"%s\\C*",
};
- static vector<string> v(ops, ops + arraysize(ops));
+ static std::vector<string> v(ops, ops + arraysize(ops));
return v;
}
RegexpGenerator::RegexpGenerator(int maxatoms, int maxops,
- const vector<string>& atoms,
- const vector<string>& ops)
+ const std::vector<string>& atoms,
+ const std::vector<string>& ops)
: maxatoms_(maxatoms), maxops_(maxops), atoms_(atoms), ops_(ops) {
// Degenerate case.
- if (atoms_.size() == 0)
+ if (atoms_.empty())
maxatoms_ = 0;
- if (ops_.size() == 0)
+ if (ops_.empty())
maxops_ = 0;
}
// Generates all possible regular expressions (within the parameters),
// calling HandleRegexp for each one.
void RegexpGenerator::Generate() {
- vector<string> postfix;
+ std::vector<string> postfix;
GeneratePostfix(&postfix, 0, 0, 0);
}
// Generates random regular expressions, calling HandleRegexp for each one.
-void RegexpGenerator::GenerateRandom(int32 seed, int n) {
- ACMRandom acm(seed);
- acm_ = &acm;
+void RegexpGenerator::GenerateRandom(int32_t seed, int n) {
+ rng_.seed(seed);
for (int i = 0; i < n; i++) {
- vector<string> postfix;
+ std::vector<string> postfix;
GenerateRandomPostfix(&postfix, 0, 0, 0);
}
-
- acm_ = NULL;
}
// Counts and returns the number of occurrences of "%s" in s.
@@ -98,7 +103,7 @@ static int CountArgs(const string& s) {
//
// The initial call should be GeneratePostfix([empty vector], 0, 0, 0).
//
-void RegexpGenerator::GeneratePostfix(vector<string>* post, int nstk,
+void RegexpGenerator::GeneratePostfix(std::vector<string>* post, int nstk,
int ops, int atoms) {
if (nstk == 1)
RunPostfix(*post);
@@ -111,7 +116,7 @@ void RegexpGenerator::GeneratePostfix(vector<string>* post, int nstk,
// Add atoms if there is room.
if (atoms < maxatoms_) {
- for (int i = 0; i < atoms_.size(); i++) {
+ for (size_t i = 0; i < atoms_.size(); i++) {
post->push_back(atoms_[i]);
GeneratePostfix(post, nstk + 1, ops, atoms + 1);
post->pop_back();
@@ -120,7 +125,7 @@ void RegexpGenerator::GeneratePostfix(vector<string>* post, int nstk,
// Add operators if there are enough arguments.
if (ops < maxops_) {
- for (int i = 0; i < ops_.size(); i++) {
+ for (size_t i = 0; i < ops_.size(); i++) {
const string& fmt = ops_[i];
int nargs = CountArgs(fmt);
if (nargs <= nstk) {
@@ -134,11 +139,18 @@ void RegexpGenerator::GeneratePostfix(vector<string>* post, int nstk,
// Generates a random postfix command sequence.
// Stops and returns true once a single sequence has been generated.
-bool RegexpGenerator::GenerateRandomPostfix(vector<string> *post, int nstk,
+bool RegexpGenerator::GenerateRandomPostfix(std::vector<string>* post, int nstk,
int ops, int atoms) {
+ std::uniform_int_distribution<int> random_stop(0, maxatoms_ - atoms);
+ std::uniform_int_distribution<int> random_bit(0, 1);
+ std::uniform_int_distribution<int> random_ops_index(
+ 0, static_cast<int>(ops_.size()) - 1);
+ std::uniform_int_distribution<int> random_atoms_index(
+ 0, static_cast<int>(atoms_.size()) - 1);
+
for (;;) {
// Stop if we get to a single element, but only sometimes.
- if (nstk == 1 && acm_->Uniform(maxatoms_ + 1 - atoms) == 0) {
+ if (nstk == 1 && random_stop(rng_) == 0) {
RunPostfix(*post);
return true;
}
@@ -150,8 +162,8 @@ bool RegexpGenerator::GenerateRandomPostfix(vector<string> *post, int nstk,
return false;
// Add operators if there are enough arguments.
- if (ops < maxops_ && acm_->Uniform(2) == 0) {
- const string& fmt = ops_[acm_->Uniform(ops_.size())];
+ if (ops < maxops_ && random_bit(rng_) == 0) {
+ const string& fmt = ops_[random_ops_index(rng_)];
int nargs = CountArgs(fmt);
if (nargs <= nstk) {
post->push_back(fmt);
@@ -164,8 +176,8 @@ bool RegexpGenerator::GenerateRandomPostfix(vector<string> *post, int nstk,
}
// Add atoms if there is room.
- if (atoms < maxatoms_ && acm_->Uniform(2) == 0) {
- post->push_back(atoms_[acm_->Uniform(atoms_.size())]);
+ if (atoms < maxatoms_ && random_bit(rng_) == 0) {
+ post->push_back(atoms_[random_atoms_index(rng_)]);
bool ret = GenerateRandomPostfix(post, nstk + 1, ops, atoms + 1);
post->pop_back();
if (ret)
@@ -177,9 +189,9 @@ bool RegexpGenerator::GenerateRandomPostfix(vector<string> *post, int nstk,
// Interprets the postfix command sequence to create a regular expression
// passed to HandleRegexp. The results of operators like %s|%s are wrapped
// in (?: ) to avoid needing to maintain a precedence table.
-void RegexpGenerator::RunPostfix(const vector<string>& post) {
- stack<string> regexps;
- for (int i = 0; i < post.size(); i++) {
+void RegexpGenerator::RunPostfix(const std::vector<string>& post) {
+ std::stack<string> regexps;
+ for (size_t i = 0; i < post.size(); i++) {
switch (CountArgs(post[i])) {
default:
LOG(FATAL) << "Bad operator: " << post[i];
@@ -208,7 +220,7 @@ void RegexpGenerator::RunPostfix(const vector<string>& post) {
if (regexps.size() != 1) {
// Internal error - should never happen.
printf("Bad regexp program:\n");
- for (int i = 0; i < post.size(); i++) {
+ for (size_t i = 0; i < post.size(); i++) {
printf(" %s\n", CEscape(post[i]).c_str());
}
printf("Stack after running program:\n");
@@ -226,8 +238,8 @@ void RegexpGenerator::RunPostfix(const vector<string>& post) {
}
// Split s into an vector of strings, one for each UTF-8 character.
-vector<string> Explode(const StringPiece& s) {
- vector<string> v;
+std::vector<string> Explode(const StringPiece& s) {
+ std::vector<string> v;
for (const char *q = s.begin(); q < s.end(); ) {
const char* p = q;
@@ -241,8 +253,8 @@ vector<string> Explode(const StringPiece& s) {
// Split string everywhere a substring is found, returning
// vector of pieces.
-vector<string> Split(const StringPiece& sep, const StringPiece& s) {
- vector<string> v;
+std::vector<string> Split(const StringPiece& sep, const StringPiece& s) {
+ std::vector<string> v;
if (sep.size() == 0)
return Explode(s);
diff --git a/re2/testing/regexp_generator.h b/re2/testing/regexp_generator.h
index b4506f2..b746399 100644
--- a/re2/testing/regexp_generator.h
+++ b/re2/testing/regexp_generator.h
@@ -2,15 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_TESTING_REGEXP_GENERATOR_H_
+#define RE2_TESTING_REGEXP_GENERATOR_H_
+
// Regular expression generator: generates all possible
// regular expressions within given parameters (see below for details).
-#ifndef RE2_TESTING_REGEXP_GENERATOR_H__
-#define RE2_TESTING_REGEXP_GENERATOR_H__
-
+#include <stdint.h>
+#include <random>
#include <string>
#include <vector>
-#include "util/random.h"
+
#include "util/util.h"
#include "re2/stringpiece.h"
@@ -27,44 +29,47 @@ namespace re2 {
//
class RegexpGenerator {
public:
- RegexpGenerator(int maxatoms, int maxops, const vector<string>& atoms,
- const vector<string>& ops);
+ RegexpGenerator(int maxatoms, int maxops, const std::vector<string>& atoms,
+ const std::vector<string>& ops);
virtual ~RegexpGenerator() {}
// Generates all the regular expressions, calling HandleRegexp(re) for each.
void Generate();
// Generates n random regular expressions, calling HandleRegexp(re) for each.
- void GenerateRandom(int32 seed, int n);
+ void GenerateRandom(int32_t seed, int n);
// Handles a regular expression. Must be provided by subclass.
virtual void HandleRegexp(const string& regexp) = 0;
// The egrep regexp operators: * + ? | and concatenation.
- static const vector<string>& EgrepOps();
+ static const std::vector<string>& EgrepOps();
private:
- void RunPostfix(const vector<string>& post);
- void GeneratePostfix(vector<string>* post, int nstk, int ops, int lits);
- bool GenerateRandomPostfix(vector<string>* post, int nstk, int ops, int lits);
-
- int maxatoms_; // Maximum number of atoms allowed in expr.
- int maxops_; // Maximum number of ops allowed in expr.
- vector<string> atoms_; // Possible atoms.
- vector<string> ops_; // Possible ops.
- ACMRandom* acm_; // Random generator.
- DISALLOW_EVIL_CONSTRUCTORS(RegexpGenerator);
+ void RunPostfix(const std::vector<string>& post);
+ void GeneratePostfix(std::vector<string>* post, int nstk, int ops, int lits);
+ bool GenerateRandomPostfix(std::vector<string>* post, int nstk, int ops,
+ int lits);
+
+ int maxatoms_; // Maximum number of atoms allowed in expr.
+ int maxops_; // Maximum number of ops allowed in expr.
+ std::vector<string> atoms_; // Possible atoms.
+ std::vector<string> ops_; // Possible ops.
+ std::minstd_rand0 rng_; // Random number generator.
+
+ RegexpGenerator(const RegexpGenerator&) = delete;
+ RegexpGenerator& operator=(const RegexpGenerator&) = delete;
};
// Helpers for preparing arguments to RegexpGenerator constructor.
// Returns one string for each character in s.
-vector<string> Explode(const StringPiece& s);
+std::vector<string> Explode(const StringPiece& s);
// Splits string everywhere sep is found, returning
// vector of pieces.
-vector<string> Split(const StringPiece& sep, const StringPiece& s);
+std::vector<string> Split(const StringPiece& sep, const StringPiece& s);
} // namespace re2
-#endif // RE2_TESTING_REGEXP_GENERATOR_H__
+#endif // RE2_TESTING_REGEXP_GENERATOR_H_
diff --git a/re2/testing/regexp_test.cc b/re2/testing/regexp_test.cc
index f317cbc..7830322 100644
--- a/re2/testing/regexp_test.cc
+++ b/re2/testing/regexp_test.cc
@@ -4,9 +4,13 @@
// Test parse.cc, dump.cc, and tostring.cc.
+#include <stddef.h>
+#include <map>
#include <string>
#include <vector>
+
#include "util/test.h"
+#include "util/logging.h"
#include "re2/regexp.h"
namespace re2 {
@@ -19,7 +23,7 @@ TEST(Regexp, BigRef) {
re->Incref();
for (int i = 0; i < 100000; i++)
re->Decref();
- CHECK_EQ(re->Ref(), 1);
+ ASSERT_EQ(re->Ref(), 1);
re->Decref();
}
@@ -28,14 +32,15 @@ TEST(Regexp, BigRef) {
TEST(Regexp, BigConcat) {
Regexp* x;
x = Regexp::Parse("x", Regexp::NoParseFlags, NULL);
- vector<Regexp*> v(90000, x); // ToString bails out at 100000
- for (int i = 0; i < v.size(); i++)
+ std::vector<Regexp*> v(90000, x); // ToString bails out at 100000
+ for (size_t i = 0; i < v.size(); i++)
x->Incref();
- CHECK_EQ(x->Ref(), 1 + v.size()) << x->Ref();
- Regexp* re = Regexp::Concat(&v[0], v.size(), Regexp::NoParseFlags);
- CHECK_EQ(re->ToString(), string(v.size(), 'x'));
+ ASSERT_EQ(x->Ref(), 1 + static_cast<int>(v.size())) << x->Ref();
+ Regexp* re = Regexp::Concat(v.data(), static_cast<int>(v.size()),
+ Regexp::NoParseFlags);
+ ASSERT_EQ(re->ToString(), string(v.size(), 'x'));
re->Decref();
- CHECK_EQ(x->Ref(), 1) << x->Ref();
+ ASSERT_EQ(x->Ref(), 1) << x->Ref();
x->Decref();
}
@@ -46,11 +51,11 @@ TEST(Regexp, NamedCaptures) {
"(?P<g1>a+)|(e)(?P<g2>w*)+(?P<g1>b+)", Regexp::PerlX, &status);
EXPECT_TRUE(status.ok());
EXPECT_EQ(4, x->NumCaptures());
- const map<string, int>* have = x->NamedCaptures();
+ const std::map<string, int>* have = x->NamedCaptures();
EXPECT_TRUE(have != NULL);
EXPECT_EQ(2, have->size()); // there are only two named groups in
// the regexp: 'g1' and 'g2'.
- map<string, int> want;
+ std::map<string, int> want;
want["g1"] = 1;
want["g2"] = 3;
EXPECT_EQ(want, *have);
@@ -65,10 +70,10 @@ TEST(Regexp, CaptureNames) {
"(?P<g1>a+)|(e)(?P<g2>w*)+(?P<g1>b+)", Regexp::PerlX, &status);
EXPECT_TRUE(status.ok());
EXPECT_EQ(4, x->NumCaptures());
- const map<int, string>* have = x->CaptureNames();
+ const std::map<int, string>* have = x->CaptureNames();
EXPECT_TRUE(have != NULL);
EXPECT_EQ(3, have->size());
- map<int, string> want;
+ std::map<int, string> want;
want[1] = "g1";
want[3] = "g2";
want[4] = "g1";
diff --git a/re2/testing/required_prefix_test.cc b/re2/testing/required_prefix_test.cc
index 1f0b216..3f18d9b 100644
--- a/re2/testing/required_prefix_test.cc
+++ b/re2/testing/required_prefix_test.cc
@@ -2,7 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include <string>
+
#include "util/test.h"
+#include "util/logging.h"
#include "re2/regexp.h"
namespace re2 {
@@ -28,7 +31,7 @@ static PrefixTest tests[] = {
// Otherwise, it should work.
{ "^abc$", true, "abc", false, "(?-m:$)" },
- { "^abc", "true", "abc", false, "" },
+ { "^abc", true, "abc", false, "" },
{ "^(?i)abc", true, "abc", true, "" },
{ "^abcd*", true, "abc", false, "d*" },
{ "^[Aa][Bb]cd*", true, "ab", true, "cd*" },
@@ -44,18 +47,20 @@ TEST(RequiredPrefix, SimpleTests) {
if (j == 0)
flags = flags | Regexp::Latin1;
Regexp* re = Regexp::Parse(t.regexp, flags, NULL);
- CHECK(re) << " " << t.regexp;
+ ASSERT_TRUE(re != NULL) << " " << t.regexp;
+
string p;
- bool f = false;
- Regexp* s = NULL;
- CHECK_EQ(t.return_value, re->RequiredPrefix(&p, &f, &s))
- << " " << t.regexp << " " << (j==0 ? "latin1" : "utf") << " " << re->Dump();
+ bool f;
+ Regexp* s;
+ ASSERT_EQ(t.return_value, re->RequiredPrefix(&p, &f, &s))
+ << " " << t.regexp << " " << (j==0 ? "latin1" : "utf")
+ << " " << re->Dump();
if (t.return_value) {
- CHECK_EQ(p, string(t.prefix))
+ ASSERT_EQ(p, string(t.prefix))
<< " " << t.regexp << " " << (j==0 ? "latin1" : "utf");
- CHECK_EQ(f, t.foldcase)
+ ASSERT_EQ(f, t.foldcase)
<< " " << t.regexp << " " << (j==0 ? "latin1" : "utf");
- CHECK_EQ(s->ToString(), string(t.suffix))
+ ASSERT_EQ(s->ToString(), string(t.suffix))
<< " " << t.regexp << " " << (j==0 ? "latin1" : "utf");
s->Decref();
}
diff --git a/re2/testing/search_test.cc b/re2/testing/search_test.cc
index 3ab2ae3..8adef6c 100644
--- a/re2/testing/search_test.cc
+++ b/re2/testing/search_test.cc
@@ -2,14 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include <stdlib.h>
-#include <vector>
#include "util/test.h"
#include "re2/prog.h"
#include "re2/regexp.h"
#include "re2/testing/tester.h"
#include "re2/testing/exhaustive_tester.h"
+// For target `log' in the Makefile.
+#ifndef LOGGING
+#define LOGGING 0
+#endif
+
namespace re2 {
struct RegexpTest {
@@ -32,7 +35,6 @@ RegexpTest simple_tests[] = {
{ "a", "aaaaaaa" },
{ "a*", "aaaaaaa" },
{ "a*", "" },
- { "a*", NULL },
{ "ab|cd", "xabcdx" },
{ "a", "cab" },
{ "a*b", "cab" },
@@ -264,7 +266,7 @@ RegexpTest simple_tests[] = {
{ "[A-Z]+", "aAzZ" },
{ "[^\\\\]+", "Aa\\" },
{ "[acegikmoqsuwy]+", "acegikmoqsuwyACEGIKMOQSUWY" },
-
+
// Anchoring. (^abc in aabcdef was a former bug)
// The tester checks for a match in the text and
// subpieces of the text with a byte removed on either side.
@@ -297,7 +299,12 @@ RegexpTest simple_tests[] = {
{ "a", "a" },
{ "ab*", "a" },
{ "a\\C*", "a" },
-
+ { "a\\C+", "a" },
+ { "a\\C?", "a" },
+ { "a\\C*?", "a" },
+ { "a\\C+?", "a" },
+ { "a\\C??", "a" },
+
// Former bugs.
{ "a\\C*|ba\\C", "baba" },
};
@@ -309,15 +316,14 @@ TEST(Regexp, SearchTests) {
if (!TestRegexpOnText(t.regexp, t.text))
failures++;
-#ifdef LOGGING
- // Build a dummy ExhaustiveTest call that will trigger just
- // this one test, so that we log the test case.
- vector<string> atom, alpha, ops;
- atom.push_back(StringPiece(t.regexp).as_string());
- alpha.push_back(StringPiece(t.text).as_string());
- ExhaustiveTest(1, 0, atom, ops, 1, alpha, "", "");
-#endif
-
+ if (LOGGING) {
+ // Build a dummy ExhaustiveTest call that will trigger just
+ // this one test, so that we log the test case.
+ std::vector<string> atom, alpha, ops;
+ atom.push_back(t.regexp);
+ alpha.push_back(t.text);
+ ExhaustiveTest(1, 0, atom, ops, 1, alpha, "", "");
+ }
}
EXPECT_EQ(failures, 0);
}
diff --git a/re2/testing/set_test.cc b/re2/testing/set_test.cc
index 74058a4..5cdc11f 100644
--- a/re2/testing/set_test.cc
+++ b/re2/testing/set_test.cc
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <stddef.h>
+#include <string>
#include <vector>
#include "util/test.h"
+#include "util/logging.h"
#include "re2/re2.h"
#include "re2/set.h"
@@ -15,100 +16,203 @@ namespace re2 {
TEST(Set, Unanchored) {
RE2::Set s(RE2::DefaultOptions, RE2::UNANCHORED);
- CHECK_EQ(s.Add("foo", NULL), 0);
- CHECK_EQ(s.Add("(", NULL), -1);
- CHECK_EQ(s.Add("bar", NULL), 1);
+ ASSERT_EQ(s.Add("foo", NULL), 0);
+ ASSERT_EQ(s.Add("(", NULL), -1);
+ ASSERT_EQ(s.Add("bar", NULL), 1);
+ ASSERT_EQ(s.Compile(), true);
- CHECK_EQ(s.Compile(), true);
+ ASSERT_EQ(s.Match("foobar", NULL), true);
+ ASSERT_EQ(s.Match("fooba", NULL), true);
+ ASSERT_EQ(s.Match("oobar", NULL), true);
- vector<int> v;
- CHECK_EQ(s.Match("foobar", &v), true);
- CHECK_EQ(v.size(), 2);
- CHECK_EQ(v[0], 0);
- CHECK_EQ(v[1], 1);
+ std::vector<int> v;
+ ASSERT_EQ(s.Match("foobar", &v), true);
+ ASSERT_EQ(v.size(), 2);
+ ASSERT_EQ(v[0], 0);
+ ASSERT_EQ(v[1], 1);
- v.clear();
- CHECK_EQ(s.Match("fooba", &v), true);
- CHECK_EQ(v.size(), 1);
- CHECK_EQ(v[0], 0);
+ ASSERT_EQ(s.Match("fooba", &v), true);
+ ASSERT_EQ(v.size(), 1);
+ ASSERT_EQ(v[0], 0);
- v.clear();
- CHECK_EQ(s.Match("oobar", &v), true);
- CHECK_EQ(v.size(), 1);
- CHECK_EQ(v[0], 1);
+ ASSERT_EQ(s.Match("oobar", &v), true);
+ ASSERT_EQ(v.size(), 1);
+ ASSERT_EQ(v[0], 1);
}
TEST(Set, UnanchoredFactored) {
RE2::Set s(RE2::DefaultOptions, RE2::UNANCHORED);
- CHECK_EQ(s.Add("foo", NULL), 0);
- CHECK_EQ(s.Add("(", NULL), -1);
- CHECK_EQ(s.Add("foobar", NULL), 1);
+ ASSERT_EQ(s.Add("foo", NULL), 0);
+ ASSERT_EQ(s.Add("(", NULL), -1);
+ ASSERT_EQ(s.Add("foobar", NULL), 1);
+ ASSERT_EQ(s.Compile(), true);
+
+ ASSERT_EQ(s.Match("foobar", NULL), true);
+ ASSERT_EQ(s.Match("obarfoobaroo", NULL), true);
+ ASSERT_EQ(s.Match("fooba", NULL), true);
+ ASSERT_EQ(s.Match("oobar", NULL), false);
+
+ std::vector<int> v;
+ ASSERT_EQ(s.Match("foobar", &v), true);
+ ASSERT_EQ(v.size(), 2);
+ ASSERT_EQ(v[0], 0);
+ ASSERT_EQ(v[1], 1);
+
+ ASSERT_EQ(s.Match("obarfoobaroo", &v), true);
+ ASSERT_EQ(v.size(), 2);
+ ASSERT_EQ(v[0], 0);
+ ASSERT_EQ(v[1], 1);
+
+ ASSERT_EQ(s.Match("fooba", &v), true);
+ ASSERT_EQ(v.size(), 1);
+ ASSERT_EQ(v[0], 0);
+
+ ASSERT_EQ(s.Match("oobar", &v), false);
+ ASSERT_EQ(v.size(), 0);
+}
- CHECK_EQ(s.Compile(), true);
+TEST(Set, UnanchoredDollar) {
+ RE2::Set s(RE2::DefaultOptions, RE2::UNANCHORED);
- vector<int> v;
- CHECK_EQ(s.Match("foobar", &v), true);
- CHECK_EQ(v.size(), 2);
- CHECK_EQ(v[0], 0);
- CHECK_EQ(v[1], 1);
+ ASSERT_EQ(s.Add("foo$", NULL), 0);
+ ASSERT_EQ(s.Compile(), true);
- v.clear();
- CHECK_EQ(s.Match("obarfoobaroo", &v), true);
- CHECK_EQ(v.size(), 2);
- CHECK_EQ(v[0], 0);
- CHECK_EQ(v[1], 1);
+ ASSERT_EQ(s.Match("foo", NULL), true);
+ ASSERT_EQ(s.Match("foobar", NULL), false);
- v.clear();
- CHECK_EQ(s.Match("fooba", &v), true);
- CHECK_EQ(v.size(), 1);
- CHECK_EQ(v[0], 0);
+ std::vector<int> v;
+ ASSERT_EQ(s.Match("foo", &v), true);
+ ASSERT_EQ(v.size(), 1);
+ ASSERT_EQ(v[0], 0);
- v.clear();
- CHECK_EQ(s.Match("oobar", &v), false);
- CHECK_EQ(v.size(), 0);
+ ASSERT_EQ(s.Match("foobar", &v), false);
+ ASSERT_EQ(v.size(), 0);
}
-TEST(Set, UnanchoredDollar) {
+TEST(Set, UnanchoredWordBoundary) {
RE2::Set s(RE2::DefaultOptions, RE2::UNANCHORED);
-
- CHECK_EQ(s.Add("foo$", NULL), 0);
- CHECK_EQ(s.Compile(), true);
-
- vector<int> v;
- CHECK_EQ(s.Match("foo", &v), true);
- CHECK_EQ(v.size(), 1);
- CHECK_EQ(v[0], 0);
+
+ ASSERT_EQ(s.Add("foo\\b", NULL), 0);
+ ASSERT_EQ(s.Compile(), true);
+
+ ASSERT_EQ(s.Match("foo", NULL), true);
+ ASSERT_EQ(s.Match("foobar", NULL), false);
+ ASSERT_EQ(s.Match("foo bar", NULL), true);
+
+ std::vector<int> v;
+ ASSERT_EQ(s.Match("foo", &v), true);
+ ASSERT_EQ(v.size(), 1);
+ ASSERT_EQ(v[0], 0);
+
+ ASSERT_EQ(s.Match("foobar", &v), false);
+ ASSERT_EQ(v.size(), 0);
+
+ ASSERT_EQ(s.Match("foo bar", &v), true);
+ ASSERT_EQ(v.size(), 1);
+ ASSERT_EQ(v[0], 0);
}
TEST(Set, Anchored) {
RE2::Set s(RE2::DefaultOptions, RE2::ANCHOR_BOTH);
- CHECK_EQ(s.Add("foo", NULL), 0);
- CHECK_EQ(s.Add("(", NULL), -1);
- CHECK_EQ(s.Add("bar", NULL), 1);
+ ASSERT_EQ(s.Add("foo", NULL), 0);
+ ASSERT_EQ(s.Add("(", NULL), -1);
+ ASSERT_EQ(s.Add("bar", NULL), 1);
+ ASSERT_EQ(s.Compile(), true);
- CHECK_EQ(s.Compile(), true);
+ ASSERT_EQ(s.Match("foobar", NULL), false);
+ ASSERT_EQ(s.Match("fooba", NULL), false);
+ ASSERT_EQ(s.Match("oobar", NULL), false);
+ ASSERT_EQ(s.Match("foo", NULL), true);
+ ASSERT_EQ(s.Match("bar", NULL), true);
- vector<int> v;
- CHECK_EQ(s.Match("foobar", &v), false);
- CHECK_EQ(v.size(), 0);
+ std::vector<int> v;
+ ASSERT_EQ(s.Match("foobar", &v), false);
+ ASSERT_EQ(v.size(), 0);
- CHECK_EQ(s.Match("fooba", &v), false);
- CHECK_EQ(v.size(), 0);
+ ASSERT_EQ(s.Match("fooba", &v), false);
+ ASSERT_EQ(v.size(), 0);
- CHECK_EQ(s.Match("oobar", &v), false);
- CHECK_EQ(v.size(), 0);
+ ASSERT_EQ(s.Match("oobar", &v), false);
+ ASSERT_EQ(v.size(), 0);
- CHECK_EQ(s.Match("foo", &v), true);
- CHECK_EQ(v.size(), 1);
- CHECK_EQ(v[0], 0);
+ ASSERT_EQ(s.Match("foo", &v), true);
+ ASSERT_EQ(v.size(), 1);
+ ASSERT_EQ(v[0], 0);
- CHECK_EQ(s.Match("bar", &v), true);
- CHECK_EQ(v.size(), 1);
- CHECK_EQ(v[0], 1);
+ ASSERT_EQ(s.Match("bar", &v), true);
+ ASSERT_EQ(v.size(), 1);
+ ASSERT_EQ(v[0], 1);
+}
+
+TEST(Set, EmptyUnanchored) {
+ RE2::Set s(RE2::DefaultOptions, RE2::UNANCHORED);
+
+ ASSERT_EQ(s.Compile(), true);
+
+ ASSERT_EQ(s.Match("", NULL), false);
+ ASSERT_EQ(s.Match("foobar", NULL), false);
+ std::vector<int> v;
+ ASSERT_EQ(s.Match("", &v), false);
+ ASSERT_EQ(v.size(), 0);
+
+ ASSERT_EQ(s.Match("foobar", &v), false);
+ ASSERT_EQ(v.size(), 0);
}
-} // namespace re2
+TEST(Set, EmptyAnchored) {
+ RE2::Set s(RE2::DefaultOptions, RE2::ANCHOR_BOTH);
+
+ ASSERT_EQ(s.Compile(), true);
+ ASSERT_EQ(s.Match("", NULL), false);
+ ASSERT_EQ(s.Match("foobar", NULL), false);
+
+ std::vector<int> v;
+ ASSERT_EQ(s.Match("", &v), false);
+ ASSERT_EQ(v.size(), 0);
+
+ ASSERT_EQ(s.Match("foobar", &v), false);
+ ASSERT_EQ(v.size(), 0);
+}
+
+TEST(Set, Prefix) {
+ RE2::Set s(RE2::DefaultOptions, RE2::ANCHOR_BOTH);
+
+ ASSERT_EQ(s.Add("/prefix/\\d*", NULL), 0);
+ ASSERT_EQ(s.Compile(), true);
+
+ ASSERT_EQ(s.Match("/prefix", NULL), false);
+ ASSERT_EQ(s.Match("/prefix/", NULL), true);
+ ASSERT_EQ(s.Match("/prefix/42", NULL), true);
+
+ std::vector<int> v;
+ ASSERT_EQ(s.Match("/prefix", &v), false);
+ ASSERT_EQ(v.size(), 0);
+
+ ASSERT_EQ(s.Match("/prefix/", &v), true);
+ ASSERT_EQ(v.size(), 1);
+ ASSERT_EQ(v[0], 0);
+
+ ASSERT_EQ(s.Match("/prefix/42", &v), true);
+ ASSERT_EQ(v.size(), 1);
+ ASSERT_EQ(v[0], 0);
+}
+
+TEST(Set, OutOfMemory) {
+ RE2::Set s(RE2::DefaultOptions, RE2::UNANCHORED);
+
+ string a(10000, 'a');
+ ASSERT_EQ(s.Add(a, NULL), 0);
+ ASSERT_EQ(s.Compile(), true);
+
+ std::vector<int> v;
+ RE2::Set::ErrorInfo ei;
+ ASSERT_EQ(s.Match(a, &v, &ei), false);
+ ASSERT_EQ(v.size(), 0);
+ ASSERT_EQ(ei.kind, RE2::Set::kOutOfMemory);
+}
+
+} // namespace re2
diff --git a/re2/testing/simplify_test.cc b/re2/testing/simplify_test.cc
index d54837c..ede0f32 100644
--- a/re2/testing/simplify_test.cc
+++ b/re2/testing/simplify_test.cc
@@ -4,9 +4,11 @@
// Test simplify.cc.
+#include <string.h>
#include <string>
-#include <vector>
+
#include "util/test.h"
+#include "util/logging.h"
#include "re2/regexp.h"
namespace re2 {
@@ -123,7 +125,7 @@ static Test tests[] = {
// explicit (?:) in place of non-parenthesized empty strings,
// to make them easier to spot for other parsers.
{ "(a|b|)", "([a-b]|(?:))" },
- { "(|)", "()" },
+ { "(|)", "((?:)|(?:))" },
{ "a()", "a()" },
{ "(()|())", "(()|())" },
{ "(a|)", "(a|(?:))" },
@@ -136,6 +138,110 @@ static Test tests[] = {
{ "(){1}", "()" },
{ "(){1,}", "()+" },
{ "(){0,2}", "(?:()()?)?" },
+
+ // Test that coalescing occurs and that the resulting repeats are simplified.
+ // Two-op combinations of *, +, ?, {n}, {n,} and {n,m} with a literal:
+ { "a*a*", "a*" },
+ { "a*a+", "a+" },
+ { "a*a?", "a*" },
+ { "a*a{2}", "aa+" },
+ { "a*a{2,}", "aa+" },
+ { "a*a{2,3}", "aa+" },
+ { "a+a*", "a+" },
+ { "a+a+", "aa+" },
+ { "a+a?", "a+" },
+ { "a+a{2}", "aaa+" },
+ { "a+a{2,}", "aaa+" },
+ { "a+a{2,3}", "aaa+" },
+ { "a?a*", "a*" },
+ { "a?a+", "a+" },
+ { "a?a?", "(?:aa?)?" },
+ { "a?a{2}", "aaa?" },
+ { "a?a{2,}", "aa+" },
+ { "a?a{2,3}", "aa(?:aa?)?" },
+ { "a{2}a*", "aa+" },
+ { "a{2}a+", "aaa+" },
+ { "a{2}a?", "aaa?" },
+ { "a{2}a{2}", "aaaa" },
+ { "a{2}a{2,}", "aaaa+" },
+ { "a{2}a{2,3}", "aaaaa?" },
+ { "a{2,}a*", "aa+" },
+ { "a{2,}a+", "aaa+" },
+ { "a{2,}a?", "aa+" },
+ { "a{2,}a{2}", "aaaa+" },
+ { "a{2,}a{2,}", "aaaa+" },
+ { "a{2,}a{2,3}", "aaaa+" },
+ { "a{2,3}a*", "aa+" },
+ { "a{2,3}a+", "aaa+" },
+ { "a{2,3}a?", "aa(?:aa?)?" },
+ { "a{2,3}a{2}", "aaaaa?" },
+ { "a{2,3}a{2,}", "aaaa+" },
+ { "a{2,3}a{2,3}", "aaaa(?:aa?)?" },
+ // With a char class, any char and any byte:
+ { "\\d*\\d*", "[0-9]*" },
+ { ".*.*", ".*" },
+ { "\\C*\\C*", "\\C*" },
+ // FoldCase works, but must be consistent:
+ { "(?i)A*a*", "[Aa]*" },
+ { "(?i)a+A+", "[Aa][Aa]+" },
+ { "(?i)A*(?-i)a*", "[Aa]*a*" },
+ { "(?i)a+(?-i)A+", "[Aa]+A+" },
+ // NonGreedy works, but must be consistent:
+ { "a*?a*?", "a*?" },
+ { "a+?a+?", "aa+?" },
+ { "a*?a*", "a*?a*" },
+ { "a+a+?", "a+a+?" },
+ // The second element is the literal, char class, any char or any byte:
+ { "a*a", "a+" },
+ { "\\d*\\d", "[0-9]+" },
+ { ".*.", ".+" },
+ { "\\C*\\C", "\\C+" },
+ // FoldCase works, but must be consistent:
+ { "(?i)A*a", "[Aa]+" },
+ { "(?i)a+A", "[Aa][Aa]+" },
+ { "(?i)A*(?-i)a", "[Aa]*a" },
+ { "(?i)a+(?-i)A", "[Aa]+A" },
+ // The second element is a literal string that begins with the literal:
+ { "a*aa", "aa+" },
+ { "a*aab", "aa+b" },
+ // FoldCase works, but must be consistent:
+ { "(?i)a*aa", "[Aa][Aa]+" },
+ { "(?i)a*aab", "[Aa][Aa]+[Bb]" },
+ { "(?i)a*(?-i)aa", "[Aa]*aa" },
+ { "(?i)a*(?-i)aab", "[Aa]*aab" },
+ // Negative tests with mismatching ops:
+ { "a*b*", "a*b*" },
+ { "\\d*\\D*", "[0-9]*[^0-9]*" },
+ { "a+b", "a+b" },
+ { "\\d+\\D", "[0-9]+[^0-9]" },
+ { "a?bb", "a?bb" },
+ // Negative tests with capturing groups:
+ { "(a*)a*", "(a*)a*" },
+ { "a+(a)", "a+(a)" },
+ { "(a?)(aa)", "(a?)(aa)" },
+ // Just for fun:
+ { "aa*aa+aa?aa{2}aaa{2,}aaa{2,3}a", "aaaaaaaaaaaaaaaa+" },
+
+ // During coalescing, the child of the repeat changes, so we build a new
+ // repeat. The new repeat must have the min and max of the old repeat.
+ // Failure to copy them results in min=0 and max=0 -> empty match.
+ { "(?:a*aab){2}", "aa+baa+b" },
+
+ // During coalescing, the child of the capture changes, so we build a new
+ // capture. The new capture must have the cap of the old capture.
+ // Failure to copy it results in cap=0 -> ToString() logs a fatal error.
+ { "(a*aab)", "(aa+b)" },
+
+ // Test squashing of **, ++, ?? et cetera.
+ { "(?:(?:a){0,}){0,}", "a*" },
+ { "(?:(?:a){1,}){1,}", "a+" },
+ { "(?:(?:a){0,1}){0,1}", "a?" },
+ { "(?:(?:a){0,}){1,}", "a*" },
+ { "(?:(?:a){0,}){0,1}", "a*" },
+ { "(?:(?:a){1,}){0,}", "a*" },
+ { "(?:(?:a){1,}){0,1}", "a*" },
+ { "(?:(?:a){0,1}){0,}", "a*" },
+ { "(?:(?:a){0,1}){1,}", "a*" },
};
TEST(TestSimplify, SimpleRegexps) {
@@ -146,13 +252,13 @@ TEST(TestSimplify, SimpleRegexps) {
Regexp::MatchNL | (Regexp::LikePerl &
~Regexp::OneLine),
&status);
- CHECK(re != NULL) << " " << tests[i].regexp << " " << status.Text();
+ ASSERT_TRUE(re != NULL) << " " << tests[i].regexp << " " << status.Text();
Regexp* sre = re->Simplify();
- CHECK(sre != NULL);
+ ASSERT_TRUE(sre != NULL);
// Check that already-simple regexps don't allocate new ones.
if (strcmp(tests[i].regexp, tests[i].simplified) == 0) {
- CHECK(re == sre) << " " << tests[i].regexp
+ ASSERT_TRUE(re == sre) << " " << tests[i].regexp
<< " " << re->ToString() << " " << sre->ToString();
}
diff --git a/re2/testing/string_generator.cc b/re2/testing/string_generator.cc
index 5be6d3e..feef200 100644
--- a/re2/testing/string_generator.cc
+++ b/re2/testing/string_generator.cc
@@ -6,30 +6,31 @@
// maxlen letters using the set of letters in alpha.
// Fetch strings using a Java-like Next()/HasNext() interface.
+#include <stddef.h>
+#include <stdint.h>
#include <string>
#include <vector>
+
#include "util/test.h"
+#include "util/logging.h"
#include "re2/testing/string_generator.h"
namespace re2 {
-StringGenerator::StringGenerator(int maxlen, const vector<string>& alphabet)
+StringGenerator::StringGenerator(int maxlen,
+ const std::vector<string>& alphabet)
: maxlen_(maxlen), alphabet_(alphabet),
generate_null_(false),
- random_(false), nrandom_(0), acm_(NULL) {
+ random_(false), nrandom_(0) {
// Degenerate case: no letters, no non-empty strings.
- if (alphabet_.size() == 0)
+ if (alphabet_.empty())
maxlen_ = 0;
// Next() will return empty string (digits_ is empty).
hasnext_ = true;
}
-StringGenerator::~StringGenerator() {
- delete acm_;
-}
-
// Resets the string generator state to the beginning.
void StringGenerator::Reset() {
digits_.clear();
@@ -43,14 +44,14 @@ void StringGenerator::Reset() {
// Returns false if all the numbers have been used.
bool StringGenerator::IncrementDigits() {
// First try to increment the current number.
- for (int i = digits_.size() - 1; i >= 0; i--) {
- if (++digits_[i] < alphabet_.size())
+ for (int i = static_cast<int>(digits_.size()) - 1; i >= 0; i--) {
+ if (++digits_[i] < static_cast<int>(alphabet_.size()))
return true;
digits_[i] = 0;
}
// If that failed, make a longer number.
- if (digits_.size() < maxlen_) {
+ if (static_cast<int>(digits_.size()) < maxlen_) {
digits_.push_back(0);
return true;
}
@@ -64,11 +65,15 @@ bool StringGenerator::RandomDigits() {
if (--nrandom_ <= 0)
return false;
+ std::uniform_int_distribution<int> random_len(0, maxlen_);
+ std::uniform_int_distribution<int> random_alphabet_index(
+ 0, static_cast<int>(alphabet_.size()) - 1);
+
// Pick length.
- int len = acm_->Uniform(maxlen_+1);
+ int len = random_len(rng_);
digits_.resize(len);
for (int i = 0; i < len; i++)
- digits_[i] = acm_->Uniform(alphabet_.size());
+ digits_[i] = random_alphabet_index(rng_);
return true;
}
@@ -80,11 +85,11 @@ const StringPiece& StringGenerator::Next() {
CHECK(hasnext_);
if (generate_null_) {
generate_null_ = false;
- sp_ = NULL;
+ sp_ = StringPiece();
return sp_;
}
s_.clear();
- for (int i = 0; i < digits_.size(); i++) {
+ for (size_t i = 0; i < digits_.size(); i++) {
s_ += alphabet_[digits_[i]];
}
hasnext_ = random_ ? RandomDigits() : IncrementDigits();
@@ -93,11 +98,8 @@ const StringPiece& StringGenerator::Next() {
}
// Sets generator up to return n random strings.
-void StringGenerator::Random(int32 seed, int n) {
- if (acm_ == NULL)
- acm_ = new ACMRandom(seed);
- else
- acm_->Reset(seed);
+void StringGenerator::Random(int32_t seed, int n) {
+ rng_.seed(seed);
random_ = true;
nrandom_ = n;
@@ -110,4 +112,3 @@ void StringGenerator::GenerateNULL() {
}
} // namespace re2
-
diff --git a/re2/testing/string_generator.h b/re2/testing/string_generator.h
index 6a9ef42..5a36617 100644
--- a/re2/testing/string_generator.h
+++ b/re2/testing/string_generator.h
@@ -2,25 +2,28 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_TESTING_STRING_GENERATOR_H_
+#define RE2_TESTING_STRING_GENERATOR_H_
+
// String generator: generates all possible strings of up to
// maxlen letters using the set of letters in alpha.
// Fetch strings using a Java-like Next()/HasNext() interface.
-#ifndef RE2_TESTING_STRING_GENERATOR_H__
-#define RE2_TESTING_STRING_GENERATOR_H__
-
+#include <stdint.h>
+#include <random>
#include <string>
#include <vector>
+
#include "util/util.h"
-#include "util/random.h"
#include "re2/stringpiece.h"
namespace re2 {
class StringGenerator {
public:
- StringGenerator(int maxlen, const vector<string>& alphabet);
- ~StringGenerator();
+ StringGenerator(int maxlen, const std::vector<string>& alphabet);
+ ~StringGenerator() {}
+
const StringPiece& Next();
bool HasNext() { return hasnext_; }
@@ -28,7 +31,7 @@ class StringGenerator {
void Reset();
// Causes generator to emit random strings for next n calls to Next().
- void Random(int32 seed, int n);
+ void Random(int32_t seed, int n);
// Causes generator to emit a NULL as the next call.
void GenerateNULL();
@@ -38,21 +41,23 @@ class StringGenerator {
bool RandomDigits();
// Global state.
- int maxlen_; // Maximum length string to generate.
- vector<string> alphabet_; // Alphabet, one string per letter.
+ int maxlen_; // Maximum length string to generate.
+ std::vector<string> alphabet_; // Alphabet, one string per letter.
// Iteration state.
StringPiece sp_; // Last StringPiece returned by Next().
string s_; // String data in last StringPiece returned by Next().
bool hasnext_; // Whether Next() can be called again.
- vector<int> digits_; // Alphabet indices for next string.
+ std::vector<int> digits_; // Alphabet indices for next string.
bool generate_null_; // Whether to generate a NULL StringPiece next.
bool random_; // Whether generated strings are random.
int nrandom_; // Number of random strings left to generate.
- ACMRandom* acm_; // Random number generator
- DISALLOW_EVIL_CONSTRUCTORS(StringGenerator);
+ std::minstd_rand0 rng_; // Random number generator.
+
+ StringGenerator(const StringGenerator&) = delete;
+ StringGenerator& operator=(const StringGenerator&) = delete;
};
} // namespace re2
-#endif // RE2_TESTING_STRING_GENERATOR_H__
+#endif // RE2_TESTING_STRING_GENERATOR_H_
diff --git a/re2/testing/string_generator_test.cc b/re2/testing/string_generator_test.cc
index d13401a..2c040a3 100644
--- a/re2/testing/string_generator_test.cc
+++ b/re2/testing/string_generator_test.cc
@@ -4,18 +4,19 @@
// Test StringGenerator.
-#include <stdlib.h>
+#include <stdint.h>
#include <string>
-#include <vector>
+
#include "util/test.h"
+#include "util/utf.h"
#include "re2/testing/string_generator.h"
#include "re2/testing/regexp_generator.h"
namespace re2 {
// Returns i to the e.
-static int64 IntegerPower(int i, int e) {
- int64 p = 1;
+static int64_t IntegerPower(int i, int e) {
+ int64_t p = 1;
while (e-- > 0)
p *= i;
return p;
@@ -30,7 +31,7 @@ static int64 IntegerPower(int i, int e) {
// If all of these hold, the StringGenerator is behaving.
// Assumes that the alphabet is sorted, so that the generated
// strings can just be compared lexicographically.
-static void RunTest(int len, string alphabet, bool donull) {
+static void RunTest(int len, const string& alphabet, bool donull) {
StringGenerator g(len, Explode(alphabet));
int n = 0;
@@ -46,7 +47,7 @@ static void RunTest(int len, string alphabet, bool donull) {
}
while (g.HasNext()) {
- string s = g.Next().as_string();
+ string s = string(g.Next());
n++;
// Check that all characters in s appear in alphabet.
@@ -69,7 +70,7 @@ static void RunTest(int len, string alphabet, bool donull) {
}
// Check total string count.
- int64 m = 0;
+ int64_t m = 0;
int alpha = utflen(alphabet.c_str());
if (alpha == 0) // Degenerate case.
len = 0;
diff --git a/re2/testing/tester.cc b/re2/testing/tester.cc
index 003dc5a..c37aada 100644
--- a/re2/testing/tester.cc
+++ b/re2/testing/tester.cc
@@ -4,8 +4,15 @@
// Regular expression engine tester -- test all the implementations against each other.
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <string>
+
#include "util/util.h"
#include "util/flags.h"
+#include "util/logging.h"
+#include "util/strutil.h"
#include "re2/testing/tester.h"
#include "re2/prog.h"
#include "re2/re2.h"
@@ -26,7 +33,7 @@ enum {
kMaxSubmatch = 1+16, // $0...$16
};
-const char* engine_types[kEngineMax] = {
+const char* engine_names[kEngineMax] = {
"Backtrack",
"NFA",
"DFA",
@@ -39,18 +46,18 @@ const char* engine_types[kEngineMax] = {
"PCRE",
};
-// Returns the name string for the type t.
-static string EngineString(Engine t) {
- if (t < 0 || t >= arraysize(engine_types) || engine_types[t] == NULL) {
- return StringPrintf("type%d", static_cast<int>(t));
- }
- return engine_types[t];
+// Returns the name of the engine.
+static const char* EngineName(Engine e) {
+ CHECK_GE(e, 0);
+ CHECK_LT(e, arraysize(engine_names));
+ CHECK(engine_names[e] != NULL);
+ return engine_names[e];
}
// Returns bit mask of engines to use.
-static uint32 Engines() {
- static uint32 cached_engines;
- static bool did_parse;
+static uint32_t Engines() {
+ static bool did_parse = false;
+ static uint32_t cached_engines = 0;
if (did_parse)
return cached_engines;
@@ -59,7 +66,7 @@ static uint32 Engines() {
cached_engines = ~0;
} else {
for (Engine i = static_cast<Engine>(0); i < kEngineMax; i++)
- if (strstr(EngineString(i).c_str(), FLAGS_regexp_engines.c_str()))
+ if (FLAGS_regexp_engines.find(EngineName(i)) != string::npos)
cached_engines |= 1<<i;
}
@@ -69,8 +76,9 @@ static uint32 Engines() {
cached_engines &= ~(1<<kEnginePCRE);
for (Engine i = static_cast<Engine>(0); i < kEngineMax; i++) {
if (cached_engines & (1<<i))
- LOG(INFO) << EngineString(i) << " enabled";
+ LOG(INFO) << EngineName(i) << " enabled";
}
+
did_parse = true;
return cached_engines;
}
@@ -92,15 +100,14 @@ typedef TestInstance::Result Result;
static string FormatCapture(const StringPiece& text, const StringPiece& s) {
if (s.begin() == NULL)
return "(?,?)";
- return StringPrintf("(%d,%d)",
- static_cast<int>(s.begin() - text.begin()),
- static_cast<int>(s.end() - text.begin()));
+ return StringPrintf("(%td,%td)",
+ s.begin() - text.begin(), s.end() - text.begin());
}
// Returns whether text contains non-ASCII (>= 0x80) bytes.
static bool NonASCII(const StringPiece& text) {
- for (int i = 0; i < text.size(); i++)
- if ((uint8)text[i] >= 0x80)
+ for (size_t i = 0; i < text.size(); i++)
+ if ((uint8_t)text[i] >= 0x80)
return true;
return false;
}
@@ -153,7 +160,7 @@ static string FormatMode(Regexp::ParseFlags flags) {
for (int i = 0; i < arraysize(parse_modes); i++)
if (parse_modes[i].parse_flags == flags)
return parse_modes[i].desc;
- return StringPrintf("%#x", static_cast<uint>(flags));
+ return StringPrintf("%#x", static_cast<uint32_t>(flags));
}
// Constructs and saves all the matching engines that
@@ -213,7 +220,7 @@ TestInstance::TestInstance(const StringPiece& regexp_str, Prog::MatchKind kind,
}
// Create re string that will be used for RE and RE2.
- string re = regexp_str.as_string();
+ string re = string(regexp_str);
// Accomodate flags.
// Regexp::Latin1 will be accomodated below.
if (!(flags & Regexp::OneLine))
@@ -246,6 +253,7 @@ TestInstance::TestInstance(const StringPiece& regexp_str, Prog::MatchKind kind,
// 2. It treats $ as this weird thing meaning end of string
// or before the \n at the end of the string.
// 3. It doesn't implement POSIX leftmost-longest matching.
+ // 4. It lets \s match vertical tab.
// MimicsPCRE() detects 1 and 2.
if ((Engines() & (1<<kEnginePCRE)) && regexp_->MimicsPCRE() &&
kind_ != Prog::kLongestMatch) {
@@ -280,8 +288,10 @@ void TestInstance::RunSearch(Engine type,
const StringPiece& orig_text,
const StringPiece& orig_context,
Prog::Anchor anchor,
- Result *result) {
- memset(result, 0, sizeof *result);
+ Result* result) {
+ // Result is not trivial, so we cannot freely clear it with memset(3),
+ // but zeroing objects like so is safe and expedient for our purposes.
+ memset(reinterpret_cast<void*>(result), 0, sizeof *result);
if (regexp_ == NULL) {
result->skipped = true;
return;
@@ -343,7 +353,8 @@ void TestInstance::RunSearch(Engine type,
Prog::kAnchored, Prog::kLongestMatch,
result->submatch,
&result->skipped, NULL)) {
- LOG(ERROR) << "Reverse DFA inconsistency: " << CEscape(regexp_str_)
+ LOG(ERROR) << "Reverse DFA inconsistency: "
+ << CEscape(regexp_str_)
<< " on " << CEscape(text);
result->matched = false;
}
@@ -390,10 +401,13 @@ void TestInstance::RunSearch(Engine type,
if (kind_ == Prog::kFullMatch)
re_anchor = RE2::ANCHOR_BOTH;
- result->matched = re2_->Match(context,
- text.begin() - context.begin(),
- text.end() - context.begin(),
- re_anchor, result->submatch, nsubmatch);
+ result->matched = re2_->Match(
+ context,
+ static_cast<size_t>(text.begin() - context.begin()),
+ static_cast<size_t>(text.end() - context.begin()),
+ re_anchor,
+ result->submatch,
+ nsubmatch);
result->have_submatch = nsubmatch > 0;
break;
}
@@ -405,13 +419,34 @@ void TestInstance::RunSearch(Engine type,
break;
}
+ // In Perl/PCRE, \v matches any character considered vertical
+ // whitespace, not just vertical tab. Regexp::MimicsPCRE() is
+ // unable to handle all cases of this, unfortunately, so just
+ // catch them here. :(
+ if (regexp_str_.find("\\v") != StringPiece::npos &&
+ (text.find('\n') != StringPiece::npos ||
+ text.find('\f') != StringPiece::npos ||
+ text.find('\r') != StringPiece::npos)) {
+ result->skipped = true;
+ break;
+ }
+
+ // PCRE 8.34 or so started allowing vertical tab to match \s,
+ // following a change made in Perl 5.18. RE2 does not.
+ if ((regexp_str_.find("\\s") != StringPiece::npos ||
+ regexp_str_.find("\\S") != StringPiece::npos) &&
+ text.find('\v') != StringPiece::npos) {
+ result->skipped = true;
+ break;
+ }
+
const PCRE::Arg **argptr = new const PCRE::Arg*[nsubmatch];
PCRE::Arg *a = new PCRE::Arg[nsubmatch];
for (int i = 0; i < nsubmatch; i++) {
a[i] = PCRE::Arg(&result->submatch[i]);
argptr[i] = &a[i];
}
- int consumed;
+ size_t consumed;
PCRE::Anchor pcre_anchor;
if (anchor == Prog::kAnchored)
pcre_anchor = PCRE::ANCHOR_START;
@@ -432,14 +467,6 @@ void TestInstance::RunSearch(Engine type,
break;
}
result->have_submatch = true;
-
- // Work around RE interface bug: PCRE returns -1 as the
- // offsets for an unmatched subexpression, and RE should
- // turn that into StringPiece(NULL) but in fact it uses
- // StringPiece(text.begin() - 1, 0). Oops.
- for (int i = 0; i < nsubmatch; i++)
- if (result->submatch[i].begin() == text.begin() - 1)
- result->submatch[i] = NULL;
delete[] argptr;
delete[] a;
break;
@@ -505,7 +532,7 @@ bool TestInstance::RunCase(const StringPiece& text, const StringPiece& context,
}
// We disagree with PCRE on the meaning of some Unicode matches.
- // In particular, we treat all non-ASCII UTF-8 as word characters.
+ // In particular, we treat non-ASCII UTF-8 as non-word characters.
// We also treat "empty" character sets like [^\w\W] as being
// impossible to match, while PCRE apparently excludes some code
// points (e.g., 0x0080) from both \w and \W.
@@ -553,7 +580,7 @@ void TestInstance::LogMatch(const char* prefix, Engine e,
const StringPiece& text, const StringPiece& context,
Prog::Anchor anchor) {
LOG(INFO) << prefix
- << EngineString(e)
+ << EngineName(e)
<< " regexp "
<< CEscape(regexp_str_)
<< " "
@@ -592,14 +619,14 @@ Tester::Tester(const StringPiece& regexp) {
}
Tester::~Tester() {
- for (int i = 0; i < v_.size(); i++)
+ for (size_t i = 0; i < v_.size(); i++)
delete v_[i];
}
bool Tester::TestCase(const StringPiece& text, const StringPiece& context,
Prog::Anchor anchor) {
bool okay = true;
- for (int i = 0; i < v_.size(); i++)
+ for (size_t i = 0; i < v_.size(); i++)
okay &= (!v_[i]->error() && v_[i]->RunCase(text, context, anchor));
return okay;
}
diff --git a/re2/testing/tester.h b/re2/testing/tester.h
index 6e16e77..47d0c43 100644
--- a/re2/testing/tester.h
+++ b/re2/testing/tester.h
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_TESTING_TESTER_H_
+#define RE2_TESTING_TESTER_H_
+
// Comparative tester for regular expression matching.
// Checks all implementations against each other.
-#ifndef RE2_TESTING_TESTER_H__
-#define RE2_TESTING_TESTER_H__
+#include <vector>
#include "re2/stringpiece.h"
#include "re2/prog.h"
@@ -16,11 +18,9 @@
namespace re2 {
-class Regexp;
-
// All the supported regexp engines.
enum Engine {
- kEngineBacktrack = 0, // Prog::BadSearchBacktrack
+ kEngineBacktrack = 0, // Prog::UnsafeSearchBacktrack
kEngineNFA, // Prog::SearchNFA
kEngineDFA, // Prog::SearchDFA, only ask whether it matched
kEngineDFA1, // Prog::SearchDFA, ask for match[0]
@@ -72,7 +72,7 @@ class TestInstance {
void LogMatch(const char* prefix, Engine e, const StringPiece& text,
const StringPiece& context, Prog::Anchor anchor);
- const StringPiece& regexp_str_; // regexp being tested
+ const StringPiece regexp_str_; // regexp being tested
Prog::MatchKind kind_; // kind of match
Regexp::ParseFlags flags_; // flags for parsing regexp_str_
bool error_; // error during constructor?
@@ -84,7 +84,8 @@ class TestInstance {
PCRE* re_; // PCRE implementation
RE2* re2_; // RE2 implementation
- DISALLOW_EVIL_CONSTRUCTORS(TestInstance);
+ TestInstance(const TestInstance&) = delete;
+ TestInstance& operator=(const TestInstance&) = delete;
};
// A group of TestInstances for all possible configurations.
@@ -108,9 +109,10 @@ class Tester {
private:
bool error_;
- vector<TestInstance*> v_;
+ std::vector<TestInstance*> v_;
- DISALLOW_EVIL_CONSTRUCTORS(Tester);
+ Tester(const Tester&) = delete;
+ Tester& operator=(const Tester&) = delete;
};
// Run all possible tests using regexp and text.
@@ -118,4 +120,4 @@ bool TestRegexpOnText(const StringPiece& regexp, const StringPiece& text);
} // namespace re2
-#endif // RE2_TESTING_TESTER_H__
+#endif // RE2_TESTING_TESTER_H_
diff --git a/re2/testing/unicode_test.py b/re2/testing/unicode_test.py
deleted file mode 100755
index a88a3ad..0000000
--- a/re2/testing/unicode_test.py
+++ /dev/null
@@ -1,207 +0,0 @@
-#!/usr/bin/python2.4
-#
-# Copyright 2008 The RE2 Authors. All Rights Reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-"""Unittest for the util/regexp/re2/unicode.py module."""
-
-import os
-import StringIO
-from google3.pyglib import flags
-from google3.testing.pybase import googletest
-from google3.util.regexp.re2 import unicode
-
-_UNICODE_DIR = os.path.join(flags.FLAGS.test_srcdir, "google3", "third_party",
- "unicode", "ucd-5.1.0")
-
-
-class ConvertTest(googletest.TestCase):
- """Test the conversion functions."""
-
- def testUInt(self):
- self.assertEquals(0x0000, unicode._UInt("0000"))
- self.assertEquals(0x263A, unicode._UInt("263A"))
- self.assertEquals(0x10FFFF, unicode._UInt("10FFFF"))
- self.assertRaises(unicode.InputError, unicode._UInt, "263")
- self.assertRaises(unicode.InputError, unicode._UInt, "263AAAA")
- self.assertRaises(unicode.InputError, unicode._UInt, "110000")
-
- def testURange(self):
- self.assertEquals([1, 2, 3], unicode._URange("0001..0003"))
- self.assertEquals([1], unicode._URange("0001"))
- self.assertRaises(unicode.InputError, unicode._URange, "0001..0003..0005")
- self.assertRaises(unicode.InputError, unicode._URange, "0003..0001")
- self.assertRaises(unicode.InputError, unicode._URange, "0001..0001")
-
- def testUStr(self):
- self.assertEquals("0x263A", unicode._UStr(0x263a))
- self.assertEquals("0x10FFFF", unicode._UStr(0x10FFFF))
- self.assertRaises(unicode.InputError, unicode._UStr, 0x110000)
- self.assertRaises(unicode.InputError, unicode._UStr, -1)
-
-
-_UNICODE_TABLE = """# Commented line, should be ignored.
-# The next line is blank and should be ignored.
-
-0041;Capital A;Line 1
-0061..007A;Lowercase;Line 2
-1F00;<Greek, First>;Ignored
-1FFE;<Greek, Last>;Line 3
-10FFFF;Runemax;Line 4
-0000;Zero;Line 5
-"""
-
-_BAD_TABLE1 = """
-111111;Not a code point;
-"""
-
-_BAD_TABLE2 = """
-0000;<Zero, First>;Missing <Zero, Last>
-"""
-
-_BAD_TABLE3 = """
-0010..0001;Bad range;
-"""
-
-
-class AbortError(Exception):
- """Function should not have been called."""
-
-
-def Abort():
- raise AbortError("Abort")
-
-
-def StringTable(s, n, f):
- unicode.ReadUnicodeTable(StringIO.StringIO(s), n, f)
-
-
-class ReadUnicodeTableTest(googletest.TestCase):
- """Test the ReadUnicodeTable function."""
-
- def testSimpleTable(self):
-
- ncall = [0] # can't assign to ordinary int in DoLine
-
- def DoLine(codes, fields):
- self.assertEquals(3, len(fields))
- ncall[0] += 1
- self.assertEquals("Line %d" % (ncall[0],), fields[2])
- if ncall[0] == 1:
- self.assertEquals([0x0041], codes)
- self.assertEquals("0041", fields[0])
- self.assertEquals("Capital A", fields[1])
- elif ncall[0] == 2:
- self.assertEquals(range(0x0061, 0x007A + 1), codes)
- self.assertEquals("0061..007A", fields[0])
- self.assertEquals("Lowercase", fields[1])
- elif ncall[0] == 3:
- self.assertEquals(range(0x1F00, 0x1FFE + 1), codes)
- self.assertEquals("1F00..1FFE", fields[0])
- self.assertEquals("Greek", fields[1])
- elif ncall[0] == 4:
- self.assertEquals([0x10FFFF], codes)
- self.assertEquals("10FFFF", fields[0])
- self.assertEquals("Runemax", fields[1])
- elif ncall[0] == 5:
- self.assertEquals([0x0000], codes)
- self.assertEquals("0000", fields[0])
- self.assertEquals("Zero", fields[1])
-
- StringTable(_UNICODE_TABLE, 3, DoLine)
- self.assertEquals(5, ncall[0])
-
- def testErrorTables(self):
- self.assertRaises(unicode.InputError, StringTable, _UNICODE_TABLE, 4, Abort)
- self.assertRaises(unicode.InputError, StringTable, _UNICODE_TABLE, 2, Abort)
- self.assertRaises(unicode.InputError, StringTable, _BAD_TABLE1, 3, Abort)
- self.assertRaises(unicode.InputError, StringTable, _BAD_TABLE2, 3, Abort)
- self.assertRaises(unicode.InputError, StringTable, _BAD_TABLE3, 3, Abort)
-
-
-class ParseContinueTest(googletest.TestCase):
- """Test the ParseContinue function."""
-
- def testParseContinue(self):
- self.assertEquals(("Private Use", "First"),
- unicode._ParseContinue("<Private Use, First>"))
- self.assertEquals(("Private Use", "Last"),
- unicode._ParseContinue("<Private Use, Last>"))
- self.assertEquals(("<Private Use, Blah>", None),
- unicode._ParseContinue("<Private Use, Blah>"))
-
-
-class CaseGroupsTest(googletest.TestCase):
- """Test the CaseGroups function (and the CaseFoldingReader)."""
-
- def FindGroup(self, c):
- if type(c) == str:
- c = ord(c)
- for g in self.groups:
- if c in g:
- return g
- return None
-
- def testCaseGroups(self):
- self.groups = unicode.CaseGroups(unicode_dir=_UNICODE_DIR)
- self.assertEquals([ord("A"), ord("a")], self.FindGroup("a"))
- self.assertEquals(None, self.FindGroup("0"))
-
-
-class ScriptsTest(googletest.TestCase):
- """Test the Scripts function (and the ScriptsReader)."""
-
- def FindScript(self, c):
- if type(c) == str:
- c = ord(c)
- for script, codes in self.scripts.items():
- for code in codes:
- if c == code:
- return script
- return None
-
- def testScripts(self):
- self.scripts = unicode.Scripts(unicode_dir=_UNICODE_DIR)
- self.assertEquals("Latin", self.FindScript("a"))
- self.assertEquals("Common", self.FindScript("0"))
- self.assertEquals(None, self.FindScript(0xFFFE))
-
-
-class CategoriesTest(googletest.TestCase):
- """Test the Categories function (and the UnicodeDataReader)."""
-
- def FindCategory(self, c):
- if type(c) == str:
- c = ord(c)
- short = None
- for category, codes in self.categories.items():
- for code in codes:
- if code == c:
- # prefer category Nd over N
- if len(category) > 1:
- return category
- if short == None:
- short = category
- return short
-
- def testCategories(self):
- self.categories = unicode.Categories(unicode_dir=_UNICODE_DIR)
- self.assertEquals("Ll", self.FindCategory("a"))
- self.assertEquals("Nd", self.FindCategory("0"))
- self.assertEquals("Lo", self.FindCategory(0xAD00)) # in First, Last range
- self.assertEquals(None, self.FindCategory(0xFFFE))
- self.assertEquals("Lo", self.FindCategory(0x8B5A))
- self.assertEquals("Lo", self.FindCategory(0x6C38))
- self.assertEquals("Lo", self.FindCategory(0x92D2))
- self.assertTrue(ord("a") in self.categories["L"])
- self.assertTrue(ord("0") in self.categories["N"])
- self.assertTrue(0x8B5A in self.categories["L"])
- self.assertTrue(0x6C38 in self.categories["L"])
- self.assertTrue(0x92D2 in self.categories["L"])
-
-def main():
- googletest.main()
-
-if __name__ == "__main__":
- main()
diff --git a/re2/tostring.cc b/re2/tostring.cc
index 555524f..278c310 100644
--- a/re2/tostring.cc
+++ b/re2/tostring.cc
@@ -5,7 +5,13 @@
// Format a regular expression structure as a string.
// Tested by parse_test.cc
+#include <string.h>
+#include <string>
+
#include "util/util.h"
+#include "util/logging.h"
+#include "util/strutil.h"
+#include "util/utf.h"
#include "re2/regexp.h"
#include "re2/walker-inl.h"
@@ -42,7 +48,8 @@ class ToStringWalker : public Regexp::Walker<int> {
private:
string* t_; // The string the walker appends to.
- DISALLOW_EVIL_CONSTRUCTORS(ToStringWalker);
+ ToStringWalker(const ToStringWalker&) = delete;
+ ToStringWalker& operator=(const ToStringWalker&) = delete;
};
string Regexp::ToString() {
@@ -94,6 +101,8 @@ int ToStringWalker::PreVisit(Regexp* re, int parent_arg, bool* stop) {
case kRegexpCapture:
t_->append("(");
+ if (re->cap() == 0)
+ LOG(DFATAL) << "kRegexpCapture cap() == 0";
if (re->name()) {
t_->append("?P<");
t_->append(*re->name());
@@ -120,13 +129,12 @@ int ToStringWalker::PreVisit(Regexp* re, int parent_arg, bool* stop) {
static void AppendLiteral(string *t, Rune r, bool foldcase) {
if (r != 0 && r < 0x80 && strchr("(){}[]*+?|.^$\\", r)) {
t->append(1, '\\');
- t->append(1, r);
+ t->append(1, static_cast<char>(r));
} else if (foldcase && 'a' <= r && r <= 'z') {
- if ('a' <= r && r <= 'z')
- r += 'A' - 'a';
+ r -= 'a' - 'A';
t->append(1, '[');
- t->append(1, r);
- t->append(1, r + 'a' - 'A');
+ t->append(1, static_cast<char>(r));
+ t->append(1, static_cast<char>(r) + 'a' - 'A');
t->append(1, ']');
} else {
AppendCCRange(t, r, r);
@@ -154,12 +162,14 @@ int ToStringWalker::PostVisit(Regexp* re, int parent_arg, int pre_arg,
break;
case kRegexpLiteral:
- AppendLiteral(t_, re->rune(), re->parse_flags() & Regexp::FoldCase);
+ AppendLiteral(t_, re->rune(),
+ (re->parse_flags() & Regexp::FoldCase) != 0);
break;
case kRegexpLiteralString:
for (int i = 0; i < re->nrunes(); i++)
- AppendLiteral(t_, re->runes()[i], re->parse_flags() & Regexp::FoldCase);
+ AppendLiteral(t_, re->runes()[i],
+ (re->parse_flags() & Regexp::FoldCase) != 0);
if (prec < PrecConcat)
t_->append(")");
break;
@@ -297,7 +307,7 @@ static void AppendCCChar(string* t, Rune r) {
if (0x20 <= r && r <= 0x7E) {
if (strchr("[]^-\\", r))
t->append("\\");
- t->append(1, r);
+ t->append(1, static_cast<char>(r));
return;
}
switch (r) {
diff --git a/re2/unicode.py b/re2/unicode.py
index 8d78312..deff4d8 100644
--- a/re2/unicode.py
+++ b/re2/unicode.py
@@ -9,7 +9,7 @@ import re
import urllib2
# Directory or URL where Unicode tables reside.
-_UNICODE_DIR = "http://www.unicode.org/Public/6.0.0/ucd"
+_UNICODE_DIR = "https://www.unicode.org/Public/11.0.0/ucd"
# Largest valid Unicode code value.
_RUNE_MAX = 0x10FFFF
@@ -148,7 +148,7 @@ def ReadUnicodeTable(filename, nfields, doline):
raise InputError("invalid number of fields %d" % (nfields,))
if type(filename) == str:
- if filename.startswith("http://"):
+ if filename.startswith("https://"):
fil = urllib2.urlopen(filename)
else:
fil = open(filename, "r")
diff --git a/re2/unicode_casefold.cc b/re2/unicode_casefold.cc
index 6d4e878..b4da09d 100644
--- a/re2/unicode_casefold.cc
+++ b/re2/unicode_casefold.cc
@@ -7,8 +7,8 @@
namespace re2 {
-// 1029 groups, 2079 pairs, 282 ranges
-CaseFold unicode_casefold[] = {
+// 1374 groups, 2778 pairs, 349 ranges
+const CaseFold unicode_casefold[] = {
{ 65, 90, 32 },
{ 97, 106, -32 },
{ 107, 107, 8383 },
@@ -105,12 +105,17 @@ CaseFold unicode_casefold[] = {
{ 598, 599, -205 },
{ 601, 601, -202 },
{ 603, 603, -203 },
+ { 604, 604, 42319 },
{ 608, 608, -205 },
+ { 609, 609, 42315 },
{ 611, 611, -207 },
{ 613, 613, 42280 },
+ { 614, 614, 42308 },
{ 616, 616, -209 },
{ 617, 617, -211 },
+ { 618, 618, 42308 },
{ 619, 619, 10743 },
+ { 620, 620, 42305 },
{ 623, 623, -211 },
{ 625, 625, 10749 },
{ 626, 626, -213 },
@@ -118,15 +123,19 @@ CaseFold unicode_casefold[] = {
{ 637, 637, 10727 },
{ 640, 640, -218 },
{ 643, 643, -218 },
+ { 647, 647, 42282 },
{ 648, 648, -218 },
{ 649, 649, -69 },
{ 650, 651, -217 },
{ 652, 652, -71 },
{ 658, 658, -219 },
+ { 669, 669, 42261 },
+ { 670, 670, 42258 },
{ 837, 837, 84 },
{ 880, 883, EvenOdd },
{ 886, 887, EvenOdd },
{ 891, 893, 130 },
+ { 895, 895, 116 },
{ 902, 902, 38 },
{ 904, 906, 37 },
{ 908, 908, 64 },
@@ -167,6 +176,7 @@ CaseFold unicode_casefold[] = {
{ 1008, 1008, -86 },
{ 1009, 1009, -80 },
{ 1010, 1010, 7 },
+ { 1011, 1011, -116 },
{ 1012, 1012, -92 },
{ 1013, 1013, -96 },
{ 1015, 1016, OddEven },
@@ -175,17 +185,47 @@ CaseFold unicode_casefold[] = {
{ 1021, 1023, -130 },
{ 1024, 1039, 80 },
{ 1040, 1071, 32 },
- { 1072, 1103, -32 },
+ { 1072, 1073, -32 },
+ { 1074, 1074, 6222 },
+ { 1075, 1075, -32 },
+ { 1076, 1076, 6221 },
+ { 1077, 1085, -32 },
+ { 1086, 1086, 6212 },
+ { 1087, 1088, -32 },
+ { 1089, 1090, 6210 },
+ { 1091, 1097, -32 },
+ { 1098, 1098, 6204 },
+ { 1099, 1103, -32 },
{ 1104, 1119, -80 },
- { 1120, 1153, EvenOdd },
+ { 1120, 1122, EvenOdd },
+ { 1123, 1123, 6180 },
+ { 1124, 1153, EvenOdd },
{ 1162, 1215, EvenOdd },
{ 1216, 1216, 15 },
{ 1217, 1230, OddEven },
{ 1231, 1231, -15 },
- { 1232, 1319, EvenOdd },
+ { 1232, 1327, EvenOdd },
{ 1329, 1366, 48 },
{ 1377, 1414, -48 },
{ 4256, 4293, 7264 },
+ { 4295, 4295, 7264 },
+ { 4301, 4301, 7264 },
+ { 4304, 4346, 3008 },
+ { 4349, 4351, 3008 },
+ { 5024, 5103, 38864 },
+ { 5104, 5109, 8 },
+ { 5112, 5117, -8 },
+ { 7296, 7296, -6254 },
+ { 7297, 7297, -6253 },
+ { 7298, 7298, -6244 },
+ { 7299, 7299, -6242 },
+ { 7300, 7300, EvenOdd },
+ { 7301, 7301, -6243 },
+ { 7302, 7302, -6236 },
+ { 7303, 7303, -6181 },
+ { 7304, 7304, 35266 },
+ { 7312, 7354, -3008 },
+ { 7357, 7359, -3008 },
{ 7545, 7545, 35332 },
{ 7549, 7549, 3814 },
{ 7680, 7776, EvenOdd },
@@ -275,9 +315,14 @@ CaseFold unicode_casefold[] = {
{ 11390, 11391, -10815 },
{ 11392, 11491, EvenOdd },
{ 11499, 11502, OddEven },
+ { 11506, 11507, EvenOdd },
{ 11520, 11557, -7264 },
- { 42560, 42605, EvenOdd },
- { 42624, 42647, EvenOdd },
+ { 11559, 11559, -7264 },
+ { 11565, 11565, -7264 },
+ { 42560, 42570, EvenOdd },
+ { 42571, 42571, -35267 },
+ { 42572, 42605, EvenOdd },
+ { 42624, 42651, EvenOdd },
{ 42786, 42799, EvenOdd },
{ 42802, 42863, EvenOdd },
{ 42873, 42876, OddEven },
@@ -285,17 +330,39 @@ CaseFold unicode_casefold[] = {
{ 42878, 42887, EvenOdd },
{ 42891, 42892, OddEven },
{ 42893, 42893, -42280 },
- { 42896, 42897, EvenOdd },
- { 42912, 42921, EvenOdd },
+ { 42896, 42899, EvenOdd },
+ { 42902, 42921, EvenOdd },
+ { 42922, 42922, -42308 },
+ { 42923, 42923, -42319 },
+ { 42924, 42924, -42315 },
+ { 42925, 42925, -42305 },
+ { 42926, 42926, -42308 },
+ { 42928, 42928, -42258 },
+ { 42929, 42929, -42282 },
+ { 42930, 42930, -42261 },
+ { 42931, 42931, 928 },
+ { 42932, 42937, EvenOdd },
+ { 43859, 43859, -928 },
+ { 43888, 43967, -38864 },
{ 65313, 65338, 32 },
{ 65345, 65370, -32 },
{ 66560, 66599, 40 },
{ 66600, 66639, -40 },
+ { 66736, 66771, 40 },
+ { 66776, 66811, -40 },
+ { 68736, 68786, 64 },
+ { 68800, 68850, -64 },
+ { 71840, 71871, 32 },
+ { 71872, 71903, -32 },
+ { 93760, 93791, 32 },
+ { 93792, 93823, -32 },
+ { 125184, 125217, 34 },
+ { 125218, 125251, -34 },
};
-int num_unicode_casefold = 282;
+const int num_unicode_casefold = 349;
-// 1029 groups, 1050 pairs, 163 ranges
-CaseFold unicode_tolower[] = {
+// 1374 groups, 1404 pairs, 194 ranges
+const CaseFold unicode_tolower[] = {
{ 65, 90, 32 },
{ 181, 181, 775 },
{ 192, 214, 32 },
@@ -363,6 +430,7 @@ CaseFold unicode_tolower[] = {
{ 837, 837, 116 },
{ 880, 882, EvenOddSkip },
{ 886, 886, EvenOdd },
+ { 895, 895, 116 },
{ 902, 902, 38 },
{ 904, 906, 37 },
{ 908, 908, 64 },
@@ -390,9 +458,22 @@ CaseFold unicode_tolower[] = {
{ 1162, 1214, EvenOddSkip },
{ 1216, 1216, 15 },
{ 1217, 1229, OddEvenSkip },
- { 1232, 1318, EvenOddSkip },
+ { 1232, 1326, EvenOddSkip },
{ 1329, 1366, 48 },
{ 4256, 4293, 7264 },
+ { 4295, 4295, 7264 },
+ { 4301, 4301, 7264 },
+ { 5112, 5117, -8 },
+ { 7296, 7296, -6222 },
+ { 7297, 7297, -6221 },
+ { 7298, 7298, -6212 },
+ { 7299, 7300, -6210 },
+ { 7301, 7301, -6211 },
+ { 7302, 7302, -6204 },
+ { 7303, 7303, -6180 },
+ { 7304, 7304, 35267 },
+ { 7312, 7354, -3008 },
+ { 7357, 7359, -3008 },
{ 7680, 7828, EvenOddSkip },
{ 7835, 7835, -58 },
{ 7838, 7838, -7615 },
@@ -446,8 +527,9 @@ CaseFold unicode_tolower[] = {
{ 11390, 11391, -10815 },
{ 11392, 11490, EvenOddSkip },
{ 11499, 11501, OddEvenSkip },
+ { 11506, 11506, EvenOdd },
{ 42560, 42604, EvenOddSkip },
- { 42624, 42646, EvenOddSkip },
+ { 42624, 42650, EvenOddSkip },
{ 42786, 42798, EvenOddSkip },
{ 42802, 42862, EvenOddSkip },
{ 42873, 42875, OddEvenSkip },
@@ -455,12 +537,28 @@ CaseFold unicode_tolower[] = {
{ 42878, 42886, EvenOddSkip },
{ 42891, 42891, OddEven },
{ 42893, 42893, -42280 },
- { 42896, 42896, EvenOdd },
- { 42912, 42920, EvenOddSkip },
+ { 42896, 42898, EvenOddSkip },
+ { 42902, 42920, EvenOddSkip },
+ { 42922, 42922, -42308 },
+ { 42923, 42923, -42319 },
+ { 42924, 42924, -42315 },
+ { 42925, 42925, -42305 },
+ { 42926, 42926, -42308 },
+ { 42928, 42928, -42258 },
+ { 42929, 42929, -42282 },
+ { 42930, 42930, -42261 },
+ { 42931, 42931, 928 },
+ { 42932, 42936, EvenOddSkip },
+ { 43888, 43967, -38864 },
{ 65313, 65338, 32 },
{ 66560, 66599, 40 },
+ { 66736, 66771, 40 },
+ { 68736, 68786, 64 },
+ { 71840, 71871, 32 },
+ { 93760, 93791, 32 },
+ { 125184, 125217, 34 },
};
-int num_unicode_tolower = 163;
+const int num_unicode_tolower = 194;
diff --git a/re2/unicode_casefold.h b/re2/unicode_casefold.h
index 160b07e..8bdbb42 100644
--- a/re2/unicode_casefold.h
+++ b/re2/unicode_casefold.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_UNICODE_CASEFOLD_H_
+#define RE2_UNICODE_CASEFOLD_H_
+
// Unicode case folding tables.
// The Unicode case folding tables encode the mapping from one Unicode point
@@ -16,7 +19,7 @@
// 'K' -> 'K'
//
// Like everything Unicode, these tables are big. If we represent the table
-// as a sorted list of uint32 pairs, it has 2049 entries and is 16 kB.
+// as a sorted list of uint32_t pairs, it has 2049 entries and is 16 kB.
// Most table entries look like the ones around them:
// 'A' maps to 'A'+32, 'B' maps to 'B'+32, etc.
// Instead of listing all the pairs explicitly, we make a list of ranges
@@ -36,10 +39,10 @@
// The grouped form also allows for efficient fold range calculations
// rather than looping one character at a time.
-#ifndef RE2_UNICODE_CASEFOLD_H__
-#define RE2_UNICODE_CASEFOLD_H__
+#include <stdint.h>
#include "util/util.h"
+#include "util/utf.h"
namespace re2 {
@@ -51,25 +54,25 @@ enum {
};
struct CaseFold {
- uint32 lo;
- uint32 hi;
- int32 delta;
+ Rune lo;
+ Rune hi;
+ int32_t delta;
};
-extern CaseFold unicode_casefold[];
-extern int num_unicode_casefold;
+extern const CaseFold unicode_casefold[];
+extern const int num_unicode_casefold;
-extern CaseFold unicode_tolower[];
-extern int num_unicode_tolower;
+extern const CaseFold unicode_tolower[];
+extern const int num_unicode_tolower;
// Returns the CaseFold* in the tables that contains rune.
// If rune is not in the tables, returns the first CaseFold* after rune.
// If rune is larger than any value in the tables, returns NULL.
-extern CaseFold* LookupCaseFold(CaseFold*, int, Rune rune);
+extern const CaseFold* LookupCaseFold(const CaseFold*, int, Rune rune);
// Returns the result of applying the fold f to the rune r.
-extern Rune ApplyFold(CaseFold *f, Rune r);
+extern Rune ApplyFold(const CaseFold *f, Rune r);
} // namespace re2
-#endif // RE2_UNICODE_CASEFOLD_H__
+#endif // RE2_UNICODE_CASEFOLD_H_
diff --git a/re2/unicode_groups.cc b/re2/unicode_groups.cc
index b57a327..8052827 100644
--- a/re2/unicode_groups.cc
+++ b/re2/unicode_groups.cc
@@ -7,7 +7,7 @@
namespace re2 {
-static URange16 Ps_range16[] = {
+static const URange16 Ps_range16[] = {
{ 40, 40 },
{ 91, 91 },
{ 123, 123 },
@@ -19,6 +19,8 @@ static URange16 Ps_range16[] = {
{ 8261, 8261 },
{ 8317, 8317 },
{ 8333, 8333 },
+ { 8968, 8968 },
+ { 8970, 8970 },
{ 9001, 9001 },
{ 10088, 10088 },
{ 10090, 10090 },
@@ -51,6 +53,7 @@ static URange16 Ps_range16[] = {
{ 11812, 11812 },
{ 11814, 11814 },
{ 11816, 11816 },
+ { 11842, 11842 },
{ 12296, 12296 },
{ 12298, 12298 },
{ 12300, 12300 },
@@ -61,7 +64,7 @@ static URange16 Ps_range16[] = {
{ 12312, 12312 },
{ 12314, 12314 },
{ 12317, 12317 },
- { 64830, 64830 },
+ { 64831, 64831 },
{ 65047, 65047 },
{ 65077, 65077 },
{ 65079, 65079 },
@@ -81,7 +84,7 @@ static URange16 Ps_range16[] = {
{ 65375, 65375 },
{ 65378, 65378 },
};
-static URange16 Nl_range16[] = {
+static const URange16 Nl_range16[] = {
{ 5870, 5872 },
{ 8544, 8578 },
{ 8581, 8584 },
@@ -90,14 +93,14 @@ static URange16 Nl_range16[] = {
{ 12344, 12346 },
{ 42726, 42735 },
};
-static URange32 Nl_range32[] = {
+static const URange32 Nl_range32[] = {
{ 65856, 65908 },
{ 66369, 66369 },
{ 66378, 66378 },
{ 66513, 66517 },
- { 74752, 74850 },
+ { 74752, 74862 },
};
-static URange16 No_range16[] = {
+static const URange16 No_range16[] = {
{ 178, 179 },
{ 185, 185 },
{ 188, 190 },
@@ -105,7 +108,8 @@ static URange16 No_range16[] = {
{ 2930, 2935 },
{ 3056, 3058 },
{ 3192, 3198 },
- { 3440, 3445 },
+ { 3416, 3422 },
+ { 3440, 3448 },
{ 3882, 3891 },
{ 4969, 4988 },
{ 6128, 6137 },
@@ -121,33 +125,60 @@ static URange16 No_range16[] = {
{ 11517, 11517 },
{ 12690, 12693 },
{ 12832, 12841 },
+ { 12872, 12879 },
{ 12881, 12895 },
{ 12928, 12937 },
{ 12977, 12991 },
{ 43056, 43061 },
};
-static URange32 No_range32[] = {
+static const URange32 No_range32[] = {
{ 65799, 65843 },
{ 65909, 65912 },
- { 65930, 65930 },
+ { 65930, 65931 },
+ { 66273, 66299 },
{ 66336, 66339 },
{ 67672, 67679 },
+ { 67705, 67711 },
+ { 67751, 67759 },
+ { 67835, 67839 },
{ 67862, 67867 },
- { 68160, 68167 },
+ { 68028, 68029 },
+ { 68032, 68047 },
+ { 68050, 68095 },
+ { 68160, 68168 },
{ 68221, 68222 },
+ { 68253, 68255 },
+ { 68331, 68335 },
{ 68440, 68447 },
{ 68472, 68479 },
+ { 68521, 68527 },
+ { 68858, 68863 },
{ 69216, 69246 },
+ { 69405, 69414 },
+ { 69457, 69460 },
{ 69714, 69733 },
- { 119648, 119665 },
- { 127232, 127242 },
-};
-static URange16 Lo_range16[] = {
+ { 70113, 70132 },
+ { 71482, 71483 },
+ { 71914, 71922 },
+ { 72794, 72812 },
+ { 93019, 93025 },
+ { 93824, 93846 },
+ { 119520, 119539 },
+ { 119648, 119672 },
+ { 125127, 125135 },
+ { 126065, 126123 },
+ { 126125, 126127 },
+ { 126129, 126132 },
+ { 127232, 127244 },
+};
+static const URange16 Lo_range16[] = {
+ { 170, 170 },
+ { 186, 186 },
{ 443, 443 },
{ 448, 451 },
{ 660, 660 },
{ 1488, 1514 },
- { 1520, 1522 },
+ { 1519, 1522 },
{ 1568, 1599 },
{ 1601, 1610 },
{ 1646, 1647 },
@@ -163,12 +194,14 @@ static URange16 Lo_range16[] = {
{ 1994, 2026 },
{ 2048, 2069 },
{ 2112, 2136 },
+ { 2144, 2154 },
+ { 2208, 2228 },
+ { 2230, 2237 },
{ 2308, 2361 },
{ 2365, 2365 },
{ 2384, 2384 },
{ 2392, 2401 },
- { 2418, 2423 },
- { 2425, 2431 },
+ { 2418, 2432 },
{ 2437, 2444 },
{ 2447, 2448 },
{ 2451, 2472 },
@@ -180,6 +213,7 @@ static URange16 Lo_range16[] = {
{ 2524, 2525 },
{ 2527, 2529 },
{ 2544, 2545 },
+ { 2556, 2556 },
{ 2565, 2570 },
{ 2575, 2576 },
{ 2579, 2600 },
@@ -199,6 +233,7 @@ static URange16 Lo_range16[] = {
{ 2749, 2749 },
{ 2768, 2768 },
{ 2784, 2785 },
+ { 2809, 2809 },
{ 2821, 2828 },
{ 2831, 2832 },
{ 2835, 2856 },
@@ -223,11 +258,11 @@ static URange16 Lo_range16[] = {
{ 3077, 3084 },
{ 3086, 3088 },
{ 3090, 3112 },
- { 3114, 3123 },
- { 3125, 3129 },
+ { 3114, 3129 },
{ 3133, 3133 },
- { 3160, 3161 },
+ { 3160, 3162 },
{ 3168, 3169 },
+ { 3200, 3200 },
{ 3205, 3212 },
{ 3214, 3216 },
{ 3218, 3240 },
@@ -242,7 +277,8 @@ static URange16 Lo_range16[] = {
{ 3346, 3386 },
{ 3389, 3389 },
{ 3406, 3406 },
- { 3424, 3425 },
+ { 3412, 3414 },
+ { 3423, 3425 },
{ 3450, 3455 },
{ 3461, 3478 },
{ 3482, 3505 },
@@ -267,7 +303,7 @@ static URange16 Lo_range16[] = {
{ 3762, 3763 },
{ 3773, 3773 },
{ 3776, 3780 },
- { 3804, 3805 },
+ { 3804, 3807 },
{ 3840, 3840 },
{ 3904, 3911 },
{ 3913, 3948 },
@@ -281,7 +317,6 @@ static URange16 Lo_range16[] = {
{ 4206, 4208 },
{ 4213, 4225 },
{ 4238, 4238 },
- { 4304, 4346 },
{ 4352, 4680 },
{ 4682, 4685 },
{ 4688, 4694 },
@@ -299,11 +334,11 @@ static URange16 Lo_range16[] = {
{ 4882, 4885 },
{ 4888, 4954 },
{ 4992, 5007 },
- { 5024, 5108 },
{ 5121, 5740 },
{ 5743, 5759 },
{ 5761, 5786 },
{ 5792, 5866 },
+ { 5873, 5880 },
{ 5888, 5900 },
{ 5902, 5905 },
{ 5920, 5937 },
@@ -313,29 +348,31 @@ static URange16 Lo_range16[] = {
{ 6016, 6067 },
{ 6108, 6108 },
{ 6176, 6210 },
- { 6212, 6263 },
- { 6272, 6312 },
+ { 6212, 6264 },
+ { 6272, 6276 },
+ { 6279, 6312 },
{ 6314, 6314 },
{ 6320, 6389 },
- { 6400, 6428 },
+ { 6400, 6430 },
{ 6480, 6509 },
{ 6512, 6516 },
{ 6528, 6571 },
- { 6593, 6599 },
+ { 6576, 6601 },
{ 6656, 6678 },
{ 6688, 6740 },
{ 6917, 6963 },
{ 6981, 6987 },
{ 7043, 7072 },
{ 7086, 7087 },
- { 7104, 7141 },
+ { 7098, 7141 },
{ 7168, 7203 },
{ 7245, 7247 },
{ 7258, 7287 },
{ 7401, 7404 },
{ 7406, 7409 },
+ { 7413, 7414 },
{ 8501, 8504 },
- { 11568, 11621 },
+ { 11568, 11623 },
{ 11648, 11670 },
{ 11680, 11686 },
{ 11688, 11694 },
@@ -351,12 +388,12 @@ static URange16 Lo_range16[] = {
{ 12447, 12447 },
{ 12449, 12538 },
{ 12543, 12543 },
- { 12549, 12589 },
+ { 12549, 12591 },
{ 12593, 12686 },
{ 12704, 12730 },
{ 12784, 12799 },
{ 13312, 19893 },
- { 19968, 40907 },
+ { 19968, 40943 },
{ 40960, 40980 },
{ 40982, 42124 },
{ 42192, 42231 },
@@ -365,6 +402,8 @@ static URange16 Lo_range16[] = {
{ 42538, 42539 },
{ 42606, 42606 },
{ 42656, 42725 },
+ { 42895, 42895 },
+ { 42999, 42999 },
{ 43003, 43009 },
{ 43011, 43013 },
{ 43015, 43018 },
@@ -373,23 +412,29 @@ static URange16 Lo_range16[] = {
{ 43138, 43187 },
{ 43250, 43255 },
{ 43259, 43259 },
+ { 43261, 43262 },
{ 43274, 43301 },
{ 43312, 43334 },
{ 43360, 43388 },
{ 43396, 43442 },
+ { 43488, 43492 },
+ { 43495, 43503 },
+ { 43514, 43518 },
{ 43520, 43560 },
{ 43584, 43586 },
{ 43588, 43595 },
{ 43616, 43631 },
{ 43633, 43638 },
{ 43642, 43642 },
- { 43648, 43695 },
+ { 43646, 43695 },
{ 43697, 43697 },
{ 43701, 43702 },
{ 43705, 43709 },
{ 43712, 43712 },
{ 43714, 43714 },
{ 43739, 43740 },
+ { 43744, 43754 },
+ { 43762, 43762 },
{ 43777, 43782 },
{ 43785, 43790 },
{ 43793, 43798 },
@@ -399,8 +444,7 @@ static URange16 Lo_range16[] = {
{ 44032, 55203 },
{ 55216, 55238 },
{ 55243, 55291 },
- { 63744, 64045 },
- { 64048, 64109 },
+ { 63744, 64109 },
{ 64112, 64217 },
{ 64285, 64285 },
{ 64287, 64296 },
@@ -424,7 +468,7 @@ static URange16 Lo_range16[] = {
{ 65490, 65495 },
{ 65498, 65500 },
};
-static URange32 Lo_range32[] = {
+static const URange32 Lo_range32[] = {
{ 65536, 65547 },
{ 65549, 65574 },
{ 65576, 65594 },
@@ -434,46 +478,176 @@ static URange32 Lo_range32[] = {
{ 65664, 65786 },
{ 66176, 66204 },
{ 66208, 66256 },
- { 66304, 66334 },
- { 66352, 66368 },
+ { 66304, 66335 },
+ { 66349, 66368 },
{ 66370, 66377 },
+ { 66384, 66421 },
{ 66432, 66461 },
{ 66464, 66499 },
{ 66504, 66511 },
{ 66640, 66717 },
+ { 66816, 66855 },
+ { 66864, 66915 },
+ { 67072, 67382 },
+ { 67392, 67413 },
+ { 67424, 67431 },
{ 67584, 67589 },
{ 67592, 67592 },
{ 67594, 67637 },
{ 67639, 67640 },
{ 67644, 67644 },
{ 67647, 67669 },
+ { 67680, 67702 },
+ { 67712, 67742 },
+ { 67808, 67826 },
+ { 67828, 67829 },
{ 67840, 67861 },
{ 67872, 67897 },
+ { 67968, 68023 },
+ { 68030, 68031 },
{ 68096, 68096 },
{ 68112, 68115 },
{ 68117, 68119 },
- { 68121, 68147 },
+ { 68121, 68149 },
{ 68192, 68220 },
+ { 68224, 68252 },
+ { 68288, 68295 },
+ { 68297, 68324 },
{ 68352, 68405 },
{ 68416, 68437 },
{ 68448, 68466 },
+ { 68480, 68497 },
{ 68608, 68680 },
+ { 68864, 68899 },
+ { 69376, 69404 },
+ { 69415, 69415 },
+ { 69424, 69445 },
{ 69635, 69687 },
{ 69763, 69807 },
- { 73728, 74606 },
+ { 69840, 69864 },
+ { 69891, 69926 },
+ { 69956, 69956 },
+ { 69968, 70002 },
+ { 70006, 70006 },
+ { 70019, 70066 },
+ { 70081, 70084 },
+ { 70106, 70106 },
+ { 70108, 70108 },
+ { 70144, 70161 },
+ { 70163, 70187 },
+ { 70272, 70278 },
+ { 70280, 70280 },
+ { 70282, 70285 },
+ { 70287, 70301 },
+ { 70303, 70312 },
+ { 70320, 70366 },
+ { 70405, 70412 },
+ { 70415, 70416 },
+ { 70419, 70440 },
+ { 70442, 70448 },
+ { 70450, 70451 },
+ { 70453, 70457 },
+ { 70461, 70461 },
+ { 70480, 70480 },
+ { 70493, 70497 },
+ { 70656, 70708 },
+ { 70727, 70730 },
+ { 70784, 70831 },
+ { 70852, 70853 },
+ { 70855, 70855 },
+ { 71040, 71086 },
+ { 71128, 71131 },
+ { 71168, 71215 },
+ { 71236, 71236 },
+ { 71296, 71338 },
+ { 71424, 71450 },
+ { 71680, 71723 },
+ { 71935, 71935 },
+ { 72192, 72192 },
+ { 72203, 72242 },
+ { 72250, 72250 },
+ { 72272, 72272 },
+ { 72284, 72323 },
+ { 72326, 72329 },
+ { 72349, 72349 },
+ { 72384, 72440 },
+ { 72704, 72712 },
+ { 72714, 72750 },
+ { 72768, 72768 },
+ { 72818, 72847 },
+ { 72960, 72966 },
+ { 72968, 72969 },
+ { 72971, 73008 },
+ { 73030, 73030 },
+ { 73056, 73061 },
+ { 73063, 73064 },
+ { 73066, 73097 },
+ { 73112, 73112 },
+ { 73440, 73458 },
+ { 73728, 74649 },
+ { 74880, 75075 },
{ 77824, 78894 },
+ { 82944, 83526 },
{ 92160, 92728 },
- { 110592, 110593 },
+ { 92736, 92766 },
+ { 92880, 92909 },
+ { 92928, 92975 },
+ { 93027, 93047 },
+ { 93053, 93071 },
+ { 93952, 94020 },
+ { 94032, 94032 },
+ { 94208, 100337 },
+ { 100352, 101106 },
+ { 110592, 110878 },
+ { 110960, 111355 },
+ { 113664, 113770 },
+ { 113776, 113788 },
+ { 113792, 113800 },
+ { 113808, 113817 },
+ { 124928, 125124 },
+ { 126464, 126467 },
+ { 126469, 126495 },
+ { 126497, 126498 },
+ { 126500, 126500 },
+ { 126503, 126503 },
+ { 126505, 126514 },
+ { 126516, 126519 },
+ { 126521, 126521 },
+ { 126523, 126523 },
+ { 126530, 126530 },
+ { 126535, 126535 },
+ { 126537, 126537 },
+ { 126539, 126539 },
+ { 126541, 126543 },
+ { 126545, 126546 },
+ { 126548, 126548 },
+ { 126551, 126551 },
+ { 126553, 126553 },
+ { 126555, 126555 },
+ { 126557, 126557 },
+ { 126559, 126559 },
+ { 126561, 126562 },
+ { 126564, 126564 },
+ { 126567, 126570 },
+ { 126572, 126578 },
+ { 126580, 126583 },
+ { 126585, 126588 },
+ { 126590, 126590 },
+ { 126592, 126601 },
+ { 126603, 126619 },
+ { 126625, 126627 },
+ { 126629, 126633 },
+ { 126635, 126651 },
{ 131072, 173782 },
{ 173824, 177972 },
{ 177984, 178205 },
+ { 178208, 183969 },
+ { 183984, 191456 },
{ 194560, 195101 },
};
-static URange16 Ll_range16[] = {
+static const URange16 Ll_range16[] = {
{ 97, 122 },
- { 170, 170 },
{ 181, 181 },
- { 186, 186 },
{ 223, 246 },
{ 248, 255 },
{ 257, 257 },
@@ -737,9 +911,17 @@ static URange16 Ll_range16[] = {
{ 1315, 1315 },
{ 1317, 1317 },
{ 1319, 1319 },
- { 1377, 1415 },
+ { 1321, 1321 },
+ { 1323, 1323 },
+ { 1325, 1325 },
+ { 1327, 1327 },
+ { 1376, 1416 },
+ { 4304, 4346 },
+ { 4349, 4351 },
+ { 5112, 5117 },
+ { 7296, 7304 },
{ 7424, 7467 },
- { 7522, 7543 },
+ { 7531, 7543 },
{ 7545, 7578 },
{ 7681, 7681 },
{ 7683, 7683 },
@@ -903,7 +1085,7 @@ static URange16 Ll_range16[] = {
{ 11372, 11372 },
{ 11377, 11377 },
{ 11379, 11380 },
- { 11382, 11388 },
+ { 11382, 11387 },
{ 11393, 11393 },
{ 11395, 11395 },
{ 11397, 11397 },
@@ -956,7 +1138,10 @@ static URange16 Ll_range16[] = {
{ 11491, 11492 },
{ 11500, 11500 },
{ 11502, 11502 },
+ { 11507, 11507 },
{ 11520, 11557 },
+ { 11559, 11559 },
+ { 11565, 11565 },
{ 42561, 42561 },
{ 42563, 42563 },
{ 42565, 42565 },
@@ -992,6 +1177,8 @@ static URange16 Ll_range16[] = {
{ 42643, 42643 },
{ 42645, 42645 },
{ 42647, 42647 },
+ { 42649, 42649 },
+ { 42651, 42651 },
{ 42787, 42787 },
{ 42789, 42789 },
{ 42791, 42791 },
@@ -1041,18 +1228,35 @@ static URange16 Ll_range16[] = {
{ 42892, 42892 },
{ 42894, 42894 },
{ 42897, 42897 },
+ { 42899, 42901 },
+ { 42903, 42903 },
+ { 42905, 42905 },
+ { 42907, 42907 },
+ { 42909, 42909 },
+ { 42911, 42911 },
{ 42913, 42913 },
{ 42915, 42915 },
{ 42917, 42917 },
{ 42919, 42919 },
{ 42921, 42921 },
+ { 42927, 42927 },
+ { 42933, 42933 },
+ { 42935, 42935 },
+ { 42937, 42937 },
{ 43002, 43002 },
+ { 43824, 43866 },
+ { 43872, 43877 },
+ { 43888, 43967 },
{ 64256, 64262 },
{ 64275, 64279 },
{ 65345, 65370 },
};
-static URange32 Ll_range32[] = {
+static const URange32 Ll_range32[] = {
{ 66600, 66639 },
+ { 66776, 66811 },
+ { 68800, 68850 },
+ { 71872, 71903 },
+ { 93792, 93823 },
{ 119834, 119859 },
{ 119886, 119892 },
{ 119894, 119911 },
@@ -1081,8 +1285,9 @@ static URange32 Ll_range32[] = {
{ 120746, 120770 },
{ 120772, 120777 },
{ 120779, 120779 },
+ { 125218, 125251 },
};
-static URange16 Lm_range16[] = {
+static const URange16 Lm_range16[] = {
{ 688, 705 },
{ 710, 721 },
{ 736, 740 },
@@ -1106,13 +1311,13 @@ static URange16 Lm_range16[] = {
{ 6211, 6211 },
{ 6823, 6823 },
{ 7288, 7293 },
- { 7468, 7521 },
+ { 7468, 7530 },
{ 7544, 7544 },
{ 7579, 7615 },
{ 8305, 8305 },
{ 8319, 8319 },
{ 8336, 8348 },
- { 11389, 11389 },
+ { 11388, 11389 },
{ 11631, 11631 },
{ 11823, 11823 },
{ 12293, 12293 },
@@ -1124,16 +1329,26 @@ static URange16 Lm_range16[] = {
{ 42232, 42237 },
{ 42508, 42508 },
{ 42623, 42623 },
+ { 42652, 42653 },
{ 42775, 42783 },
{ 42864, 42864 },
{ 42888, 42888 },
+ { 43000, 43001 },
{ 43471, 43471 },
+ { 43494, 43494 },
{ 43632, 43632 },
{ 43741, 43741 },
+ { 43763, 43764 },
+ { 43868, 43871 },
{ 65392, 65392 },
{ 65438, 65439 },
};
-static URange16 Nd_range16[] = {
+static const URange32 Lm_range32[] = {
+ { 92992, 92995 },
+ { 94099, 94111 },
+ { 94176, 94177 },
+};
+static const URange16 Nd_range16[] = {
{ 48, 57 },
{ 1632, 1641 },
{ 1776, 1785 },
@@ -1147,6 +1362,7 @@ static URange16 Nd_range16[] = {
{ 3174, 3183 },
{ 3302, 3311 },
{ 3430, 3439 },
+ { 3558, 3567 },
{ 3664, 3673 },
{ 3792, 3801 },
{ 3872, 3881 },
@@ -1166,16 +1382,34 @@ static URange16 Nd_range16[] = {
{ 43216, 43225 },
{ 43264, 43273 },
{ 43472, 43481 },
+ { 43504, 43513 },
{ 43600, 43609 },
{ 44016, 44025 },
{ 65296, 65305 },
};
-static URange32 Nd_range32[] = {
+static const URange32 Nd_range32[] = {
{ 66720, 66729 },
+ { 68912, 68921 },
{ 69734, 69743 },
+ { 69872, 69881 },
+ { 69942, 69951 },
+ { 70096, 70105 },
+ { 70384, 70393 },
+ { 70736, 70745 },
+ { 70864, 70873 },
+ { 71248, 71257 },
+ { 71360, 71369 },
+ { 71472, 71481 },
+ { 71904, 71913 },
+ { 72784, 72793 },
+ { 73040, 73049 },
+ { 73120, 73129 },
+ { 92768, 92777 },
+ { 93008, 93017 },
{ 120782, 120831 },
+ { 125264, 125273 },
};
-static URange16 Pc_range16[] = {
+static const URange16 Pc_range16[] = {
{ 95, 95 },
{ 8255, 8256 },
{ 8276, 8276 },
@@ -1183,7 +1417,7 @@ static URange16 Pc_range16[] = {
{ 65101, 65103 },
{ 65343, 65343 },
};
-static URange16 Lt_range16[] = {
+static const URange16 Lt_range16[] = {
{ 453, 453 },
{ 456, 456 },
{ 459, 459 },
@@ -1195,7 +1429,7 @@ static URange16 Lt_range16[] = {
{ 8140, 8140 },
{ 8188, 8188 },
};
-static URange16 Lu_range16[] = {
+static const URange16 Lu_range16[] = {
{ 65, 90 },
{ 192, 214 },
{ 216, 222 },
@@ -1343,6 +1577,7 @@ static URange16 Lu_range16[] = {
{ 880, 880 },
{ 882, 882 },
{ 886, 886 },
+ { 895, 895 },
{ 902, 902 },
{ 904, 906 },
{ 908, 908 },
@@ -1462,8 +1697,17 @@ static URange16 Lu_range16[] = {
{ 1314, 1314 },
{ 1316, 1316 },
{ 1318, 1318 },
+ { 1320, 1320 },
+ { 1322, 1322 },
+ { 1324, 1324 },
+ { 1326, 1326 },
{ 1329, 1366 },
{ 4256, 4293 },
+ { 4295, 4295 },
+ { 4301, 4301 },
+ { 5024, 5109 },
+ { 7312, 7354 },
+ { 7357, 7359 },
{ 7680, 7680 },
{ 7682, 7682 },
{ 7684, 7684 },
@@ -1678,6 +1922,7 @@ static URange16 Lu_range16[] = {
{ 11490, 11490 },
{ 11499, 11499 },
{ 11501, 11501 },
+ { 11506, 11506 },
{ 42560, 42560 },
{ 42562, 42562 },
{ 42564, 42564 },
@@ -1713,6 +1958,8 @@ static URange16 Lu_range16[] = {
{ 42642, 42642 },
{ 42644, 42644 },
{ 42646, 42646 },
+ { 42648, 42648 },
+ { 42650, 42650 },
{ 42786, 42786 },
{ 42788, 42788 },
{ 42790, 42790 },
@@ -1761,15 +2008,29 @@ static URange16 Lu_range16[] = {
{ 42891, 42891 },
{ 42893, 42893 },
{ 42896, 42896 },
+ { 42898, 42898 },
+ { 42902, 42902 },
+ { 42904, 42904 },
+ { 42906, 42906 },
+ { 42908, 42908 },
+ { 42910, 42910 },
{ 42912, 42912 },
{ 42914, 42914 },
{ 42916, 42916 },
{ 42918, 42918 },
{ 42920, 42920 },
+ { 42922, 42926 },
+ { 42928, 42932 },
+ { 42934, 42934 },
+ { 42936, 42936 },
{ 65313, 65338 },
};
-static URange32 Lu_range32[] = {
+static const URange32 Lu_range32[] = {
{ 66560, 66599 },
+ { 66736, 66771 },
+ { 68736, 68786 },
+ { 71840, 71871 },
+ { 93760, 93791 },
{ 119808, 119833 },
{ 119860, 119885 },
{ 119912, 119937 },
@@ -1801,8 +2062,9 @@ static URange32 Lu_range32[] = {
{ 120662, 120686 },
{ 120720, 120744 },
{ 120778, 120778 },
+ { 125184, 125217 },
};
-static URange16 Pf_range16[] = {
+static const URange16 Pf_range16[] = {
{ 187, 187 },
{ 8217, 8217 },
{ 8221, 8221 },
@@ -1814,7 +2076,7 @@ static URange16 Pf_range16[] = {
{ 11805, 11805 },
{ 11809, 11809 },
};
-static URange16 Pd_range16[] = {
+static const URange16 Pd_range16[] = {
{ 45, 45 },
{ 1418, 1418 },
{ 1470, 1470 },
@@ -1823,6 +2085,8 @@ static URange16 Pd_range16[] = {
{ 8208, 8213 },
{ 11799, 11799 },
{ 11802, 11802 },
+ { 11834, 11835 },
+ { 11840, 11840 },
{ 12316, 12316 },
{ 12336, 12336 },
{ 12448, 12448 },
@@ -1831,7 +2095,7 @@ static URange16 Pd_range16[] = {
{ 65123, 65123 },
{ 65293, 65293 },
};
-static URange16 Pe_range16[] = {
+static const URange16 Pe_range16[] = {
{ 41, 41 },
{ 93, 93 },
{ 125, 125 },
@@ -1841,6 +2105,8 @@ static URange16 Pe_range16[] = {
{ 8262, 8262 },
{ 8318, 8318 },
{ 8334, 8334 },
+ { 8969, 8969 },
+ { 8971, 8971 },
{ 9002, 9002 },
{ 10089, 10089 },
{ 10091, 10091 },
@@ -1883,7 +2149,7 @@ static URange16 Pe_range16[] = {
{ 12313, 12313 },
{ 12315, 12315 },
{ 12318, 12319 },
- { 64831, 64831 },
+ { 64830, 64830 },
{ 65048, 65048 },
{ 65078, 65078 },
{ 65080, 65080 },
@@ -1903,7 +2169,7 @@ static URange16 Pe_range16[] = {
{ 65376, 65376 },
{ 65379, 65379 },
};
-static URange16 Pi_range16[] = {
+static const URange16 Pi_range16[] = {
{ 171, 171 },
{ 8216, 8216 },
{ 8219, 8220 },
@@ -1916,7 +2182,7 @@ static URange16 Pi_range16[] = {
{ 11804, 11804 },
{ 11808, 11808 },
};
-static URange16 Po_range16[] = {
+static const URange16 Po_range16[] = {
{ 33, 35 },
{ 37, 39 },
{ 42, 42 },
@@ -1926,7 +2192,8 @@ static URange16 Po_range16[] = {
{ 63, 64 },
{ 92, 92 },
{ 161, 161 },
- { 183, 183 },
+ { 167, 167 },
+ { 182, 183 },
{ 191, 191 },
{ 894, 894 },
{ 903, 903 },
@@ -1948,16 +2215,21 @@ static URange16 Po_range16[] = {
{ 2142, 2142 },
{ 2404, 2405 },
{ 2416, 2416 },
+ { 2557, 2557 },
+ { 2678, 2678 },
+ { 2800, 2800 },
+ { 3204, 3204 },
{ 3572, 3572 },
{ 3663, 3663 },
{ 3674, 3675 },
{ 3844, 3858 },
+ { 3860, 3860 },
{ 3973, 3973 },
{ 4048, 4052 },
{ 4057, 4058 },
{ 4170, 4175 },
{ 4347, 4347 },
- { 4961, 4968 },
+ { 4960, 4968 },
{ 5741, 5742 },
{ 5867, 5869 },
{ 5941, 5942 },
@@ -1973,6 +2245,7 @@ static URange16 Po_range16[] = {
{ 7164, 7167 },
{ 7227, 7231 },
{ 7294, 7295 },
+ { 7360, 7367 },
{ 7379, 7379 },
{ 8214, 8215 },
{ 8224, 8231 },
@@ -1993,7 +2266,10 @@ static URange16 Po_range16[] = {
{ 11803, 11803 },
{ 11806, 11807 },
{ 11818, 11822 },
- { 11824, 11825 },
+ { 11824, 11833 },
+ { 11836, 11839 },
+ { 11841, 11841 },
+ { 11843, 11854 },
{ 12289, 12291 },
{ 12349, 12349 },
{ 12539, 12539 },
@@ -2005,12 +2281,14 @@ static URange16 Po_range16[] = {
{ 43124, 43127 },
{ 43214, 43215 },
{ 43256, 43258 },
+ { 43260, 43260 },
{ 43310, 43311 },
{ 43359, 43359 },
{ 43457, 43469 },
{ 43486, 43487 },
{ 43612, 43615 },
{ 43742, 43743 },
+ { 43760, 43761 },
{ 44011, 44011 },
{ 65040, 65046 },
{ 65049, 65049 },
@@ -2033,52 +2311,92 @@ static URange16 Po_range16[] = {
{ 65377, 65377 },
{ 65380, 65381 },
};
-static URange32 Po_range32[] = {
- { 65792, 65793 },
+static const URange32 Po_range32[] = {
+ { 65792, 65794 },
{ 66463, 66463 },
{ 66512, 66512 },
+ { 66927, 66927 },
{ 67671, 67671 },
{ 67871, 67871 },
{ 67903, 67903 },
{ 68176, 68184 },
{ 68223, 68223 },
+ { 68336, 68342 },
{ 68409, 68415 },
+ { 68505, 68508 },
+ { 69461, 69465 },
{ 69703, 69709 },
{ 69819, 69820 },
{ 69822, 69825 },
- { 74864, 74867 },
-};
-static URange16 Me_range16[] = {
+ { 69952, 69955 },
+ { 70004, 70005 },
+ { 70085, 70088 },
+ { 70093, 70093 },
+ { 70107, 70107 },
+ { 70109, 70111 },
+ { 70200, 70205 },
+ { 70313, 70313 },
+ { 70731, 70735 },
+ { 70747, 70747 },
+ { 70749, 70749 },
+ { 70854, 70854 },
+ { 71105, 71127 },
+ { 71233, 71235 },
+ { 71264, 71276 },
+ { 71484, 71486 },
+ { 71739, 71739 },
+ { 72255, 72262 },
+ { 72346, 72348 },
+ { 72350, 72354 },
+ { 72769, 72773 },
+ { 72816, 72817 },
+ { 73463, 73464 },
+ { 74864, 74868 },
+ { 92782, 92783 },
+ { 92917, 92917 },
+ { 92983, 92987 },
+ { 92996, 92996 },
+ { 93847, 93850 },
+ { 113823, 113823 },
+ { 121479, 121483 },
+ { 125278, 125279 },
+};
+static const URange16 Me_range16[] = {
{ 1160, 1161 },
+ { 6846, 6846 },
{ 8413, 8416 },
{ 8418, 8420 },
{ 42608, 42610 },
};
-static URange16 C_range16[] = {
+static const URange16 C_range16[] = {
{ 0, 31 },
{ 127, 159 },
{ 173, 173 },
- { 1536, 1539 },
+ { 1536, 1541 },
+ { 1564, 1564 },
{ 1757, 1757 },
{ 1807, 1807 },
- { 6068, 6069 },
+ { 2274, 2274 },
+ { 6158, 6158 },
{ 8203, 8207 },
{ 8234, 8238 },
{ 8288, 8292 },
- { 8298, 8303 },
+ { 8294, 8303 },
{ 55296, 63743 },
{ 65279, 65279 },
{ 65529, 65531 },
};
-static URange32 C_range32[] = {
+static const URange32 C_range32[] = {
{ 69821, 69821 },
+ { 69837, 69837 },
+ { 113824, 113827 },
{ 119155, 119162 },
{ 917505, 917505 },
{ 917536, 917631 },
{ 983040, 1048573 },
{ 1048576, 1114109 },
};
-static URange16 Mc_range16[] = {
+static const URange16 Mc_range16[] = {
{ 2307, 2307 },
{ 2363, 2363 },
{ 2366, 2368 },
@@ -2143,9 +2461,7 @@ static URange16 Mc_range16[] = {
{ 6441, 6443 },
{ 6448, 6449 },
{ 6451, 6456 },
- { 6576, 6592 },
- { 6600, 6601 },
- { 6681, 6683 },
+ { 6681, 6682 },
{ 6741, 6741 },
{ 6743, 6743 },
{ 6753, 6753 },
@@ -2167,7 +2483,9 @@ static URange16 Mc_range16[] = {
{ 7204, 7211 },
{ 7220, 7221 },
{ 7393, 7393 },
- { 7410, 7410 },
+ { 7410, 7411 },
+ { 7415, 7415 },
+ { 12334, 12335 },
{ 43043, 43044 },
{ 43047, 43047 },
{ 43136, 43137 },
@@ -2181,21 +2499,74 @@ static URange16 Mc_range16[] = {
{ 43571, 43572 },
{ 43597, 43597 },
{ 43643, 43643 },
+ { 43645, 43645 },
+ { 43755, 43755 },
+ { 43758, 43759 },
+ { 43765, 43765 },
{ 44003, 44004 },
{ 44006, 44007 },
{ 44009, 44010 },
{ 44012, 44012 },
};
-static URange32 Mc_range32[] = {
+static const URange32 Mc_range32[] = {
{ 69632, 69632 },
{ 69634, 69634 },
{ 69762, 69762 },
{ 69808, 69810 },
{ 69815, 69816 },
+ { 69932, 69932 },
+ { 69957, 69958 },
+ { 70018, 70018 },
+ { 70067, 70069 },
+ { 70079, 70080 },
+ { 70188, 70190 },
+ { 70194, 70195 },
+ { 70197, 70197 },
+ { 70368, 70370 },
+ { 70402, 70403 },
+ { 70462, 70463 },
+ { 70465, 70468 },
+ { 70471, 70472 },
+ { 70475, 70477 },
+ { 70487, 70487 },
+ { 70498, 70499 },
+ { 70709, 70711 },
+ { 70720, 70721 },
+ { 70725, 70725 },
+ { 70832, 70834 },
+ { 70841, 70841 },
+ { 70843, 70846 },
+ { 70849, 70849 },
+ { 71087, 71089 },
+ { 71096, 71099 },
+ { 71102, 71102 },
+ { 71216, 71218 },
+ { 71227, 71228 },
+ { 71230, 71230 },
+ { 71340, 71340 },
+ { 71342, 71343 },
+ { 71350, 71350 },
+ { 71456, 71457 },
+ { 71462, 71462 },
+ { 71724, 71726 },
+ { 71736, 71736 },
+ { 72249, 72249 },
+ { 72279, 72280 },
+ { 72343, 72343 },
+ { 72751, 72751 },
+ { 72766, 72766 },
+ { 72873, 72873 },
+ { 72881, 72881 },
+ { 72884, 72884 },
+ { 73098, 73102 },
+ { 73107, 73108 },
+ { 73110, 73110 },
+ { 73461, 73462 },
+ { 94033, 94078 },
{ 119141, 119142 },
{ 119149, 119154 },
};
-static URange16 Mn_range16[] = {
+static const URange16 Mn_range16[] = {
{ 768, 879 },
{ 1155, 1159 },
{ 1425, 1469 },
@@ -2214,12 +2585,14 @@ static URange16 Mn_range16[] = {
{ 1840, 1866 },
{ 1958, 1968 },
{ 2027, 2035 },
+ { 2045, 2045 },
{ 2070, 2073 },
{ 2075, 2083 },
{ 2085, 2087 },
{ 2089, 2093 },
{ 2137, 2139 },
- { 2304, 2306 },
+ { 2259, 2273 },
+ { 2275, 2306 },
{ 2362, 2362 },
{ 2364, 2364 },
{ 2369, 2376 },
@@ -2231,6 +2604,7 @@ static URange16 Mn_range16[] = {
{ 2497, 2500 },
{ 2509, 2509 },
{ 2530, 2531 },
+ { 2558, 2558 },
{ 2561, 2562 },
{ 2620, 2620 },
{ 2625, 2626 },
@@ -2245,6 +2619,7 @@ static URange16 Mn_range16[] = {
{ 2759, 2760 },
{ 2765, 2765 },
{ 2786, 2787 },
+ { 2810, 2815 },
{ 2817, 2817 },
{ 2876, 2876 },
{ 2879, 2879 },
@@ -2255,16 +2630,21 @@ static URange16 Mn_range16[] = {
{ 2946, 2946 },
{ 3008, 3008 },
{ 3021, 3021 },
+ { 3072, 3072 },
+ { 3076, 3076 },
{ 3134, 3136 },
{ 3142, 3144 },
{ 3146, 3149 },
{ 3157, 3158 },
{ 3170, 3171 },
+ { 3201, 3201 },
{ 3260, 3260 },
{ 3263, 3263 },
{ 3270, 3270 },
{ 3276, 3277 },
{ 3298, 3299 },
+ { 3328, 3329 },
+ { 3387, 3388 },
{ 3393, 3396 },
{ 3405, 3405 },
{ 3426, 3427 },
@@ -2304,17 +2684,20 @@ static URange16 Mn_range16[] = {
{ 5938, 5940 },
{ 5970, 5971 },
{ 6002, 6003 },
+ { 6068, 6069 },
{ 6071, 6077 },
{ 6086, 6086 },
{ 6089, 6099 },
{ 6109, 6109 },
{ 6155, 6157 },
+ { 6277, 6278 },
{ 6313, 6313 },
{ 6432, 6434 },
{ 6439, 6440 },
{ 6450, 6450 },
{ 6457, 6459 },
{ 6679, 6680 },
+ { 6683, 6683 },
{ 6742, 6742 },
{ 6744, 6750 },
{ 6752, 6752 },
@@ -2322,6 +2705,7 @@ static URange16 Mn_range16[] = {
{ 6757, 6764 },
{ 6771, 6780 },
{ 6783, 6783 },
+ { 6832, 6845 },
{ 6912, 6915 },
{ 6964, 6964 },
{ 6966, 6970 },
@@ -2331,6 +2715,7 @@ static URange16 Mn_range16[] = {
{ 7040, 7041 },
{ 7074, 7077 },
{ 7080, 7081 },
+ { 7083, 7085 },
{ 7142, 7142 },
{ 7144, 7145 },
{ 7149, 7149 },
@@ -2341,68 +2726,164 @@ static URange16 Mn_range16[] = {
{ 7380, 7392 },
{ 7394, 7400 },
{ 7405, 7405 },
- { 7616, 7654 },
- { 7676, 7679 },
+ { 7412, 7412 },
+ { 7416, 7417 },
+ { 7616, 7673 },
+ { 7675, 7679 },
{ 8400, 8412 },
{ 8417, 8417 },
{ 8421, 8432 },
{ 11503, 11505 },
{ 11647, 11647 },
{ 11744, 11775 },
- { 12330, 12335 },
+ { 12330, 12333 },
{ 12441, 12442 },
{ 42607, 42607 },
- { 42620, 42621 },
+ { 42612, 42621 },
+ { 42654, 42655 },
{ 42736, 42737 },
{ 43010, 43010 },
{ 43014, 43014 },
{ 43019, 43019 },
{ 43045, 43046 },
- { 43204, 43204 },
+ { 43204, 43205 },
{ 43232, 43249 },
+ { 43263, 43263 },
{ 43302, 43309 },
{ 43335, 43345 },
{ 43392, 43394 },
{ 43443, 43443 },
{ 43446, 43449 },
{ 43452, 43452 },
+ { 43493, 43493 },
{ 43561, 43566 },
{ 43569, 43570 },
{ 43573, 43574 },
{ 43587, 43587 },
{ 43596, 43596 },
+ { 43644, 43644 },
{ 43696, 43696 },
{ 43698, 43700 },
{ 43703, 43704 },
{ 43710, 43711 },
{ 43713, 43713 },
+ { 43756, 43757 },
+ { 43766, 43766 },
{ 44005, 44005 },
{ 44008, 44008 },
{ 44013, 44013 },
{ 64286, 64286 },
{ 65024, 65039 },
- { 65056, 65062 },
+ { 65056, 65071 },
};
-static URange32 Mn_range32[] = {
+static const URange32 Mn_range32[] = {
{ 66045, 66045 },
+ { 66272, 66272 },
+ { 66422, 66426 },
{ 68097, 68099 },
{ 68101, 68102 },
{ 68108, 68111 },
{ 68152, 68154 },
{ 68159, 68159 },
+ { 68325, 68326 },
+ { 68900, 68903 },
+ { 69446, 69456 },
{ 69633, 69633 },
{ 69688, 69702 },
- { 69760, 69761 },
+ { 69759, 69761 },
{ 69811, 69814 },
{ 69817, 69818 },
+ { 69888, 69890 },
+ { 69927, 69931 },
+ { 69933, 69940 },
+ { 70003, 70003 },
+ { 70016, 70017 },
+ { 70070, 70078 },
+ { 70089, 70092 },
+ { 70191, 70193 },
+ { 70196, 70196 },
+ { 70198, 70199 },
+ { 70206, 70206 },
+ { 70367, 70367 },
+ { 70371, 70378 },
+ { 70400, 70401 },
+ { 70459, 70460 },
+ { 70464, 70464 },
+ { 70502, 70508 },
+ { 70512, 70516 },
+ { 70712, 70719 },
+ { 70722, 70724 },
+ { 70726, 70726 },
+ { 70750, 70750 },
+ { 70835, 70840 },
+ { 70842, 70842 },
+ { 70847, 70848 },
+ { 70850, 70851 },
+ { 71090, 71093 },
+ { 71100, 71101 },
+ { 71103, 71104 },
+ { 71132, 71133 },
+ { 71219, 71226 },
+ { 71229, 71229 },
+ { 71231, 71232 },
+ { 71339, 71339 },
+ { 71341, 71341 },
+ { 71344, 71349 },
+ { 71351, 71351 },
+ { 71453, 71455 },
+ { 71458, 71461 },
+ { 71463, 71467 },
+ { 71727, 71735 },
+ { 71737, 71738 },
+ { 72193, 72202 },
+ { 72243, 72248 },
+ { 72251, 72254 },
+ { 72263, 72263 },
+ { 72273, 72278 },
+ { 72281, 72283 },
+ { 72330, 72342 },
+ { 72344, 72345 },
+ { 72752, 72758 },
+ { 72760, 72765 },
+ { 72767, 72767 },
+ { 72850, 72871 },
+ { 72874, 72880 },
+ { 72882, 72883 },
+ { 72885, 72886 },
+ { 73009, 73014 },
+ { 73018, 73018 },
+ { 73020, 73021 },
+ { 73023, 73029 },
+ { 73031, 73031 },
+ { 73104, 73105 },
+ { 73109, 73109 },
+ { 73111, 73111 },
+ { 73459, 73460 },
+ { 92912, 92916 },
+ { 92976, 92982 },
+ { 94095, 94098 },
+ { 113821, 113822 },
{ 119143, 119145 },
{ 119163, 119170 },
{ 119173, 119179 },
{ 119210, 119213 },
{ 119362, 119364 },
+ { 121344, 121398 },
+ { 121403, 121452 },
+ { 121461, 121461 },
+ { 121476, 121476 },
+ { 121499, 121503 },
+ { 121505, 121519 },
+ { 122880, 122886 },
+ { 122888, 122904 },
+ { 122907, 122913 },
+ { 122915, 122916 },
+ { 122918, 122922 },
+ { 125136, 125142 },
+ { 125252, 125258 },
{ 917760, 917999 },
};
-static URange16 M_range16[] = {
+static const URange16 M_range16[] = {
{ 768, 879 },
{ 1155, 1161 },
{ 1425, 1469 },
@@ -2421,12 +2902,14 @@ static URange16 M_range16[] = {
{ 1840, 1866 },
{ 1958, 1968 },
{ 2027, 2035 },
+ { 2045, 2045 },
{ 2070, 2073 },
{ 2075, 2083 },
{ 2085, 2087 },
{ 2089, 2093 },
{ 2137, 2139 },
- { 2304, 2307 },
+ { 2259, 2273 },
+ { 2275, 2307 },
{ 2362, 2364 },
{ 2366, 2383 },
{ 2385, 2391 },
@@ -2438,6 +2921,7 @@ static URange16 M_range16[] = {
{ 2507, 2509 },
{ 2519, 2519 },
{ 2530, 2531 },
+ { 2558, 2558 },
{ 2561, 2563 },
{ 2620, 2620 },
{ 2622, 2626 },
@@ -2452,6 +2936,7 @@ static URange16 M_range16[] = {
{ 2759, 2761 },
{ 2763, 2765 },
{ 2786, 2787 },
+ { 2810, 2815 },
{ 2817, 2819 },
{ 2876, 2876 },
{ 2878, 2884 },
@@ -2464,20 +2949,21 @@ static URange16 M_range16[] = {
{ 3014, 3016 },
{ 3018, 3021 },
{ 3031, 3031 },
- { 3073, 3075 },
+ { 3072, 3076 },
{ 3134, 3140 },
{ 3142, 3144 },
{ 3146, 3149 },
{ 3157, 3158 },
{ 3170, 3171 },
- { 3202, 3203 },
+ { 3201, 3203 },
{ 3260, 3260 },
{ 3262, 3268 },
{ 3270, 3272 },
{ 3274, 3277 },
{ 3285, 3286 },
{ 3298, 3299 },
- { 3330, 3331 },
+ { 3328, 3331 },
+ { 3387, 3388 },
{ 3390, 3396 },
{ 3398, 3400 },
{ 3402, 3405 },
@@ -2520,31 +3006,32 @@ static URange16 M_range16[] = {
{ 5938, 5940 },
{ 5970, 5971 },
{ 6002, 6003 },
- { 6070, 6099 },
+ { 6068, 6099 },
{ 6109, 6109 },
{ 6155, 6157 },
+ { 6277, 6278 },
{ 6313, 6313 },
{ 6432, 6443 },
{ 6448, 6459 },
- { 6576, 6592 },
- { 6600, 6601 },
{ 6679, 6683 },
{ 6741, 6750 },
{ 6752, 6780 },
{ 6783, 6783 },
+ { 6832, 6846 },
{ 6912, 6916 },
{ 6964, 6980 },
{ 7019, 7027 },
{ 7040, 7042 },
- { 7073, 7082 },
+ { 7073, 7085 },
{ 7142, 7155 },
{ 7204, 7223 },
{ 7376, 7378 },
{ 7380, 7400 },
{ 7405, 7405 },
- { 7410, 7410 },
- { 7616, 7654 },
- { 7676, 7679 },
+ { 7410, 7412 },
+ { 7415, 7417 },
+ { 7616, 7673 },
+ { 7675, 7679 },
{ 8400, 8432 },
{ 11503, 11505 },
{ 11647, 11647 },
@@ -2552,54 +3039,130 @@ static URange16 M_range16[] = {
{ 12330, 12335 },
{ 12441, 12442 },
{ 42607, 42610 },
- { 42620, 42621 },
+ { 42612, 42621 },
+ { 42654, 42655 },
{ 42736, 42737 },
{ 43010, 43010 },
{ 43014, 43014 },
{ 43019, 43019 },
{ 43043, 43047 },
{ 43136, 43137 },
- { 43188, 43204 },
+ { 43188, 43205 },
{ 43232, 43249 },
+ { 43263, 43263 },
{ 43302, 43309 },
{ 43335, 43347 },
{ 43392, 43395 },
{ 43443, 43456 },
+ { 43493, 43493 },
{ 43561, 43574 },
{ 43587, 43587 },
{ 43596, 43597 },
- { 43643, 43643 },
+ { 43643, 43645 },
{ 43696, 43696 },
{ 43698, 43700 },
{ 43703, 43704 },
{ 43710, 43711 },
{ 43713, 43713 },
+ { 43755, 43759 },
+ { 43765, 43766 },
{ 44003, 44010 },
{ 44012, 44013 },
{ 64286, 64286 },
{ 65024, 65039 },
- { 65056, 65062 },
+ { 65056, 65071 },
};
-static URange32 M_range32[] = {
+static const URange32 M_range32[] = {
{ 66045, 66045 },
+ { 66272, 66272 },
+ { 66422, 66426 },
{ 68097, 68099 },
{ 68101, 68102 },
{ 68108, 68111 },
{ 68152, 68154 },
{ 68159, 68159 },
+ { 68325, 68326 },
+ { 68900, 68903 },
+ { 69446, 69456 },
{ 69632, 69634 },
{ 69688, 69702 },
- { 69760, 69762 },
+ { 69759, 69762 },
{ 69808, 69818 },
+ { 69888, 69890 },
+ { 69927, 69940 },
+ { 69957, 69958 },
+ { 70003, 70003 },
+ { 70016, 70018 },
+ { 70067, 70080 },
+ { 70089, 70092 },
+ { 70188, 70199 },
+ { 70206, 70206 },
+ { 70367, 70378 },
+ { 70400, 70403 },
+ { 70459, 70460 },
+ { 70462, 70468 },
+ { 70471, 70472 },
+ { 70475, 70477 },
+ { 70487, 70487 },
+ { 70498, 70499 },
+ { 70502, 70508 },
+ { 70512, 70516 },
+ { 70709, 70726 },
+ { 70750, 70750 },
+ { 70832, 70851 },
+ { 71087, 71093 },
+ { 71096, 71104 },
+ { 71132, 71133 },
+ { 71216, 71232 },
+ { 71339, 71351 },
+ { 71453, 71467 },
+ { 71724, 71738 },
+ { 72193, 72202 },
+ { 72243, 72249 },
+ { 72251, 72254 },
+ { 72263, 72263 },
+ { 72273, 72283 },
+ { 72330, 72345 },
+ { 72751, 72758 },
+ { 72760, 72767 },
+ { 72850, 72871 },
+ { 72873, 72886 },
+ { 73009, 73014 },
+ { 73018, 73018 },
+ { 73020, 73021 },
+ { 73023, 73029 },
+ { 73031, 73031 },
+ { 73098, 73102 },
+ { 73104, 73105 },
+ { 73107, 73111 },
+ { 73459, 73462 },
+ { 92912, 92916 },
+ { 92976, 92982 },
+ { 94033, 94078 },
+ { 94095, 94098 },
+ { 113821, 113822 },
{ 119141, 119145 },
{ 119149, 119154 },
{ 119163, 119170 },
{ 119173, 119179 },
{ 119210, 119213 },
{ 119362, 119364 },
+ { 121344, 121398 },
+ { 121403, 121452 },
+ { 121461, 121461 },
+ { 121476, 121476 },
+ { 121499, 121503 },
+ { 121505, 121519 },
+ { 122880, 122886 },
+ { 122888, 122904 },
+ { 122907, 122913 },
+ { 122915, 122916 },
+ { 122918, 122922 },
+ { 125136, 125142 },
+ { 125252, 125258 },
{ 917760, 917999 },
};
-static URange16 L_range16[] = {
+static const URange16 L_range16[] = {
{ 65, 90 },
{ 97, 122 },
{ 170, 170 },
@@ -2615,18 +3178,19 @@ static URange16 L_range16[] = {
{ 880, 884 },
{ 886, 887 },
{ 890, 893 },
+ { 895, 895 },
{ 902, 902 },
{ 904, 906 },
{ 908, 908 },
{ 910, 929 },
{ 931, 1013 },
{ 1015, 1153 },
- { 1162, 1319 },
+ { 1162, 1327 },
{ 1329, 1366 },
{ 1369, 1369 },
- { 1377, 1415 },
+ { 1376, 1416 },
{ 1488, 1514 },
- { 1520, 1522 },
+ { 1519, 1522 },
{ 1568, 1610 },
{ 1646, 1647 },
{ 1649, 1747 },
@@ -2647,12 +3211,14 @@ static URange16 L_range16[] = {
{ 2084, 2084 },
{ 2088, 2088 },
{ 2112, 2136 },
+ { 2144, 2154 },
+ { 2208, 2228 },
+ { 2230, 2237 },
{ 2308, 2361 },
{ 2365, 2365 },
{ 2384, 2384 },
{ 2392, 2401 },
- { 2417, 2423 },
- { 2425, 2431 },
+ { 2417, 2432 },
{ 2437, 2444 },
{ 2447, 2448 },
{ 2451, 2472 },
@@ -2664,6 +3230,7 @@ static URange16 L_range16[] = {
{ 2524, 2525 },
{ 2527, 2529 },
{ 2544, 2545 },
+ { 2556, 2556 },
{ 2565, 2570 },
{ 2575, 2576 },
{ 2579, 2600 },
@@ -2683,6 +3250,7 @@ static URange16 L_range16[] = {
{ 2749, 2749 },
{ 2768, 2768 },
{ 2784, 2785 },
+ { 2809, 2809 },
{ 2821, 2828 },
{ 2831, 2832 },
{ 2835, 2856 },
@@ -2707,11 +3275,11 @@ static URange16 L_range16[] = {
{ 3077, 3084 },
{ 3086, 3088 },
{ 3090, 3112 },
- { 3114, 3123 },
- { 3125, 3129 },
+ { 3114, 3129 },
{ 3133, 3133 },
- { 3160, 3161 },
+ { 3160, 3162 },
{ 3168, 3169 },
+ { 3200, 3200 },
{ 3205, 3212 },
{ 3214, 3216 },
{ 3218, 3240 },
@@ -2726,7 +3294,8 @@ static URange16 L_range16[] = {
{ 3346, 3386 },
{ 3389, 3389 },
{ 3406, 3406 },
- { 3424, 3425 },
+ { 3412, 3414 },
+ { 3423, 3425 },
{ 3450, 3455 },
{ 3461, 3478 },
{ 3482, 3505 },
@@ -2752,7 +3321,7 @@ static URange16 L_range16[] = {
{ 3773, 3773 },
{ 3776, 3780 },
{ 3782, 3782 },
- { 3804, 3805 },
+ { 3804, 3807 },
{ 3840, 3840 },
{ 3904, 3911 },
{ 3913, 3948 },
@@ -2767,9 +3336,10 @@ static URange16 L_range16[] = {
{ 4213, 4225 },
{ 4238, 4238 },
{ 4256, 4293 },
+ { 4295, 4295 },
+ { 4301, 4301 },
{ 4304, 4346 },
- { 4348, 4348 },
- { 4352, 4680 },
+ { 4348, 4680 },
{ 4682, 4685 },
{ 4688, 4694 },
{ 4696, 4696 },
@@ -2786,11 +3356,13 @@ static URange16 L_range16[] = {
{ 4882, 4885 },
{ 4888, 4954 },
{ 4992, 5007 },
- { 5024, 5108 },
+ { 5024, 5109 },
+ { 5112, 5117 },
{ 5121, 5740 },
{ 5743, 5759 },
{ 5761, 5786 },
{ 5792, 5866 },
+ { 5873, 5880 },
{ 5888, 5900 },
{ 5902, 5905 },
{ 5920, 5937 },
@@ -2800,15 +3372,16 @@ static URange16 L_range16[] = {
{ 6016, 6067 },
{ 6103, 6103 },
{ 6108, 6108 },
- { 6176, 6263 },
- { 6272, 6312 },
+ { 6176, 6264 },
+ { 6272, 6276 },
+ { 6279, 6312 },
{ 6314, 6314 },
{ 6320, 6389 },
- { 6400, 6428 },
+ { 6400, 6430 },
{ 6480, 6509 },
{ 6512, 6516 },
{ 6528, 6571 },
- { 6593, 6599 },
+ { 6576, 6601 },
{ 6656, 6678 },
{ 6688, 6740 },
{ 6823, 6823 },
@@ -2816,12 +3389,16 @@ static URange16 L_range16[] = {
{ 6981, 6987 },
{ 7043, 7072 },
{ 7086, 7087 },
- { 7104, 7141 },
+ { 7098, 7141 },
{ 7168, 7203 },
{ 7245, 7247 },
{ 7258, 7293 },
+ { 7296, 7304 },
+ { 7312, 7354 },
+ { 7357, 7359 },
{ 7401, 7404 },
{ 7406, 7409 },
+ { 7413, 7414 },
{ 7424, 7615 },
{ 7680, 7957 },
{ 7960, 7965 },
@@ -2863,8 +3440,11 @@ static URange16 L_range16[] = {
{ 11312, 11358 },
{ 11360, 11492 },
{ 11499, 11502 },
+ { 11506, 11507 },
{ 11520, 11557 },
- { 11568, 11621 },
+ { 11559, 11559 },
+ { 11565, 11565 },
+ { 11568, 11623 },
{ 11631, 11631 },
{ 11648, 11670 },
{ 11680, 11686 },
@@ -2883,26 +3463,24 @@ static URange16 L_range16[] = {
{ 12445, 12447 },
{ 12449, 12538 },
{ 12540, 12543 },
- { 12549, 12589 },
+ { 12549, 12591 },
{ 12593, 12686 },
{ 12704, 12730 },
{ 12784, 12799 },
{ 13312, 19893 },
- { 19968, 40907 },
+ { 19968, 40943 },
{ 40960, 42124 },
{ 42192, 42237 },
{ 42240, 42508 },
{ 42512, 42527 },
{ 42538, 42539 },
{ 42560, 42606 },
- { 42623, 42647 },
+ { 42623, 42653 },
{ 42656, 42725 },
{ 42775, 42783 },
{ 42786, 42888 },
- { 42891, 42894 },
- { 42896, 42897 },
- { 42912, 42921 },
- { 43002, 43009 },
+ { 42891, 42937 },
+ { 42999, 43009 },
{ 43011, 43013 },
{ 43015, 43018 },
{ 43020, 43042 },
@@ -2910,34 +3488,41 @@ static URange16 L_range16[] = {
{ 43138, 43187 },
{ 43250, 43255 },
{ 43259, 43259 },
+ { 43261, 43262 },
{ 43274, 43301 },
{ 43312, 43334 },
{ 43360, 43388 },
{ 43396, 43442 },
{ 43471, 43471 },
+ { 43488, 43492 },
+ { 43494, 43503 },
+ { 43514, 43518 },
{ 43520, 43560 },
{ 43584, 43586 },
{ 43588, 43595 },
{ 43616, 43638 },
{ 43642, 43642 },
- { 43648, 43695 },
+ { 43646, 43695 },
{ 43697, 43697 },
{ 43701, 43702 },
{ 43705, 43709 },
{ 43712, 43712 },
{ 43714, 43714 },
{ 43739, 43741 },
+ { 43744, 43754 },
+ { 43762, 43764 },
{ 43777, 43782 },
{ 43785, 43790 },
{ 43793, 43798 },
{ 43808, 43814 },
{ 43816, 43822 },
- { 43968, 44002 },
+ { 43824, 43866 },
+ { 43868, 43877 },
+ { 43888, 44002 },
{ 44032, 55203 },
{ 55216, 55238 },
{ 55243, 55291 },
- { 63744, 64045 },
- { 64048, 64109 },
+ { 63744, 64109 },
{ 64112, 64217 },
{ 64256, 64262 },
{ 64275, 64279 },
@@ -2963,7 +3548,7 @@ static URange16 L_range16[] = {
{ 65490, 65495 },
{ 65498, 65500 },
};
-static URange32 L_range32[] = {
+static const URange32 L_range32[] = {
{ 65536, 65547 },
{ 65549, 65574 },
{ 65576, 65594 },
@@ -2973,36 +3558,141 @@ static URange32 L_range32[] = {
{ 65664, 65786 },
{ 66176, 66204 },
{ 66208, 66256 },
- { 66304, 66334 },
- { 66352, 66368 },
+ { 66304, 66335 },
+ { 66349, 66368 },
{ 66370, 66377 },
+ { 66384, 66421 },
{ 66432, 66461 },
{ 66464, 66499 },
{ 66504, 66511 },
{ 66560, 66717 },
+ { 66736, 66771 },
+ { 66776, 66811 },
+ { 66816, 66855 },
+ { 66864, 66915 },
+ { 67072, 67382 },
+ { 67392, 67413 },
+ { 67424, 67431 },
{ 67584, 67589 },
{ 67592, 67592 },
{ 67594, 67637 },
{ 67639, 67640 },
{ 67644, 67644 },
{ 67647, 67669 },
+ { 67680, 67702 },
+ { 67712, 67742 },
+ { 67808, 67826 },
+ { 67828, 67829 },
{ 67840, 67861 },
{ 67872, 67897 },
+ { 67968, 68023 },
+ { 68030, 68031 },
{ 68096, 68096 },
{ 68112, 68115 },
{ 68117, 68119 },
- { 68121, 68147 },
+ { 68121, 68149 },
{ 68192, 68220 },
+ { 68224, 68252 },
+ { 68288, 68295 },
+ { 68297, 68324 },
{ 68352, 68405 },
{ 68416, 68437 },
{ 68448, 68466 },
+ { 68480, 68497 },
{ 68608, 68680 },
+ { 68736, 68786 },
+ { 68800, 68850 },
+ { 68864, 68899 },
+ { 69376, 69404 },
+ { 69415, 69415 },
+ { 69424, 69445 },
{ 69635, 69687 },
{ 69763, 69807 },
- { 73728, 74606 },
+ { 69840, 69864 },
+ { 69891, 69926 },
+ { 69956, 69956 },
+ { 69968, 70002 },
+ { 70006, 70006 },
+ { 70019, 70066 },
+ { 70081, 70084 },
+ { 70106, 70106 },
+ { 70108, 70108 },
+ { 70144, 70161 },
+ { 70163, 70187 },
+ { 70272, 70278 },
+ { 70280, 70280 },
+ { 70282, 70285 },
+ { 70287, 70301 },
+ { 70303, 70312 },
+ { 70320, 70366 },
+ { 70405, 70412 },
+ { 70415, 70416 },
+ { 70419, 70440 },
+ { 70442, 70448 },
+ { 70450, 70451 },
+ { 70453, 70457 },
+ { 70461, 70461 },
+ { 70480, 70480 },
+ { 70493, 70497 },
+ { 70656, 70708 },
+ { 70727, 70730 },
+ { 70784, 70831 },
+ { 70852, 70853 },
+ { 70855, 70855 },
+ { 71040, 71086 },
+ { 71128, 71131 },
+ { 71168, 71215 },
+ { 71236, 71236 },
+ { 71296, 71338 },
+ { 71424, 71450 },
+ { 71680, 71723 },
+ { 71840, 71903 },
+ { 71935, 71935 },
+ { 72192, 72192 },
+ { 72203, 72242 },
+ { 72250, 72250 },
+ { 72272, 72272 },
+ { 72284, 72323 },
+ { 72326, 72329 },
+ { 72349, 72349 },
+ { 72384, 72440 },
+ { 72704, 72712 },
+ { 72714, 72750 },
+ { 72768, 72768 },
+ { 72818, 72847 },
+ { 72960, 72966 },
+ { 72968, 72969 },
+ { 72971, 73008 },
+ { 73030, 73030 },
+ { 73056, 73061 },
+ { 73063, 73064 },
+ { 73066, 73097 },
+ { 73112, 73112 },
+ { 73440, 73458 },
+ { 73728, 74649 },
+ { 74880, 75075 },
{ 77824, 78894 },
+ { 82944, 83526 },
{ 92160, 92728 },
- { 110592, 110593 },
+ { 92736, 92766 },
+ { 92880, 92909 },
+ { 92928, 92975 },
+ { 92992, 92995 },
+ { 93027, 93047 },
+ { 93053, 93071 },
+ { 93760, 93823 },
+ { 93952, 94020 },
+ { 94032, 94032 },
+ { 94099, 94111 },
+ { 94176, 94177 },
+ { 94208, 100337 },
+ { 100352, 101106 },
+ { 110592, 110878 },
+ { 110960, 111355 },
+ { 113664, 113770 },
+ { 113776, 113788 },
+ { 113792, 113800 },
+ { 113808, 113817 },
{ 119808, 119892 },
{ 119894, 119964 },
{ 119966, 119967 },
@@ -3033,12 +3723,49 @@ static URange32 L_range32[] = {
{ 120714, 120744 },
{ 120746, 120770 },
{ 120772, 120779 },
+ { 124928, 125124 },
+ { 125184, 125251 },
+ { 126464, 126467 },
+ { 126469, 126495 },
+ { 126497, 126498 },
+ { 126500, 126500 },
+ { 126503, 126503 },
+ { 126505, 126514 },
+ { 126516, 126519 },
+ { 126521, 126521 },
+ { 126523, 126523 },
+ { 126530, 126530 },
+ { 126535, 126535 },
+ { 126537, 126537 },
+ { 126539, 126539 },
+ { 126541, 126543 },
+ { 126545, 126546 },
+ { 126548, 126548 },
+ { 126551, 126551 },
+ { 126553, 126553 },
+ { 126555, 126555 },
+ { 126557, 126557 },
+ { 126559, 126559 },
+ { 126561, 126562 },
+ { 126564, 126564 },
+ { 126567, 126570 },
+ { 126572, 126578 },
+ { 126580, 126583 },
+ { 126585, 126588 },
+ { 126590, 126590 },
+ { 126592, 126601 },
+ { 126603, 126619 },
+ { 126625, 126627 },
+ { 126629, 126633 },
+ { 126635, 126651 },
{ 131072, 173782 },
{ 173824, 177972 },
{ 177984, 178205 },
+ { 178208, 183969 },
+ { 183984, 191456 },
{ 194560, 195101 },
};
-static URange16 N_range16[] = {
+static const URange16 N_range16[] = {
{ 48, 57 },
{ 178, 179 },
{ 185, 185 },
@@ -3057,7 +3784,9 @@ static URange16 N_range16[] = {
{ 3174, 3183 },
{ 3192, 3198 },
{ 3302, 3311 },
- { 3430, 3445 },
+ { 3416, 3422 },
+ { 3430, 3448 },
+ { 3558, 3567 },
{ 3664, 3673 },
{ 3792, 3801 },
{ 3872, 3891 },
@@ -3090,6 +3819,7 @@ static URange16 N_range16[] = {
{ 12344, 12346 },
{ 12690, 12693 },
{ 12832, 12841 },
+ { 12872, 12879 },
{ 12881, 12895 },
{ 12928, 12937 },
{ 12977, 12991 },
@@ -3099,33 +3829,72 @@ static URange16 N_range16[] = {
{ 43216, 43225 },
{ 43264, 43273 },
{ 43472, 43481 },
+ { 43504, 43513 },
{ 43600, 43609 },
{ 44016, 44025 },
{ 65296, 65305 },
};
-static URange32 N_range32[] = {
+static const URange32 N_range32[] = {
{ 65799, 65843 },
{ 65856, 65912 },
- { 65930, 65930 },
+ { 65930, 65931 },
+ { 66273, 66299 },
{ 66336, 66339 },
{ 66369, 66369 },
{ 66378, 66378 },
{ 66513, 66517 },
{ 66720, 66729 },
{ 67672, 67679 },
+ { 67705, 67711 },
+ { 67751, 67759 },
+ { 67835, 67839 },
{ 67862, 67867 },
- { 68160, 68167 },
+ { 68028, 68029 },
+ { 68032, 68047 },
+ { 68050, 68095 },
+ { 68160, 68168 },
{ 68221, 68222 },
+ { 68253, 68255 },
+ { 68331, 68335 },
{ 68440, 68447 },
{ 68472, 68479 },
+ { 68521, 68527 },
+ { 68858, 68863 },
+ { 68912, 68921 },
{ 69216, 69246 },
+ { 69405, 69414 },
+ { 69457, 69460 },
{ 69714, 69743 },
- { 74752, 74850 },
- { 119648, 119665 },
+ { 69872, 69881 },
+ { 69942, 69951 },
+ { 70096, 70105 },
+ { 70113, 70132 },
+ { 70384, 70393 },
+ { 70736, 70745 },
+ { 70864, 70873 },
+ { 71248, 71257 },
+ { 71360, 71369 },
+ { 71472, 71483 },
+ { 71904, 71922 },
+ { 72784, 72812 },
+ { 73040, 73049 },
+ { 73120, 73129 },
+ { 74752, 74862 },
+ { 92768, 92777 },
+ { 93008, 93017 },
+ { 93019, 93025 },
+ { 93824, 93846 },
+ { 119520, 119539 },
+ { 119648, 119672 },
{ 120782, 120831 },
- { 127232, 127242 },
-};
-static URange16 Sk_range16[] = {
+ { 125127, 125135 },
+ { 125264, 125273 },
+ { 126065, 126123 },
+ { 126125, 126127 },
+ { 126129, 126132 },
+ { 127232, 127244 },
+};
+static const URange16 Sk_range16[] = {
{ 94, 94 },
{ 96, 96 },
{ 168, 168 },
@@ -3149,12 +3918,16 @@ static URange16 Sk_range16[] = {
{ 42752, 42774 },
{ 42784, 42785 },
{ 42889, 42890 },
+ { 43867, 43867 },
{ 64434, 64449 },
{ 65342, 65342 },
{ 65344, 65344 },
{ 65507, 65507 },
};
-static URange16 P_range16[] = {
+static const URange32 Sk_range32[] = {
+ { 127995, 127999 },
+};
+static const URange16 P_range16[] = {
{ 33, 35 },
{ 37, 42 },
{ 44, 47 },
@@ -3165,8 +3938,9 @@ static URange16 P_range16[] = {
{ 123, 123 },
{ 125, 125 },
{ 161, 161 },
+ { 167, 167 },
{ 171, 171 },
- { 183, 183 },
+ { 182, 183 },
{ 187, 187 },
{ 191, 191 },
{ 894, 894 },
@@ -3190,17 +3964,22 @@ static URange16 P_range16[] = {
{ 2142, 2142 },
{ 2404, 2405 },
{ 2416, 2416 },
+ { 2557, 2557 },
+ { 2678, 2678 },
+ { 2800, 2800 },
+ { 3204, 3204 },
{ 3572, 3572 },
{ 3663, 3663 },
{ 3674, 3675 },
{ 3844, 3858 },
+ { 3860, 3860 },
{ 3898, 3901 },
{ 3973, 3973 },
{ 4048, 4052 },
{ 4057, 4058 },
{ 4170, 4175 },
{ 4347, 4347 },
- { 4961, 4968 },
+ { 4960, 4968 },
{ 5120, 5120 },
{ 5741, 5742 },
{ 5787, 5788 },
@@ -3217,6 +3996,7 @@ static URange16 P_range16[] = {
{ 7164, 7167 },
{ 7227, 7231 },
{ 7294, 7295 },
+ { 7360, 7367 },
{ 7379, 7379 },
{ 8208, 8231 },
{ 8240, 8259 },
@@ -3224,6 +4004,7 @@ static URange16 P_range16[] = {
{ 8275, 8286 },
{ 8317, 8318 },
{ 8333, 8334 },
+ { 8968, 8971 },
{ 9001, 9002 },
{ 10088, 10101 },
{ 10181, 10182 },
@@ -3235,7 +4016,7 @@ static URange16 P_range16[] = {
{ 11518, 11519 },
{ 11632, 11632 },
{ 11776, 11822 },
- { 11824, 11825 },
+ { 11824, 11854 },
{ 12289, 12291 },
{ 12296, 12305 },
{ 12308, 12319 },
@@ -3251,12 +4032,14 @@ static URange16 P_range16[] = {
{ 43124, 43127 },
{ 43214, 43215 },
{ 43256, 43258 },
+ { 43260, 43260 },
{ 43310, 43311 },
{ 43359, 43359 },
{ 43457, 43469 },
{ 43486, 43487 },
{ 43612, 43615 },
{ 43742, 43743 },
+ { 43760, 43761 },
{ 44011, 44011 },
{ 64830, 64831 },
{ 65040, 65049 },
@@ -3276,22 +4059,57 @@ static URange16 P_range16[] = {
{ 65373, 65373 },
{ 65375, 65381 },
};
-static URange32 P_range32[] = {
- { 65792, 65793 },
+static const URange32 P_range32[] = {
+ { 65792, 65794 },
{ 66463, 66463 },
{ 66512, 66512 },
+ { 66927, 66927 },
{ 67671, 67671 },
{ 67871, 67871 },
{ 67903, 67903 },
{ 68176, 68184 },
{ 68223, 68223 },
+ { 68336, 68342 },
{ 68409, 68415 },
+ { 68505, 68508 },
+ { 69461, 69465 },
{ 69703, 69709 },
{ 69819, 69820 },
{ 69822, 69825 },
- { 74864, 74867 },
-};
-static URange16 S_range16[] = {
+ { 69952, 69955 },
+ { 70004, 70005 },
+ { 70085, 70088 },
+ { 70093, 70093 },
+ { 70107, 70107 },
+ { 70109, 70111 },
+ { 70200, 70205 },
+ { 70313, 70313 },
+ { 70731, 70735 },
+ { 70747, 70747 },
+ { 70749, 70749 },
+ { 70854, 70854 },
+ { 71105, 71127 },
+ { 71233, 71235 },
+ { 71264, 71276 },
+ { 71484, 71486 },
+ { 71739, 71739 },
+ { 72255, 72262 },
+ { 72346, 72348 },
+ { 72350, 72354 },
+ { 72769, 72773 },
+ { 72816, 72817 },
+ { 73463, 73464 },
+ { 74864, 74868 },
+ { 92782, 92783 },
+ { 92917, 92917 },
+ { 92983, 92987 },
+ { 92996, 92996 },
+ { 93847, 93850 },
+ { 113823, 113823 },
+ { 121479, 121483 },
+ { 125278, 125279 },
+};
+static const URange16 S_range16[] = {
{ 36, 36 },
{ 43, 43 },
{ 60, 62 },
@@ -3299,11 +4117,11 @@ static URange16 S_range16[] = {
{ 96, 96 },
{ 124, 124 },
{ 126, 126 },
- { 162, 169 },
+ { 162, 166 },
+ { 168, 169 },
{ 172, 172 },
{ 174, 177 },
{ 180, 180 },
- { 182, 182 },
{ 184, 184 },
{ 215, 215 },
{ 247, 247 },
@@ -3316,6 +4134,7 @@ static URange16 S_range16[] = {
{ 900, 901 },
{ 1014, 1014 },
{ 1154, 1154 },
+ { 1421, 1423 },
{ 1542, 1544 },
{ 1547, 1547 },
{ 1550, 1551 },
@@ -3323,16 +4142,19 @@ static URange16 S_range16[] = {
{ 1769, 1769 },
{ 1789, 1790 },
{ 2038, 2038 },
+ { 2046, 2047 },
{ 2546, 2547 },
{ 2554, 2555 },
{ 2801, 2801 },
{ 2928, 2928 },
{ 3059, 3066 },
{ 3199, 3199 },
+ { 3407, 3407 },
{ 3449, 3449 },
{ 3647, 3647 },
{ 3841, 3843 },
- { 3859, 3863 },
+ { 3859, 3859 },
+ { 3861, 3863 },
{ 3866, 3871 },
{ 3892, 3892 },
{ 3894, 3894 },
@@ -3342,7 +4164,6 @@ static URange16 S_range16[] = {
{ 4046, 4047 },
{ 4053, 4056 },
{ 4254, 4255 },
- { 4960, 4960 },
{ 5008, 5017 },
{ 6107, 6107 },
{ 6464, 6464 },
@@ -3359,7 +4180,7 @@ static URange16 S_range16[] = {
{ 8274, 8274 },
{ 8314, 8316 },
{ 8330, 8332 },
- { 8352, 8377 },
+ { 8352, 8383 },
{ 8448, 8449 },
{ 8451, 8454 },
{ 8456, 8457 },
@@ -3374,22 +4195,22 @@ static URange16 S_range16[] = {
{ 8512, 8516 },
{ 8522, 8525 },
{ 8527, 8527 },
- { 8592, 9000 },
- { 9003, 9203 },
- { 9216, 9254 },
+ { 8586, 8587 },
+ { 8592, 8967 },
+ { 8972, 9000 },
+ { 9003, 9254 },
{ 9280, 9290 },
{ 9372, 9449 },
- { 9472, 9983 },
- { 9985, 10087 },
+ { 9472, 10087 },
{ 10132, 10180 },
- { 10183, 10186 },
- { 10188, 10188 },
- { 10190, 10213 },
+ { 10183, 10213 },
{ 10224, 10626 },
{ 10649, 10711 },
{ 10716, 10747 },
- { 10750, 11084 },
- { 11088, 11097 },
+ { 10750, 11123 },
+ { 11126, 11157 },
+ { 11160, 11208 },
+ { 11210, 11262 },
{ 11493, 11498 },
{ 11904, 11929 },
{ 11931, 12019 },
@@ -3405,7 +4226,8 @@ static URange16 S_range16[] = {
{ 12694, 12703 },
{ 12736, 12771 },
{ 12800, 12830 },
- { 12842, 12880 },
+ { 12842, 12871 },
+ { 12880, 12880 },
{ 12896, 12927 },
{ 12938, 12976 },
{ 12992, 13054 },
@@ -3418,6 +4240,7 @@ static URange16 S_range16[] = {
{ 43048, 43051 },
{ 43062, 43065 },
{ 43639, 43641 },
+ { 43867, 43867 },
{ 64297, 64297 },
{ 64434, 64449 },
{ 65020, 65021 },
@@ -3435,19 +4258,26 @@ static URange16 S_range16[] = {
{ 65512, 65518 },
{ 65532, 65533 },
};
-static URange32 S_range32[] = {
- { 65794, 65794 },
+static const URange32 S_range32[] = {
{ 65847, 65855 },
{ 65913, 65929 },
+ { 65932, 65934 },
{ 65936, 65947 },
+ { 65952, 65952 },
{ 66000, 66044 },
+ { 67703, 67704 },
+ { 68296, 68296 },
+ { 71487, 71487 },
+ { 92988, 92991 },
+ { 92997, 92997 },
+ { 113820, 113820 },
{ 118784, 119029 },
{ 119040, 119078 },
{ 119081, 119140 },
{ 119146, 119148 },
{ 119171, 119172 },
{ 119180, 119209 },
- { 119214, 119261 },
+ { 119214, 119272 },
{ 119296, 119361 },
{ 119365, 119365 },
{ 119552, 119638 },
@@ -3461,55 +4291,55 @@ static URange32 S_range32[] = {
{ 120713, 120713 },
{ 120745, 120745 },
{ 120771, 120771 },
+ { 120832, 121343 },
+ { 121399, 121402 },
+ { 121453, 121460 },
+ { 121462, 121475 },
+ { 121477, 121478 },
+ { 126124, 126124 },
+ { 126128, 126128 },
+ { 126704, 126705 },
{ 126976, 127019 },
{ 127024, 127123 },
{ 127136, 127150 },
- { 127153, 127166 },
+ { 127153, 127167 },
{ 127169, 127183 },
- { 127185, 127199 },
- { 127248, 127278 },
- { 127280, 127337 },
- { 127344, 127386 },
+ { 127185, 127221 },
+ { 127248, 127339 },
+ { 127344, 127404 },
{ 127462, 127490 },
- { 127504, 127546 },
+ { 127504, 127547 },
{ 127552, 127560 },
{ 127568, 127569 },
- { 127744, 127776 },
- { 127792, 127797 },
- { 127799, 127868 },
- { 127872, 127891 },
- { 127904, 127940 },
- { 127942, 127946 },
- { 127968, 127984 },
- { 128000, 128062 },
- { 128064, 128064 },
- { 128066, 128247 },
- { 128249, 128252 },
- { 128256, 128317 },
- { 128336, 128359 },
- { 128507, 128511 },
- { 128513, 128528 },
- { 128530, 128532 },
- { 128534, 128534 },
- { 128536, 128536 },
- { 128538, 128538 },
- { 128540, 128542 },
- { 128544, 128549 },
- { 128552, 128555 },
- { 128557, 128557 },
- { 128560, 128563 },
- { 128565, 128576 },
- { 128581, 128591 },
- { 128640, 128709 },
+ { 127584, 127589 },
+ { 127744, 128724 },
+ { 128736, 128748 },
+ { 128752, 128761 },
{ 128768, 128883 },
-};
-static URange16 So_range16[] = {
- { 166, 167 },
+ { 128896, 128984 },
+ { 129024, 129035 },
+ { 129040, 129095 },
+ { 129104, 129113 },
+ { 129120, 129159 },
+ { 129168, 129197 },
+ { 129280, 129291 },
+ { 129296, 129342 },
+ { 129344, 129392 },
+ { 129395, 129398 },
+ { 129402, 129402 },
+ { 129404, 129442 },
+ { 129456, 129465 },
+ { 129472, 129474 },
+ { 129488, 129535 },
+ { 129632, 129645 },
+};
+static const URange16 So_range16[] = {
+ { 166, 166 },
{ 169, 169 },
{ 174, 174 },
{ 176, 176 },
- { 182, 182 },
{ 1154, 1154 },
+ { 1421, 1422 },
{ 1550, 1551 },
{ 1758, 1758 },
{ 1769, 1769 },
@@ -3520,9 +4350,11 @@ static URange16 So_range16[] = {
{ 3059, 3064 },
{ 3066, 3066 },
{ 3199, 3199 },
+ { 3407, 3407 },
{ 3449, 3449 },
{ 3841, 3843 },
- { 3859, 3863 },
+ { 3859, 3859 },
+ { 3861, 3863 },
{ 3866, 3871 },
{ 3892, 3892 },
{ 3894, 3894 },
@@ -3532,7 +4364,6 @@ static URange16 So_range16[] = {
{ 4046, 4047 },
{ 4053, 4056 },
{ 4254, 4255 },
- { 4960, 4960 },
{ 5008, 5017 },
{ 6464, 6464 },
{ 6622, 6655 },
@@ -3552,6 +4383,7 @@ static URange16 So_range16[] = {
{ 8522, 8522 },
{ 8524, 8525 },
{ 8527, 8527 },
+ { 8586, 8587 },
{ 8597, 8601 },
{ 8604, 8607 },
{ 8609, 8610 },
@@ -3567,21 +4399,22 @@ static URange16 So_range16[] = {
{ 9003, 9083 },
{ 9085, 9114 },
{ 9140, 9179 },
- { 9186, 9203 },
- { 9216, 9254 },
+ { 9186, 9254 },
{ 9280, 9290 },
{ 9372, 9449 },
{ 9472, 9654 },
{ 9656, 9664 },
{ 9666, 9719 },
{ 9728, 9838 },
- { 9840, 9983 },
- { 9985, 10087 },
+ { 9840, 10087 },
{ 10132, 10175 },
{ 10240, 10495 },
{ 11008, 11055 },
{ 11077, 11078 },
- { 11088, 11097 },
+ { 11085, 11123 },
+ { 11126, 11157 },
+ { 11160, 11208 },
+ { 11210, 11262 },
{ 11493, 11498 },
{ 11904, 11929 },
{ 11931, 12019 },
@@ -3596,7 +4429,8 @@ static URange16 So_range16[] = {
{ 12694, 12703 },
{ 12736, 12771 },
{ 12800, 12830 },
- { 12842, 12880 },
+ { 12842, 12871 },
+ { 12880, 12880 },
{ 12896, 12927 },
{ 12938, 12976 },
{ 12992, 13054 },
@@ -3613,65 +4447,71 @@ static URange16 So_range16[] = {
{ 65517, 65518 },
{ 65532, 65533 },
};
-static URange32 So_range32[] = {
- { 65794, 65794 },
+static const URange32 So_range32[] = {
{ 65847, 65855 },
{ 65913, 65929 },
+ { 65932, 65934 },
{ 65936, 65947 },
+ { 65952, 65952 },
{ 66000, 66044 },
+ { 67703, 67704 },
+ { 68296, 68296 },
+ { 71487, 71487 },
+ { 92988, 92991 },
+ { 92997, 92997 },
+ { 113820, 113820 },
{ 118784, 119029 },
{ 119040, 119078 },
{ 119081, 119140 },
{ 119146, 119148 },
{ 119171, 119172 },
{ 119180, 119209 },
- { 119214, 119261 },
+ { 119214, 119272 },
{ 119296, 119361 },
{ 119365, 119365 },
{ 119552, 119638 },
+ { 120832, 121343 },
+ { 121399, 121402 },
+ { 121453, 121460 },
+ { 121462, 121475 },
+ { 121477, 121478 },
+ { 126124, 126124 },
{ 126976, 127019 },
{ 127024, 127123 },
{ 127136, 127150 },
- { 127153, 127166 },
+ { 127153, 127167 },
{ 127169, 127183 },
- { 127185, 127199 },
- { 127248, 127278 },
- { 127280, 127337 },
- { 127344, 127386 },
+ { 127185, 127221 },
+ { 127248, 127339 },
+ { 127344, 127404 },
{ 127462, 127490 },
- { 127504, 127546 },
+ { 127504, 127547 },
{ 127552, 127560 },
{ 127568, 127569 },
- { 127744, 127776 },
- { 127792, 127797 },
- { 127799, 127868 },
- { 127872, 127891 },
- { 127904, 127940 },
- { 127942, 127946 },
- { 127968, 127984 },
- { 128000, 128062 },
- { 128064, 128064 },
- { 128066, 128247 },
- { 128249, 128252 },
- { 128256, 128317 },
- { 128336, 128359 },
- { 128507, 128511 },
- { 128513, 128528 },
- { 128530, 128532 },
- { 128534, 128534 },
- { 128536, 128536 },
- { 128538, 128538 },
- { 128540, 128542 },
- { 128544, 128549 },
- { 128552, 128555 },
- { 128557, 128557 },
- { 128560, 128563 },
- { 128565, 128576 },
- { 128581, 128591 },
- { 128640, 128709 },
+ { 127584, 127589 },
+ { 127744, 127994 },
+ { 128000, 128724 },
+ { 128736, 128748 },
+ { 128752, 128761 },
{ 128768, 128883 },
-};
-static URange16 Sm_range16[] = {
+ { 128896, 128984 },
+ { 129024, 129035 },
+ { 129040, 129095 },
+ { 129104, 129113 },
+ { 129120, 129159 },
+ { 129168, 129197 },
+ { 129280, 129291 },
+ { 129296, 129342 },
+ { 129344, 129392 },
+ { 129395, 129398 },
+ { 129402, 129402 },
+ { 129404, 129442 },
+ { 129456, 129465 },
+ { 129472, 129474 },
+ { 129488, 129535 },
+ { 129632, 129645 },
+};
+static const URange16 Sm_range16[] = {
{ 43, 43 },
{ 60, 62 },
{ 124, 124 },
@@ -3699,7 +4539,6 @@ static URange16 Sm_range16[] = {
{ 8658, 8658 },
{ 8660, 8660 },
{ 8692, 8959 },
- { 8968, 8971 },
{ 8992, 8993 },
{ 9084, 9084 },
{ 9115, 9139 },
@@ -3709,9 +4548,7 @@ static URange16 Sm_range16[] = {
{ 9720, 9727 },
{ 9839, 9839 },
{ 10176, 10180 },
- { 10183, 10186 },
- { 10188, 10188 },
- { 10190, 10213 },
+ { 10183, 10213 },
{ 10224, 10239 },
{ 10496, 10626 },
{ 10649, 10711 },
@@ -3729,7 +4566,7 @@ static URange16 Sm_range16[] = {
{ 65506, 65506 },
{ 65513, 65516 },
};
-static URange32 Sm_range32[] = {
+static const URange32 Sm_range32[] = {
{ 120513, 120513 },
{ 120539, 120539 },
{ 120571, 120571 },
@@ -3740,18 +4577,21 @@ static URange32 Sm_range32[] = {
{ 120713, 120713 },
{ 120745, 120745 },
{ 120771, 120771 },
+ { 126704, 126705 },
};
-static URange16 Sc_range16[] = {
+static const URange16 Sc_range16[] = {
{ 36, 36 },
{ 162, 165 },
+ { 1423, 1423 },
{ 1547, 1547 },
+ { 2046, 2047 },
{ 2546, 2547 },
{ 2555, 2555 },
{ 2801, 2801 },
{ 3065, 3065 },
{ 3647, 3647 },
{ 6107, 6107 },
- { 8352, 8377 },
+ { 8352, 8383 },
{ 43064, 43064 },
{ 65020, 65020 },
{ 65129, 65129 },
@@ -3759,95 +4599,112 @@ static URange16 Sc_range16[] = {
{ 65504, 65505 },
{ 65509, 65510 },
};
-static URange16 Z_range16[] = {
+static const URange32 Sc_range32[] = {
+ { 126128, 126128 },
+};
+static const URange16 Z_range16[] = {
{ 32, 32 },
{ 160, 160 },
{ 5760, 5760 },
- { 6158, 6158 },
{ 8192, 8202 },
{ 8232, 8233 },
{ 8239, 8239 },
{ 8287, 8287 },
{ 12288, 12288 },
};
-static URange16 Zl_range16[] = {
+static const URange16 Zl_range16[] = {
{ 8232, 8232 },
};
-static URange16 Co_range16[] = {
+static const URange16 Co_range16[] = {
{ 57344, 63743 },
};
-static URange32 Co_range32[] = {
+static const URange32 Co_range32[] = {
{ 983040, 1048573 },
{ 1048576, 1114109 },
};
-static URange16 Cc_range16[] = {
+static const URange16 Cc_range16[] = {
{ 0, 31 },
{ 127, 159 },
};
-static URange16 Cf_range16[] = {
+static const URange16 Cf_range16[] = {
{ 173, 173 },
- { 1536, 1539 },
+ { 1536, 1541 },
+ { 1564, 1564 },
{ 1757, 1757 },
{ 1807, 1807 },
- { 6068, 6069 },
+ { 2274, 2274 },
+ { 6158, 6158 },
{ 8203, 8207 },
{ 8234, 8238 },
{ 8288, 8292 },
- { 8298, 8303 },
+ { 8294, 8303 },
{ 65279, 65279 },
{ 65529, 65531 },
};
-static URange32 Cf_range32[] = {
+static const URange32 Cf_range32[] = {
{ 69821, 69821 },
+ { 69837, 69837 },
+ { 113824, 113827 },
{ 119155, 119162 },
{ 917505, 917505 },
{ 917536, 917631 },
};
-static URange16 Cs_range16[] = {
+static const URange16 Cs_range16[] = {
{ 55296, 57343 },
};
-static URange16 Zp_range16[] = {
+static const URange16 Zp_range16[] = {
{ 8233, 8233 },
};
-static URange16 Zs_range16[] = {
+static const URange16 Zs_range16[] = {
{ 32, 32 },
{ 160, 160 },
{ 5760, 5760 },
- { 6158, 6158 },
{ 8192, 8202 },
{ 8239, 8239 },
{ 8287, 8287 },
{ 12288, 12288 },
};
-static URange16 Thaana_range16[] = {
+static const URange32 Tangut_range32[] = {
+ { 94176, 94176 },
+ { 94208, 100337 },
+ { 100352, 101106 },
+};
+static const URange16 Thaana_range16[] = {
{ 1920, 1969 },
};
-static URange16 Telugu_range16[] = {
- { 3073, 3075 },
- { 3077, 3084 },
+static const URange32 Adlam_range32[] = {
+ { 125184, 125258 },
+ { 125264, 125273 },
+ { 125278, 125279 },
+};
+static const URange16 Telugu_range16[] = {
+ { 3072, 3084 },
{ 3086, 3088 },
{ 3090, 3112 },
- { 3114, 3123 },
- { 3125, 3129 },
+ { 3114, 3129 },
{ 3133, 3140 },
{ 3142, 3144 },
{ 3146, 3149 },
{ 3157, 3158 },
- { 3160, 3161 },
+ { 3160, 3162 },
{ 3168, 3171 },
{ 3174, 3183 },
{ 3192, 3199 },
};
-static URange16 Cyrillic_range16[] = {
+static const URange16 Cyrillic_range16[] = {
{ 1024, 1156 },
- { 1159, 1319 },
+ { 1159, 1327 },
+ { 7296, 7304 },
{ 7467, 7467 },
{ 7544, 7544 },
{ 11744, 11775 },
- { 42560, 42611 },
- { 42620, 42647 },
+ { 42560, 42655 },
+ { 65070, 65071 },
+};
+static const URange32 Zanabazar_Square_range32[] = {
+ { 72192, 72263 },
};
-static URange16 Hangul_range16[] = {
+static const URange16 Hangul_range16[] = {
{ 4352, 4607 },
{ 12334, 12335 },
{ 12593, 12686 },
@@ -3863,10 +4720,10 @@ static URange16 Hangul_range16[] = {
{ 65490, 65495 },
{ 65498, 65500 },
};
-static URange32 Old_South_Arabian_range32[] = {
+static const URange32 Old_South_Arabian_range32[] = {
{ 68192, 68223 },
};
-static URange16 Ethiopic_range16[] = {
+static const URange16 Ethiopic_range16[] = {
{ 4608, 4680 },
{ 4682, 4685 },
{ 4688, 4694 },
@@ -3900,35 +4757,55 @@ static URange16 Ethiopic_range16[] = {
{ 43808, 43814 },
{ 43816, 43822 },
};
-static URange16 Inherited_range16[] = {
+static const URange16 Inherited_range16[] = {
{ 768, 879 },
{ 1157, 1158 },
{ 1611, 1621 },
- { 1631, 1631 },
{ 1648, 1648 },
{ 2385, 2386 },
+ { 6832, 6846 },
{ 7376, 7378 },
{ 7380, 7392 },
{ 7394, 7400 },
{ 7405, 7405 },
- { 7616, 7654 },
- { 7676, 7679 },
+ { 7412, 7412 },
+ { 7416, 7417 },
+ { 7616, 7673 },
+ { 7675, 7679 },
{ 8204, 8205 },
{ 8400, 8432 },
{ 12330, 12333 },
{ 12441, 12442 },
{ 65024, 65039 },
- { 65056, 65062 },
+ { 65056, 65069 },
};
-static URange32 Inherited_range32[] = {
+static const URange32 Inherited_range32[] = {
{ 66045, 66045 },
+ { 66272, 66272 },
+ { 70459, 70459 },
{ 119143, 119145 },
{ 119163, 119170 },
{ 119173, 119179 },
{ 119210, 119213 },
{ 917760, 917999 },
};
-static URange16 Han_range16[] = {
+static const URange32 Meroitic_Cursive_range32[] = {
+ { 68000, 68023 },
+ { 68028, 68047 },
+ { 68050, 68095 },
+};
+static const URange32 Bhaiksuki_range32[] = {
+ { 72704, 72712 },
+ { 72714, 72758 },
+ { 72760, 72773 },
+ { 72784, 72812 },
+};
+static const URange32 Ahom_range32[] = {
+ { 71424, 71450 },
+ { 71453, 71467 },
+ { 71472, 71487 },
+};
+static const URange16 Han_range16[] = {
{ 11904, 11929 },
{ 11931, 12019 },
{ 12032, 12245 },
@@ -3937,25 +4814,29 @@ static URange16 Han_range16[] = {
{ 12321, 12329 },
{ 12344, 12347 },
{ 13312, 19893 },
- { 19968, 40907 },
- { 63744, 64045 },
- { 64048, 64109 },
+ { 19968, 40943 },
+ { 63744, 64109 },
{ 64112, 64217 },
};
-static URange32 Han_range32[] = {
+static const URange32 Han_range32[] = {
{ 131072, 173782 },
{ 173824, 177972 },
{ 177984, 178205 },
+ { 178208, 183969 },
+ { 183984, 191456 },
{ 194560, 195101 },
};
-static URange16 Armenian_range16[] = {
+static const URange32 Old_North_Arabian_range32[] = {
+ { 68224, 68255 },
+};
+static const URange16 Armenian_range16[] = {
{ 1329, 1366 },
- { 1369, 1375 },
- { 1377, 1415 },
+ { 1369, 1416 },
{ 1418, 1418 },
+ { 1421, 1423 },
{ 64275, 64279 },
};
-static URange16 Tamil_range16[] = {
+static const URange16 Tamil_range16[] = {
{ 2946, 2947 },
{ 2949, 2954 },
{ 2958, 2960 },
@@ -3973,89 +4854,119 @@ static URange16 Tamil_range16[] = {
{ 3031, 3031 },
{ 3046, 3066 },
};
-static URange16 Bopomofo_range16[] = {
+static const URange16 Bopomofo_range16[] = {
{ 746, 747 },
- { 12549, 12589 },
+ { 12549, 12591 },
{ 12704, 12730 },
};
-static URange16 Sundanese_range16[] = {
- { 7040, 7082 },
- { 7086, 7097 },
+static const URange32 Bassa_Vah_range32[] = {
+ { 92880, 92909 },
+ { 92912, 92917 },
+};
+static const URange16 Sundanese_range16[] = {
+ { 7040, 7103 },
+ { 7360, 7367 },
+};
+static const URange32 Osage_range32[] = {
+ { 66736, 66771 },
+ { 66776, 66811 },
+};
+static const URange32 Old_Sogdian_range32[] = {
+ { 69376, 69415 },
};
-static URange16 Tagalog_range16[] = {
+static const URange16 Tagalog_range16[] = {
{ 5888, 5900 },
{ 5902, 5908 },
};
-static URange16 Malayalam_range16[] = {
- { 3330, 3331 },
+static const URange16 Malayalam_range16[] = {
+ { 3328, 3331 },
{ 3333, 3340 },
{ 3342, 3344 },
- { 3346, 3386 },
- { 3389, 3396 },
+ { 3346, 3396 },
{ 3398, 3400 },
- { 3402, 3406 },
- { 3415, 3415 },
- { 3424, 3427 },
- { 3430, 3445 },
- { 3449, 3455 },
+ { 3402, 3407 },
+ { 3412, 3427 },
+ { 3430, 3455 },
};
-static URange32 Carian_range32[] = {
+static const URange32 Marchen_range32[] = {
+ { 72816, 72847 },
+ { 72850, 72871 },
+ { 72873, 72886 },
+};
+static const URange32 Carian_range32[] = {
{ 66208, 66256 },
};
-static URange16 Hiragana_range16[] = {
+static const URange16 Hiragana_range16[] = {
{ 12353, 12438 },
{ 12445, 12447 },
};
-static URange32 Hiragana_range32[] = {
- { 110593, 110593 },
+static const URange32 Hiragana_range32[] = {
+ { 110593, 110878 },
{ 127488, 127488 },
};
-static URange16 Tagbanwa_range16[] = {
+static const URange16 Tagbanwa_range16[] = {
{ 5984, 5996 },
{ 5998, 6000 },
{ 6002, 6003 },
};
-static URange16 Meetei_Mayek_range16[] = {
+static const URange16 Meetei_Mayek_range16[] = {
+ { 43744, 43766 },
{ 43968, 44013 },
{ 44016, 44025 },
};
-static URange16 Tai_Le_range16[] = {
+static const URange32 Hanifi_Rohingya_range32[] = {
+ { 68864, 68903 },
+ { 68912, 68921 },
+};
+static const URange32 Pahawh_Hmong_range32[] = {
+ { 92928, 92997 },
+ { 93008, 93017 },
+ { 93019, 93025 },
+ { 93027, 93047 },
+ { 93053, 93071 },
+};
+static const URange16 Tai_Le_range16[] = {
{ 6480, 6509 },
{ 6512, 6516 },
};
-static URange16 Kayah_Li_range16[] = {
- { 43264, 43311 },
+static const URange16 Kayah_Li_range16[] = {
+ { 43264, 43309 },
+ { 43311, 43311 },
};
-static URange16 Buginese_range16[] = {
+static const URange16 Buginese_range16[] = {
{ 6656, 6683 },
{ 6686, 6687 },
};
-static URange32 Kharoshthi_range32[] = {
+static const URange32 Kharoshthi_range32[] = {
{ 68096, 68099 },
{ 68101, 68102 },
{ 68108, 68115 },
{ 68117, 68119 },
- { 68121, 68147 },
+ { 68121, 68149 },
{ 68152, 68154 },
- { 68159, 68167 },
+ { 68159, 68168 },
{ 68176, 68184 },
};
-static URange16 Tai_Tham_range16[] = {
+static const URange16 Tai_Tham_range16[] = {
{ 6688, 6750 },
{ 6752, 6780 },
{ 6783, 6793 },
{ 6800, 6809 },
{ 6816, 6829 },
};
-static URange32 Old_Italic_range32[] = {
- { 66304, 66334 },
- { 66336, 66339 },
+static const URange32 Old_Italic_range32[] = {
+ { 66304, 66339 },
+ { 66349, 66351 },
};
-static URange32 Old_Persian_range32[] = {
+static const URange32 Old_Persian_range32[] = {
{ 66464, 66499 },
{ 66504, 66517 },
};
-static URange16 Latin_range16[] = {
+static const URange32 Warang_Citi_range32[] = {
+ { 71840, 71922 },
+ { 71935, 71935 },
+};
+static const URange16 Latin_range16[] = {
{ 65, 90 },
{ 97, 122 },
{ 170, 170 },
@@ -4079,44 +4990,72 @@ static URange16 Latin_range16[] = {
{ 8544, 8584 },
{ 11360, 11391 },
{ 42786, 42887 },
- { 42891, 42894 },
- { 42896, 42897 },
- { 42912, 42921 },
- { 43002, 43007 },
+ { 42891, 42937 },
+ { 42999, 43007 },
+ { 43824, 43866 },
+ { 43868, 43876 },
{ 64256, 64262 },
{ 65313, 65338 },
{ 65345, 65370 },
};
-static URange16 Saurashtra_range16[] = {
- { 43136, 43204 },
+static const URange16 Saurashtra_range16[] = {
+ { 43136, 43205 },
{ 43214, 43225 },
};
-static URange32 Shavian_range32[] = {
+static const URange32 Shavian_range32[] = {
{ 66640, 66687 },
};
-static URange16 Georgian_range16[] = {
+static const URange16 Georgian_range16[] = {
{ 4256, 4293 },
+ { 4295, 4295 },
+ { 4301, 4301 },
{ 4304, 4346 },
- { 4348, 4348 },
+ { 4348, 4351 },
+ { 7312, 7354 },
+ { 7357, 7359 },
{ 11520, 11557 },
-};
-static URange16 Batak_range16[] = {
+ { 11559, 11559 },
+ { 11565, 11565 },
+};
+static const URange32 Grantha_range32[] = {
+ { 70400, 70403 },
+ { 70405, 70412 },
+ { 70415, 70416 },
+ { 70419, 70440 },
+ { 70442, 70448 },
+ { 70450, 70451 },
+ { 70453, 70457 },
+ { 70460, 70468 },
+ { 70471, 70472 },
+ { 70475, 70477 },
+ { 70480, 70480 },
+ { 70487, 70487 },
+ { 70493, 70499 },
+ { 70502, 70508 },
+ { 70512, 70516 },
+};
+static const URange32 Duployan_range32[] = {
+ { 113664, 113770 },
+ { 113776, 113788 },
+ { 113792, 113800 },
+ { 113808, 113817 },
+ { 113820, 113823 },
+};
+static const URange16 Batak_range16[] = {
{ 7104, 7155 },
{ 7164, 7167 },
};
-static URange16 Devanagari_range16[] = {
+static const URange16 Devanagari_range16[] = {
{ 2304, 2384 },
{ 2387, 2403 },
- { 2406, 2415 },
- { 2417, 2423 },
- { 2425, 2431 },
- { 43232, 43259 },
+ { 2406, 2431 },
+ { 43232, 43263 },
};
-static URange16 Thai_range16[] = {
+static const URange16 Thai_range16[] = {
{ 3585, 3642 },
{ 3648, 3675 },
};
-static URange16 Tibetan_range16[] = {
+static const URange16 Tibetan_range16[] = {
{ 3840, 3911 },
{ 3913, 3948 },
{ 3953, 3991 },
@@ -4125,22 +5064,26 @@ static URange16 Tibetan_range16[] = {
{ 4046, 4052 },
{ 4057, 4058 },
};
-static URange16 Tifinagh_range16[] = {
- { 11568, 11621 },
+static const URange16 Tifinagh_range16[] = {
+ { 11568, 11623 },
{ 11631, 11632 },
{ 11647, 11647 },
};
-static URange32 Ugaritic_range32[] = {
+static const URange32 Ugaritic_range32[] = {
{ 66432, 66461 },
{ 66463, 66463 },
};
-static URange16 Braille_range16[] = {
+static const URange16 Braille_range16[] = {
{ 10240, 10495 },
};
-static URange16 Greek_range16[] = {
+static const URange32 Anatolian_Hieroglyphs_range32[] = {
+ { 82944, 83526 },
+};
+static const URange16 Greek_range16[] = {
{ 880, 883 },
{ 885, 887 },
{ 890, 893 },
+ { 895, 895 },
{ 900, 900 },
{ 902, 902 },
{ 904, 906 },
@@ -4169,47 +5112,57 @@ static URange16 Greek_range16[] = {
{ 8178, 8180 },
{ 8182, 8190 },
{ 8486, 8486 },
+ { 43877, 43877 },
};
-static URange32 Greek_range32[] = {
- { 65856, 65930 },
+static const URange32 Greek_range32[] = {
+ { 65856, 65934 },
+ { 65952, 65952 },
{ 119296, 119365 },
};
-static URange32 Lycian_range32[] = {
+static const URange32 Lycian_range32[] = {
{ 66176, 66204 },
};
-static URange16 Tai_Viet_range16[] = {
+static const URange32 Mende_Kikakui_range32[] = {
+ { 124928, 125124 },
+ { 125127, 125142 },
+};
+static const URange16 Tai_Viet_range16[] = {
{ 43648, 43714 },
{ 43739, 43743 },
};
-static URange16 Vai_range16[] = {
+static const URange16 Vai_range16[] = {
{ 42240, 42539 },
};
-static URange16 Ogham_range16[] = {
+static const URange16 Ogham_range16[] = {
{ 5760, 5788 },
};
-static URange32 Inscriptional_Parthian_range32[] = {
+static const URange32 Inscriptional_Parthian_range32[] = {
{ 68416, 68437 },
{ 68440, 68447 },
};
-static URange16 Cham_range16[] = {
+static const URange16 Cham_range16[] = {
{ 43520, 43574 },
{ 43584, 43597 },
{ 43600, 43609 },
{ 43612, 43615 },
};
-static URange16 Syriac_range16[] = {
+static const URange16 Syriac_range16[] = {
{ 1792, 1805 },
{ 1807, 1866 },
{ 1869, 1871 },
+ { 2144, 2154 },
};
-static URange16 Runic_range16[] = {
+static const URange16 Runic_range16[] = {
{ 5792, 5866 },
- { 5870, 5872 },
+ { 5870, 5880 },
};
-static URange32 Gothic_range32[] = {
+static const URange32 Gothic_range32[] = {
{ 66352, 66378 },
};
-static URange16 Katakana_range16[] = {
+static const URange32 Mahajani_range32[] = {
+ { 69968, 70006 },
+};
+static const URange16 Katakana_range16[] = {
{ 12449, 12538 },
{ 12541, 12543 },
{ 12784, 12799 },
@@ -4218,33 +5171,48 @@ static URange16 Katakana_range16[] = {
{ 65382, 65391 },
{ 65393, 65437 },
};
-static URange32 Katakana_range32[] = {
+static const URange32 Katakana_range32[] = {
{ 110592, 110592 },
};
-static URange32 Osmanya_range32[] = {
+static const URange32 Osmanya_range32[] = {
{ 66688, 66717 },
{ 66720, 66729 },
};
-static URange16 New_Tai_Lue_range16[] = {
+static const URange16 New_Tai_Lue_range16[] = {
{ 6528, 6571 },
{ 6576, 6601 },
{ 6608, 6618 },
{ 6622, 6623 },
};
-static URange16 Ol_Chiki_range16[] = {
+static const URange16 Ol_Chiki_range16[] = {
{ 7248, 7295 },
};
-static URange16 Limbu_range16[] = {
- { 6400, 6428 },
+static const URange32 Newa_range32[] = {
+ { 70656, 70745 },
+ { 70747, 70747 },
+ { 70749, 70750 },
+};
+static const URange16 Limbu_range16[] = {
+ { 6400, 6430 },
{ 6432, 6443 },
{ 6448, 6459 },
{ 6464, 6464 },
{ 6468, 6479 },
};
-static URange16 Cherokee_range16[] = {
- { 5024, 5108 },
+static const URange32 Pau_Cin_Hau_range32[] = {
+ { 72384, 72440 },
+};
+static const URange16 Cherokee_range16[] = {
+ { 5024, 5109 },
+ { 5112, 5117 },
+ { 43888, 43967 },
+};
+static const URange32 Miao_range32[] = {
+ { 93952, 94020 },
+ { 94032, 94078 },
+ { 94095, 94111 },
};
-static URange16 Oriya_range16[] = {
+static const URange16 Oriya_range16[] = {
{ 2817, 2819 },
{ 2821, 2828 },
{ 2831, 2832 },
@@ -4260,7 +5228,14 @@ static URange16 Oriya_range16[] = {
{ 2911, 2915 },
{ 2918, 2935 },
};
-static URange16 Gujarati_range16[] = {
+static const URange32 Medefaidrin_range32[] = {
+ { 93760, 93850 },
+};
+static const URange32 Sharada_range32[] = {
+ { 70016, 70093 },
+ { 70096, 70111 },
+};
+static const URange16 Gujarati_range16[] = {
{ 2689, 2691 },
{ 2693, 2701 },
{ 2703, 2705 },
@@ -4273,44 +5248,82 @@ static URange16 Gujarati_range16[] = {
{ 2763, 2765 },
{ 2768, 2768 },
{ 2784, 2787 },
- { 2790, 2799 },
- { 2801, 2801 },
-};
-static URange32 Inscriptional_Pahlavi_range32[] = {
+ { 2790, 2801 },
+ { 2809, 2815 },
+};
+static const URange32 Nushu_range32[] = {
+ { 94177, 94177 },
+ { 110960, 111355 },
+};
+static const URange32 Modi_range32[] = {
+ { 71168, 71236 },
+ { 71248, 71257 },
+};
+static const URange32 Gunjala_Gondi_range32[] = {
+ { 73056, 73061 },
+ { 73063, 73064 },
+ { 73066, 73102 },
+ { 73104, 73105 },
+ { 73107, 73112 },
+ { 73120, 73129 },
+};
+static const URange32 Inscriptional_Pahlavi_range32[] = {
{ 68448, 68466 },
{ 68472, 68479 },
};
-static URange16 Khmer_range16[] = {
+static const URange32 Manichaean_range32[] = {
+ { 68288, 68326 },
+ { 68331, 68342 },
+};
+static const URange16 Khmer_range16[] = {
{ 6016, 6109 },
{ 6112, 6121 },
{ 6128, 6137 },
{ 6624, 6655 },
};
-static URange32 Cuneiform_range32[] = {
- { 73728, 74606 },
- { 74752, 74850 },
- { 74864, 74867 },
+static const URange32 Cuneiform_range32[] = {
+ { 73728, 74649 },
+ { 74752, 74862 },
+ { 74864, 74868 },
+ { 74880, 75075 },
+};
+static const URange32 Khudawadi_range32[] = {
+ { 70320, 70378 },
+ { 70384, 70393 },
};
-static URange16 Mandaic_range16[] = {
+static const URange16 Mandaic_range16[] = {
{ 2112, 2139 },
{ 2142, 2142 },
};
-static URange16 Syloti_Nagri_range16[] = {
+static const URange32 Hatran_range32[] = {
+ { 67808, 67826 },
+ { 67828, 67829 },
+ { 67835, 67839 },
+};
+static const URange16 Syloti_Nagri_range16[] = {
{ 43008, 43051 },
};
-static URange16 Nko_range16[] = {
+static const URange16 Nko_range16[] = {
{ 1984, 2042 },
+ { 2045, 2047 },
};
-static URange16 Canadian_Aboriginal_range16[] = {
+static const URange16 Canadian_Aboriginal_range16[] = {
{ 5120, 5759 },
{ 6320, 6389 },
};
-static URange32 Phoenician_range32[] = {
+static const URange32 Meroitic_Hieroglyphs_range32[] = {
+ { 67968, 67999 },
+};
+static const URange32 Phoenician_range32[] = {
{ 67840, 67867 },
{ 67871, 67871 },
};
-static URange16 Bengali_range16[] = {
- { 2433, 2435 },
+static const URange32 Nabataean_range32[] = {
+ { 67712, 67742 },
+ { 67751, 67759 },
+};
+static const URange16 Bengali_range16[] = {
+ { 2432, 2435 },
{ 2437, 2444 },
{ 2447, 2448 },
{ 2451, 2472 },
@@ -4323,20 +5336,32 @@ static URange16 Bengali_range16[] = {
{ 2519, 2519 },
{ 2524, 2525 },
{ 2527, 2531 },
- { 2534, 2555 },
+ { 2534, 2558 },
};
-static URange32 Kaithi_range32[] = {
+static const URange32 Kaithi_range32[] = {
{ 69760, 69825 },
+ { 69837, 69837 },
};
-static URange16 Glagolitic_range16[] = {
+static const URange16 Glagolitic_range16[] = {
{ 11264, 11310 },
{ 11312, 11358 },
};
-static URange32 Imperial_Aramaic_range32[] = {
+static const URange32 Glagolitic_range32[] = {
+ { 122880, 122886 },
+ { 122888, 122904 },
+ { 122907, 122913 },
+ { 122915, 122916 },
+ { 122918, 122922 },
+};
+static const URange32 Imperial_Aramaic_range32[] = {
{ 67648, 67669 },
{ 67671, 67679 },
};
-static URange16 Gurmukhi_range16[] = {
+static const URange32 Sora_Sompeng_range32[] = {
+ { 69840, 69864 },
+ { 69872, 69881 },
+};
+static const URange16 Gurmukhi_range16[] = {
{ 2561, 2563 },
{ 2565, 2570 },
{ 2575, 2576 },
@@ -4352,17 +5377,20 @@ static URange16 Gurmukhi_range16[] = {
{ 2641, 2641 },
{ 2649, 2652 },
{ 2654, 2654 },
- { 2662, 2677 },
+ { 2662, 2678 },
};
-static URange16 Javanese_range16[] = {
+static const URange16 Javanese_range16[] = {
{ 43392, 43469 },
- { 43471, 43481 },
+ { 43472, 43481 },
{ 43486, 43487 },
};
-static URange16 Phags_Pa_range16[] = {
+static const URange32 Old_Permic_range32[] = {
+ { 66384, 66426 },
+};
+static const URange16 Phags_Pa_range16[] = {
{ 43072, 43127 },
};
-static URange32 Cypriot_range32[] = {
+static const URange32 Cypriot_range32[] = {
{ 67584, 67589 },
{ 67592, 67592 },
{ 67594, 67637 },
@@ -4370,9 +5398,8 @@ static URange32 Cypriot_range32[] = {
{ 67644, 67644 },
{ 67647, 67647 },
};
-static URange16 Kannada_range16[] = {
- { 3202, 3203 },
- { 3205, 3212 },
+static const URange16 Kannada_range16[] = {
+ { 3200, 3212 },
{ 3214, 3216 },
{ 3218, 3240 },
{ 3242, 3251 },
@@ -4386,15 +5413,22 @@ static URange16 Kannada_range16[] = {
{ 3302, 3311 },
{ 3313, 3314 },
};
-static URange16 Mongolian_range16[] = {
+static const URange32 Khojki_range32[] = {
+ { 70144, 70161 },
+ { 70163, 70206 },
+};
+static const URange16 Mongolian_range16[] = {
{ 6144, 6145 },
{ 6148, 6148 },
{ 6150, 6158 },
{ 6160, 6169 },
- { 6176, 6263 },
+ { 6176, 6264 },
{ 6272, 6314 },
};
-static URange16 Sinhala_range16[] = {
+static const URange32 Mongolian_range32[] = {
+ { 71264, 71276 },
+};
+static const URange16 Sinhala_range16[] = {
{ 3458, 3459 },
{ 3461, 3478 },
{ 3482, 3505 },
@@ -4405,28 +5439,48 @@ static URange16 Sinhala_range16[] = {
{ 3535, 3540 },
{ 3542, 3542 },
{ 3544, 3551 },
+ { 3558, 3567 },
{ 3570, 3572 },
};
-static URange32 Brahmi_range32[] = {
+static const URange32 Sinhala_range32[] = {
+ { 70113, 70132 },
+};
+static const URange32 Brahmi_range32[] = {
{ 69632, 69709 },
{ 69714, 69743 },
+ { 69759, 69759 },
+};
+static const URange32 Elbasan_range32[] = {
+ { 66816, 66855 },
};
-static URange32 Deseret_range32[] = {
+static const URange32 Deseret_range32[] = {
{ 66560, 66639 },
};
-static URange16 Rejang_range16[] = {
+static const URange16 Rejang_range16[] = {
{ 43312, 43347 },
{ 43359, 43359 },
};
-static URange16 Yi_range16[] = {
+static const URange32 SignWriting_range32[] = {
+ { 120832, 121483 },
+ { 121499, 121503 },
+ { 121505, 121519 },
+};
+static const URange32 Multani_range32[] = {
+ { 70272, 70278 },
+ { 70280, 70280 },
+ { 70282, 70285 },
+ { 70287, 70301 },
+ { 70303, 70313 },
+};
+static const URange16 Yi_range16[] = {
{ 40960, 42124 },
{ 42128, 42182 },
};
-static URange16 Balinese_range16[] = {
+static const URange16 Balinese_range16[] = {
{ 6912, 6987 },
{ 6992, 7036 },
};
-static URange16 Lao_range16[] = {
+static const URange16 Lao_range16[] = {
{ 3713, 3714 },
{ 3716, 3716 },
{ 3719, 3720 },
@@ -4444,12 +5498,21 @@ static URange16 Lao_range16[] = {
{ 3782, 3782 },
{ 3784, 3789 },
{ 3792, 3801 },
- { 3804, 3805 },
+ { 3804, 3807 },
};
-static URange16 Hanunoo_range16[] = {
+static const URange16 Hanunoo_range16[] = {
{ 5920, 5940 },
};
-static URange32 Linear_B_range32[] = {
+static const URange32 Masaram_Gondi_range32[] = {
+ { 72960, 72966 },
+ { 72968, 72969 },
+ { 72971, 73014 },
+ { 73018, 73018 },
+ { 73020, 73021 },
+ { 73023, 73031 },
+ { 73040, 73049 },
+};
+static const URange32 Linear_B_range32[] = {
{ 65536, 65547 },
{ 65549, 65574 },
{ 65576, 65594 },
@@ -4458,32 +5521,67 @@ static URange32 Linear_B_range32[] = {
{ 65616, 65629 },
{ 65664, 65786 },
};
-static URange32 Old_Turkic_range32[] = {
+static const URange32 Linear_A_range32[] = {
+ { 67072, 67382 },
+ { 67392, 67413 },
+ { 67424, 67431 },
+};
+static const URange32 Dogra_range32[] = {
+ { 71680, 71739 },
+};
+static const URange32 Old_Turkic_range32[] = {
{ 68608, 68680 },
};
-static URange16 Lepcha_range16[] = {
+static const URange16 Lepcha_range16[] = {
{ 7168, 7223 },
{ 7227, 7241 },
{ 7245, 7247 },
};
-static URange32 Lydian_range32[] = {
+static const URange32 Lydian_range32[] = {
{ 67872, 67897 },
{ 67903, 67903 },
};
-static URange32 Egyptian_Hieroglyphs_range32[] = {
+static const URange32 Egyptian_Hieroglyphs_range32[] = {
{ 77824, 78894 },
};
-static URange16 Samaritan_range16[] = {
+static const URange32 Sogdian_range32[] = {
+ { 69424, 69465 },
+};
+static const URange32 Caucasian_Albanian_range32[] = {
+ { 66864, 66915 },
+ { 66927, 66927 },
+};
+static const URange32 Makasar_range32[] = {
+ { 73440, 73464 },
+};
+static const URange32 Old_Hungarian_range32[] = {
+ { 68736, 68786 },
+ { 68800, 68850 },
+ { 68858, 68863 },
+};
+static const URange16 Samaritan_range16[] = {
{ 2048, 2093 },
{ 2096, 2110 },
};
-static URange16 Lisu_range16[] = {
+static const URange16 Lisu_range16[] = {
{ 42192, 42239 },
};
-static URange16 Buhid_range16[] = {
+static const URange16 Buhid_range16[] = {
{ 5952, 5971 },
};
-static URange16 Common_range16[] = {
+static const URange32 Palmyrene_range32[] = {
+ { 67680, 67711 },
+};
+static const URange32 Tirhuta_range32[] = {
+ { 70784, 70855 },
+ { 70864, 70873 },
+};
+static const URange32 Mro_range32[] = {
+ { 92736, 92766 },
+ { 92768, 92777 },
+ { 92782, 92783 },
+};
+static const URange16 Common_range16[] = {
{ 0, 64 },
{ 91, 96 },
{ 123, 169 },
@@ -4499,14 +5597,14 @@ static URange16 Common_range16[] = {
{ 901, 901 },
{ 903, 903 },
{ 1417, 1417 },
+ { 1541, 1541 },
{ 1548, 1548 },
{ 1563, 1563 },
{ 1567, 1567 },
{ 1600, 1600 },
- { 1632, 1641 },
{ 1757, 1757 },
+ { 2274, 2274 },
{ 2404, 2405 },
- { 2416, 2416 },
{ 3647, 3647 },
{ 4053, 4056 },
{ 4347, 4347 },
@@ -4517,29 +5615,28 @@ static URange16 Common_range16[] = {
{ 7379, 7379 },
{ 7393, 7393 },
{ 7401, 7404 },
- { 7406, 7410 },
+ { 7406, 7411 },
+ { 7413, 7415 },
{ 8192, 8203 },
{ 8206, 8292 },
- { 8298, 8304 },
+ { 8294, 8304 },
{ 8308, 8318 },
{ 8320, 8334 },
- { 8352, 8377 },
+ { 8352, 8383 },
{ 8448, 8485 },
{ 8487, 8489 },
{ 8492, 8497 },
{ 8499, 8525 },
{ 8527, 8543 },
- { 8585, 8585 },
- { 8592, 9203 },
- { 9216, 9254 },
+ { 8585, 8587 },
+ { 8592, 9254 },
{ 9280, 9290 },
- { 9312, 9983 },
- { 9985, 10186 },
- { 10188, 10188 },
- { 10190, 10239 },
- { 10496, 11084 },
- { 11088, 11097 },
- { 11776, 11825 },
+ { 9312, 10239 },
+ { 10496, 11123 },
+ { 11126, 11157 },
+ { 11160, 11208 },
+ { 11210, 11262 },
+ { 11776, 11854 },
{ 12272, 12283 },
{ 12288, 12292 },
{ 12294, 12294 },
@@ -4558,8 +5655,10 @@ static URange16 Common_range16[] = {
{ 42752, 42785 },
{ 42888, 42890 },
{ 43056, 43065 },
+ { 43310, 43310 },
+ { 43471, 43471 },
+ { 43867, 43867 },
{ 64830, 64831 },
- { 65021, 65021 },
{ 65040, 65049 },
{ 65072, 65106 },
{ 65108, 65126 },
@@ -4574,21 +5673,24 @@ static URange16 Common_range16[] = {
{ 65512, 65518 },
{ 65529, 65533 },
};
-static URange32 Common_range32[] = {
+static const URange32 Common_range32[] = {
{ 65792, 65794 },
{ 65799, 65843 },
{ 65847, 65855 },
{ 65936, 65947 },
{ 66000, 66044 },
+ { 66273, 66299 },
+ { 113824, 113827 },
{ 118784, 119029 },
{ 119040, 119078 },
{ 119081, 119142 },
{ 119146, 119162 },
{ 119171, 119172 },
{ 119180, 119209 },
- { 119214, 119261 },
+ { 119214, 119272 },
+ { 119520, 119539 },
{ 119552, 119638 },
- { 119648, 119665 },
+ { 119648, 119672 },
{ 119808, 119892 },
{ 119894, 119964 },
{ 119966, 119967 },
@@ -4610,98 +5712,142 @@ static URange32 Common_range32[] = {
{ 120146, 120485 },
{ 120488, 120779 },
{ 120782, 120831 },
+ { 126065, 126132 },
{ 126976, 127019 },
{ 127024, 127123 },
{ 127136, 127150 },
- { 127153, 127166 },
+ { 127153, 127167 },
{ 127169, 127183 },
- { 127185, 127199 },
- { 127232, 127242 },
- { 127248, 127278 },
- { 127280, 127337 },
- { 127344, 127386 },
+ { 127185, 127221 },
+ { 127232, 127244 },
+ { 127248, 127339 },
+ { 127344, 127404 },
{ 127462, 127487 },
{ 127489, 127490 },
- { 127504, 127546 },
+ { 127504, 127547 },
{ 127552, 127560 },
{ 127568, 127569 },
- { 127744, 127776 },
- { 127792, 127797 },
- { 127799, 127868 },
- { 127872, 127891 },
- { 127904, 127940 },
- { 127942, 127946 },
- { 127968, 127984 },
- { 128000, 128062 },
- { 128064, 128064 },
- { 128066, 128247 },
- { 128249, 128252 },
- { 128256, 128317 },
- { 128336, 128359 },
- { 128507, 128511 },
- { 128513, 128528 },
- { 128530, 128532 },
- { 128534, 128534 },
- { 128536, 128536 },
- { 128538, 128538 },
- { 128540, 128542 },
- { 128544, 128549 },
- { 128552, 128555 },
- { 128557, 128557 },
- { 128560, 128563 },
- { 128565, 128576 },
- { 128581, 128591 },
- { 128640, 128709 },
+ { 127584, 127589 },
+ { 127744, 128724 },
+ { 128736, 128748 },
+ { 128752, 128761 },
{ 128768, 128883 },
+ { 128896, 128984 },
+ { 129024, 129035 },
+ { 129040, 129095 },
+ { 129104, 129113 },
+ { 129120, 129159 },
+ { 129168, 129197 },
+ { 129280, 129291 },
+ { 129296, 129342 },
+ { 129344, 129392 },
+ { 129395, 129398 },
+ { 129402, 129402 },
+ { 129404, 129442 },
+ { 129456, 129465 },
+ { 129472, 129474 },
+ { 129488, 129535 },
+ { 129632, 129645 },
{ 917505, 917505 },
{ 917536, 917631 },
};
-static URange16 Coptic_range16[] = {
+static const URange16 Coptic_range16[] = {
{ 994, 1007 },
- { 11392, 11505 },
+ { 11392, 11507 },
{ 11513, 11519 },
};
-static URange16 Arabic_range16[] = {
- { 1536, 1539 },
+static const URange32 Chakma_range32[] = {
+ { 69888, 69940 },
+ { 69942, 69958 },
+};
+static const URange16 Arabic_range16[] = {
+ { 1536, 1540 },
{ 1542, 1547 },
{ 1549, 1562 },
+ { 1564, 1564 },
{ 1566, 1566 },
{ 1568, 1599 },
{ 1601, 1610 },
- { 1622, 1630 },
- { 1642, 1647 },
+ { 1622, 1647 },
{ 1649, 1756 },
{ 1758, 1791 },
{ 1872, 1919 },
+ { 2208, 2228 },
+ { 2230, 2237 },
+ { 2259, 2273 },
+ { 2275, 2303 },
{ 64336, 64449 },
{ 64467, 64829 },
{ 64848, 64911 },
{ 64914, 64967 },
- { 65008, 65020 },
+ { 65008, 65021 },
{ 65136, 65140 },
{ 65142, 65276 },
};
-static URange32 Arabic_range32[] = {
+static const URange32 Arabic_range32[] = {
{ 69216, 69246 },
-};
-static URange16 Bamum_range16[] = {
+ { 126464, 126467 },
+ { 126469, 126495 },
+ { 126497, 126498 },
+ { 126500, 126500 },
+ { 126503, 126503 },
+ { 126505, 126514 },
+ { 126516, 126519 },
+ { 126521, 126521 },
+ { 126523, 126523 },
+ { 126530, 126530 },
+ { 126535, 126535 },
+ { 126537, 126537 },
+ { 126539, 126539 },
+ { 126541, 126543 },
+ { 126545, 126546 },
+ { 126548, 126548 },
+ { 126551, 126551 },
+ { 126553, 126553 },
+ { 126555, 126555 },
+ { 126557, 126557 },
+ { 126559, 126559 },
+ { 126561, 126562 },
+ { 126564, 126564 },
+ { 126567, 126570 },
+ { 126572, 126578 },
+ { 126580, 126583 },
+ { 126585, 126588 },
+ { 126590, 126590 },
+ { 126592, 126601 },
+ { 126603, 126619 },
+ { 126625, 126627 },
+ { 126629, 126633 },
+ { 126635, 126651 },
+ { 126704, 126705 },
+};
+static const URange16 Bamum_range16[] = {
{ 42656, 42743 },
};
-static URange32 Bamum_range32[] = {
+static const URange32 Bamum_range32[] = {
{ 92160, 92728 },
};
-static URange16 Myanmar_range16[] = {
+static const URange16 Myanmar_range16[] = {
{ 4096, 4255 },
- { 43616, 43643 },
+ { 43488, 43518 },
+ { 43616, 43647 },
+};
+static const URange32 Siddham_range32[] = {
+ { 71040, 71093 },
+ { 71096, 71133 },
+};
+static const URange32 Soyombo_range32[] = {
+ { 72272, 72323 },
+ { 72326, 72354 },
};
-static URange32 Avestan_range32[] = {
+static const URange32 Avestan_range32[] = {
{ 68352, 68405 },
{ 68409, 68415 },
};
-static URange16 Hebrew_range16[] = {
+static const URange16 Hebrew_range16[] = {
{ 1425, 1479 },
{ 1488, 1514 },
- { 1520, 1524 },
+ { 1519, 1524 },
{ 64285, 64310 },
{ 64312, 64316 },
{ 64318, 64318 },
@@ -4709,141 +5855,203 @@ static URange16 Hebrew_range16[] = {
{ 64323, 64324 },
{ 64326, 64335 },
};
-// 3804 16-bit ranges, 582 32-bit ranges
-UGroup unicode_groups[] = {
- { "Arabic", +1, Arabic_range16, 18, Arabic_range32, 1 },
+static const URange32 Psalter_Pahlavi_range32[] = {
+ { 68480, 68497 },
+ { 68505, 68508 },
+ { 68521, 68527 },
+};
+static const URange32 Takri_range32[] = {
+ { 71296, 71351 },
+ { 71360, 71369 },
+};
+// 3994 16-bit ranges, 1429 32-bit ranges
+const UGroup unicode_groups[] = {
+ { "Adlam", +1, 0, 0, Adlam_range32, 3 },
+ { "Ahom", +1, 0, 0, Ahom_range32, 3 },
+ { "Anatolian_Hieroglyphs", +1, 0, 0, Anatolian_Hieroglyphs_range32, 1 },
+ { "Arabic", +1, Arabic_range16, 22, Arabic_range32, 35 },
{ "Armenian", +1, Armenian_range16, 5, 0, 0 },
{ "Avestan", +1, 0, 0, Avestan_range32, 2 },
{ "Balinese", +1, Balinese_range16, 2, 0, 0 },
{ "Bamum", +1, Bamum_range16, 1, Bamum_range32, 1 },
+ { "Bassa_Vah", +1, 0, 0, Bassa_Vah_range32, 2 },
{ "Batak", +1, Batak_range16, 2, 0, 0 },
{ "Bengali", +1, Bengali_range16, 14, 0, 0 },
+ { "Bhaiksuki", +1, 0, 0, Bhaiksuki_range32, 4 },
{ "Bopomofo", +1, Bopomofo_range16, 3, 0, 0 },
- { "Brahmi", +1, 0, 0, Brahmi_range32, 2 },
+ { "Brahmi", +1, 0, 0, Brahmi_range32, 3 },
{ "Braille", +1, Braille_range16, 1, 0, 0 },
{ "Buginese", +1, Buginese_range16, 2, 0, 0 },
{ "Buhid", +1, Buhid_range16, 1, 0, 0 },
- { "C", +1, C_range16, 14, C_range32, 6 },
+ { "C", +1, C_range16, 16, C_range32, 8 },
{ "Canadian_Aboriginal", +1, Canadian_Aboriginal_range16, 2, 0, 0 },
{ "Carian", +1, 0, 0, Carian_range32, 1 },
+ { "Caucasian_Albanian", +1, 0, 0, Caucasian_Albanian_range32, 2 },
{ "Cc", +1, Cc_range16, 2, 0, 0 },
- { "Cf", +1, Cf_range16, 11, Cf_range32, 4 },
+ { "Cf", +1, Cf_range16, 13, Cf_range32, 6 },
+ { "Chakma", +1, 0, 0, Chakma_range32, 2 },
{ "Cham", +1, Cham_range16, 4, 0, 0 },
- { "Cherokee", +1, Cherokee_range16, 1, 0, 0 },
+ { "Cherokee", +1, Cherokee_range16, 3, 0, 0 },
{ "Co", +1, Co_range16, 1, Co_range32, 2 },
- { "Common", +1, Common_range16, 89, Common_range32, 80 },
+ { "Common", +1, Common_range16, 90, Common_range32, 76 },
{ "Coptic", +1, Coptic_range16, 3, 0, 0 },
{ "Cs", +1, Cs_range16, 1, 0, 0 },
- { "Cuneiform", +1, 0, 0, Cuneiform_range32, 3 },
+ { "Cuneiform", +1, 0, 0, Cuneiform_range32, 4 },
{ "Cypriot", +1, 0, 0, Cypriot_range32, 6 },
- { "Cyrillic", +1, Cyrillic_range16, 7, 0, 0 },
+ { "Cyrillic", +1, Cyrillic_range16, 8, 0, 0 },
{ "Deseret", +1, 0, 0, Deseret_range32, 1 },
- { "Devanagari", +1, Devanagari_range16, 6, 0, 0 },
+ { "Devanagari", +1, Devanagari_range16, 4, 0, 0 },
+ { "Dogra", +1, 0, 0, Dogra_range32, 1 },
+ { "Duployan", +1, 0, 0, Duployan_range32, 5 },
{ "Egyptian_Hieroglyphs", +1, 0, 0, Egyptian_Hieroglyphs_range32, 1 },
+ { "Elbasan", +1, 0, 0, Elbasan_range32, 1 },
{ "Ethiopic", +1, Ethiopic_range16, 32, 0, 0 },
- { "Georgian", +1, Georgian_range16, 4, 0, 0 },
- { "Glagolitic", +1, Glagolitic_range16, 2, 0, 0 },
+ { "Georgian", +1, Georgian_range16, 10, 0, 0 },
+ { "Glagolitic", +1, Glagolitic_range16, 2, Glagolitic_range32, 5 },
{ "Gothic", +1, 0, 0, Gothic_range32, 1 },
- { "Greek", +1, Greek_range16, 31, Greek_range32, 2 },
+ { "Grantha", +1, 0, 0, Grantha_range32, 15 },
+ { "Greek", +1, Greek_range16, 33, Greek_range32, 3 },
{ "Gujarati", +1, Gujarati_range16, 14, 0, 0 },
+ { "Gunjala_Gondi", +1, 0, 0, Gunjala_Gondi_range32, 6 },
{ "Gurmukhi", +1, Gurmukhi_range16, 16, 0, 0 },
- { "Han", +1, Han_range16, 12, Han_range32, 4 },
+ { "Han", +1, Han_range16, 11, Han_range32, 6 },
{ "Hangul", +1, Hangul_range16, 14, 0, 0 },
+ { "Hanifi_Rohingya", +1, 0, 0, Hanifi_Rohingya_range32, 2 },
{ "Hanunoo", +1, Hanunoo_range16, 1, 0, 0 },
+ { "Hatran", +1, 0, 0, Hatran_range32, 3 },
{ "Hebrew", +1, Hebrew_range16, 9, 0, 0 },
{ "Hiragana", +1, Hiragana_range16, 2, Hiragana_range32, 2 },
{ "Imperial_Aramaic", +1, 0, 0, Imperial_Aramaic_range32, 2 },
- { "Inherited", +1, Inherited_range16, 18, Inherited_range32, 6 },
+ { "Inherited", +1, Inherited_range16, 20, Inherited_range32, 8 },
{ "Inscriptional_Pahlavi", +1, 0, 0, Inscriptional_Pahlavi_range32, 2 },
{ "Inscriptional_Parthian", +1, 0, 0, Inscriptional_Parthian_range32, 2 },
{ "Javanese", +1, Javanese_range16, 3, 0, 0 },
- { "Kaithi", +1, 0, 0, Kaithi_range32, 1 },
- { "Kannada", +1, Kannada_range16, 14, 0, 0 },
+ { "Kaithi", +1, 0, 0, Kaithi_range32, 2 },
+ { "Kannada", +1, Kannada_range16, 13, 0, 0 },
{ "Katakana", +1, Katakana_range16, 7, Katakana_range32, 1 },
- { "Kayah_Li", +1, Kayah_Li_range16, 1, 0, 0 },
+ { "Kayah_Li", +1, Kayah_Li_range16, 2, 0, 0 },
{ "Kharoshthi", +1, 0, 0, Kharoshthi_range32, 8 },
{ "Khmer", +1, Khmer_range16, 4, 0, 0 },
- { "L", +1, L_range16, 362, L_range32, 73 },
+ { "Khojki", +1, 0, 0, Khojki_range32, 2 },
+ { "Khudawadi", +1, 0, 0, Khudawadi_range32, 2 },
+ { "L", +1, L_range16, 384, L_range32, 215 },
{ "Lao", +1, Lao_range16, 18, 0, 0 },
{ "Latin", +1, Latin_range16, 30, 0, 0 },
{ "Lepcha", +1, Lepcha_range16, 3, 0, 0 },
{ "Limbu", +1, Limbu_range16, 5, 0, 0 },
+ { "Linear_A", +1, 0, 0, Linear_A_range32, 3 },
{ "Linear_B", +1, 0, 0, Linear_B_range32, 7 },
{ "Lisu", +1, Lisu_range16, 1, 0, 0 },
- { "Ll", +1, Ll_range16, 580, Ll_range32, 29 },
- { "Lm", +1, Lm_range16, 49, 0, 0 },
- { "Lo", +1, Lo_range16, 280, Lo_range32, 43 },
+ { "Ll", +1, Ll_range16, 604, Ll_range32, 34 },
+ { "Lm", +1, Lm_range16, 54, Lm_range32, 3 },
+ { "Lo", +1, Lo_range16, 295, Lo_range32, 175 },
{ "Lt", +1, Lt_range16, 10, 0, 0 },
- { "Lu", +1, Lu_range16, 571, Lu_range32, 32 },
+ { "Lu", +1, Lu_range16, 594, Lu_range32, 37 },
{ "Lycian", +1, 0, 0, Lycian_range32, 1 },
{ "Lydian", +1, 0, 0, Lydian_range32, 2 },
- { "M", +1, M_range16, 176, M_range32, 17 },
- { "Malayalam", +1, Malayalam_range16, 11, 0, 0 },
+ { "M", +1, M_range16, 187, M_range32, 88 },
+ { "Mahajani", +1, 0, 0, Mahajani_range32, 1 },
+ { "Makasar", +1, 0, 0, Makasar_range32, 1 },
+ { "Malayalam", +1, Malayalam_range16, 8, 0, 0 },
{ "Mandaic", +1, Mandaic_range16, 2, 0, 0 },
- { "Mc", +1, Mc_range16, 106, Mc_range32, 7 },
- { "Me", +1, Me_range16, 4, 0, 0 },
- { "Meetei_Mayek", +1, Meetei_Mayek_range16, 2, 0, 0 },
- { "Mn", +1, Mn_range16, 186, Mn_range32, 17 },
- { "Mongolian", +1, Mongolian_range16, 6, 0, 0 },
- { "Myanmar", +1, Myanmar_range16, 2, 0, 0 },
- { "N", +1, N_range16, 63, N_range32, 20 },
- { "Nd", +1, Nd_range16, 35, Nd_range32, 3 },
+ { "Manichaean", +1, 0, 0, Manichaean_range32, 2 },
+ { "Marchen", +1, 0, 0, Marchen_range32, 3 },
+ { "Masaram_Gondi", +1, 0, 0, Masaram_Gondi_range32, 7 },
+ { "Mc", +1, Mc_range16, 110, Mc_range32, 56 },
+ { "Me", +1, Me_range16, 5, 0, 0 },
+ { "Medefaidrin", +1, 0, 0, Medefaidrin_range32, 1 },
+ { "Meetei_Mayek", +1, Meetei_Mayek_range16, 3, 0, 0 },
+ { "Mende_Kikakui", +1, 0, 0, Mende_Kikakui_range32, 2 },
+ { "Meroitic_Cursive", +1, 0, 0, Meroitic_Cursive_range32, 3 },
+ { "Meroitic_Hieroglyphs", +1, 0, 0, Meroitic_Hieroglyphs_range32, 1 },
+ { "Miao", +1, 0, 0, Miao_range32, 3 },
+ { "Mn", +1, Mn_range16, 208, Mn_range32, 105 },
+ { "Modi", +1, 0, 0, Modi_range32, 2 },
+ { "Mongolian", +1, Mongolian_range16, 6, Mongolian_range32, 1 },
+ { "Mro", +1, 0, 0, Mro_range32, 3 },
+ { "Multani", +1, 0, 0, Multani_range32, 5 },
+ { "Myanmar", +1, Myanmar_range16, 3, 0, 0 },
+ { "N", +1, N_range16, 67, N_range32, 58 },
+ { "Nabataean", +1, 0, 0, Nabataean_range32, 2 },
+ { "Nd", +1, Nd_range16, 37, Nd_range32, 20 },
{ "New_Tai_Lue", +1, New_Tai_Lue_range16, 4, 0, 0 },
- { "Nko", +1, Nko_range16, 1, 0, 0 },
+ { "Newa", +1, 0, 0, Newa_range32, 3 },
+ { "Nko", +1, Nko_range16, 2, 0, 0 },
{ "Nl", +1, Nl_range16, 7, Nl_range32, 5 },
- { "No", +1, No_range16, 27, No_range32, 14 },
+ { "No", +1, No_range16, 29, No_range32, 38 },
+ { "Nushu", +1, 0, 0, Nushu_range32, 2 },
{ "Ogham", +1, Ogham_range16, 1, 0, 0 },
{ "Ol_Chiki", +1, Ol_Chiki_range16, 1, 0, 0 },
+ { "Old_Hungarian", +1, 0, 0, Old_Hungarian_range32, 3 },
{ "Old_Italic", +1, 0, 0, Old_Italic_range32, 2 },
+ { "Old_North_Arabian", +1, 0, 0, Old_North_Arabian_range32, 1 },
+ { "Old_Permic", +1, 0, 0, Old_Permic_range32, 1 },
{ "Old_Persian", +1, 0, 0, Old_Persian_range32, 2 },
+ { "Old_Sogdian", +1, 0, 0, Old_Sogdian_range32, 1 },
{ "Old_South_Arabian", +1, 0, 0, Old_South_Arabian_range32, 1 },
{ "Old_Turkic", +1, 0, 0, Old_Turkic_range32, 1 },
{ "Oriya", +1, Oriya_range16, 14, 0, 0 },
+ { "Osage", +1, 0, 0, Osage_range32, 2 },
{ "Osmanya", +1, 0, 0, Osmanya_range32, 2 },
- { "P", +1, P_range16, 120, P_range32, 13 },
+ { "P", +1, P_range16, 130, P_range32, 48 },
+ { "Pahawh_Hmong", +1, 0, 0, Pahawh_Hmong_range32, 5 },
+ { "Palmyrene", +1, 0, 0, Palmyrene_range32, 1 },
+ { "Pau_Cin_Hau", +1, 0, 0, Pau_Cin_Hau_range32, 1 },
{ "Pc", +1, Pc_range16, 6, 0, 0 },
- { "Pd", +1, Pd_range16, 15, 0, 0 },
- { "Pe", +1, Pe_range16, 70, 0, 0 },
+ { "Pd", +1, Pd_range16, 17, 0, 0 },
+ { "Pe", +1, Pe_range16, 72, 0, 0 },
{ "Pf", +1, Pf_range16, 10, 0, 0 },
{ "Phags_Pa", +1, Phags_Pa_range16, 1, 0, 0 },
{ "Phoenician", +1, 0, 0, Phoenician_range32, 2 },
{ "Pi", +1, Pi_range16, 11, 0, 0 },
- { "Po", +1, Po_range16, 115, Po_range32, 13 },
- { "Ps", +1, Ps_range16, 72, 0, 0 },
+ { "Po", +1, Po_range16, 127, Po_range32, 48 },
+ { "Ps", +1, Ps_range16, 75, 0, 0 },
+ { "Psalter_Pahlavi", +1, 0, 0, Psalter_Pahlavi_range32, 3 },
{ "Rejang", +1, Rejang_range16, 2, 0, 0 },
{ "Runic", +1, Runic_range16, 2, 0, 0 },
- { "S", +1, S_range16, 142, S_range32, 66 },
+ { "S", +1, S_range16, 147, S_range32, 73 },
{ "Samaritan", +1, Samaritan_range16, 2, 0, 0 },
{ "Saurashtra", +1, Saurashtra_range16, 2, 0, 0 },
- { "Sc", +1, Sc_range16, 16, 0, 0 },
+ { "Sc", +1, Sc_range16, 18, Sc_range32, 1 },
+ { "Sharada", +1, 0, 0, Sharada_range32, 2 },
{ "Shavian", +1, 0, 0, Shavian_range32, 1 },
- { "Sinhala", +1, Sinhala_range16, 11, 0, 0 },
- { "Sk", +1, Sk_range16, 27, 0, 0 },
- { "Sm", +1, Sm_range16, 56, Sm_range32, 10 },
- { "So", +1, So_range16, 108, So_range32, 56 },
+ { "Siddham", +1, 0, 0, Siddham_range32, 2 },
+ { "SignWriting", +1, 0, 0, SignWriting_range32, 3 },
+ { "Sinhala", +1, Sinhala_range16, 12, Sinhala_range32, 1 },
+ { "Sk", +1, Sk_range16, 28, Sk_range32, 1 },
+ { "Sm", +1, Sm_range16, 53, Sm_range32, 11 },
+ { "So", +1, So_range16, 112, So_range32, 62 },
+ { "Sogdian", +1, 0, 0, Sogdian_range32, 1 },
+ { "Sora_Sompeng", +1, 0, 0, Sora_Sompeng_range32, 2 },
+ { "Soyombo", +1, 0, 0, Soyombo_range32, 2 },
{ "Sundanese", +1, Sundanese_range16, 2, 0, 0 },
{ "Syloti_Nagri", +1, Syloti_Nagri_range16, 1, 0, 0 },
- { "Syriac", +1, Syriac_range16, 3, 0, 0 },
+ { "Syriac", +1, Syriac_range16, 4, 0, 0 },
{ "Tagalog", +1, Tagalog_range16, 2, 0, 0 },
{ "Tagbanwa", +1, Tagbanwa_range16, 3, 0, 0 },
{ "Tai_Le", +1, Tai_Le_range16, 2, 0, 0 },
{ "Tai_Tham", +1, Tai_Tham_range16, 5, 0, 0 },
{ "Tai_Viet", +1, Tai_Viet_range16, 2, 0, 0 },
+ { "Takri", +1, 0, 0, Takri_range32, 2 },
{ "Tamil", +1, Tamil_range16, 16, 0, 0 },
- { "Telugu", +1, Telugu_range16, 14, 0, 0 },
+ { "Tangut", +1, 0, 0, Tangut_range32, 3 },
+ { "Telugu", +1, Telugu_range16, 12, 0, 0 },
{ "Thaana", +1, Thaana_range16, 1, 0, 0 },
{ "Thai", +1, Thai_range16, 2, 0, 0 },
{ "Tibetan", +1, Tibetan_range16, 7, 0, 0 },
{ "Tifinagh", +1, Tifinagh_range16, 3, 0, 0 },
+ { "Tirhuta", +1, 0, 0, Tirhuta_range32, 2 },
{ "Ugaritic", +1, 0, 0, Ugaritic_range32, 2 },
{ "Vai", +1, Vai_range16, 1, 0, 0 },
+ { "Warang_Citi", +1, 0, 0, Warang_Citi_range32, 2 },
{ "Yi", +1, Yi_range16, 2, 0, 0 },
- { "Z", +1, Z_range16, 9, 0, 0 },
+ { "Z", +1, Z_range16, 8, 0, 0 },
+ { "Zanabazar_Square", +1, 0, 0, Zanabazar_Square_range32, 1 },
{ "Zl", +1, Zl_range16, 1, 0, 0 },
{ "Zp", +1, Zp_range16, 1, 0, 0 },
- { "Zs", +1, Zs_range16, 8, 0, 0 },
+ { "Zs", +1, Zs_range16, 7, 0, 0 },
};
-int num_unicode_groups = 131;
+const int num_unicode_groups = 184;
} // namespace re2
diff --git a/re2/unicode_groups.h b/re2/unicode_groups.h
index f91c51f..75f55da 100644
--- a/re2/unicode_groups.h
+++ b/re2/unicode_groups.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_UNICODE_GROUPS_H_
+#define RE2_UNICODE_GROUPS_H_
+
// Unicode character groups.
// The codes get split into ranges of 16-bit codes
@@ -15,50 +18,50 @@
// to 16.5 kB of data but make the data harder to use;
// we don't bother.
-#ifndef RE2_UNICODE_GROUPS_H__
-#define RE2_UNICODE_GROUPS_H__
+#include <stdint.h>
#include "util/util.h"
+#include "util/utf.h"
namespace re2 {
struct URange16
{
- uint16 lo;
- uint16 hi;
+ uint16_t lo;
+ uint16_t hi;
};
struct URange32
{
- uint32 lo;
- uint32 hi;
+ Rune lo;
+ Rune hi;
};
struct UGroup
{
const char *name;
int sign; // +1 for [abc], -1 for [^abc]
- URange16 *r16;
+ const URange16 *r16;
int nr16;
- URange32 *r32;
+ const URange32 *r32;
int nr32;
};
// Named by property or script name (e.g., "Nd", "N", "Han").
// Negated groups are not included.
-extern UGroup unicode_groups[];
-extern int num_unicode_groups;
+extern const UGroup unicode_groups[];
+extern const int num_unicode_groups;
// Named by POSIX name (e.g., "[:alpha:]", "[:^lower:]").
// Negated groups are included.
-extern UGroup posix_groups[];
-extern int num_posix_groups;
+extern const UGroup posix_groups[];
+extern const int num_posix_groups;
// Named by Perl name (e.g., "\\d", "\\D").
// Negated groups are included.
-extern UGroup perl_groups[];
-extern int num_perl_groups;
+extern const UGroup perl_groups[];
+extern const int num_perl_groups;
} // namespace re2
-#endif // RE2_UNICODE_GROUPS_H__
+#endif // RE2_UNICODE_GROUPS_H_
diff --git a/re2/variadic_function.h b/re2/variadic_function.h
deleted file mode 100644
index 8d2b763..0000000
--- a/re2/variadic_function.h
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright 2010 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#ifndef RE2_VARIADIC_FUNCTION_H_
-#define RE2_VARIADIC_FUNCTION_H_
-
-namespace re2 {
-
-template <typename Result, typename Param0, typename Param1, typename Arg,
- Result (*Func)(Param0, Param1, const Arg* const [], int count)>
-class VariadicFunction2 {
- public:
- VariadicFunction2() {}
-
- Result operator()(Param0 p0, Param1 p1) const {
- return Func(p0, p1, 0, 0);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0) const {
- const Arg* const args[] = { &a0 };
- return Func(p0, p1, args, 1);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1) const {
- const Arg* const args[] = { &a0, &a1 };
- return Func(p0, p1, args, 2);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2) const {
- const Arg* const args[] = { &a0, &a1, &a2 };
- return Func(p0, p1, args, 3);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3 };
- return Func(p0, p1, args, 4);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4 };
- return Func(p0, p1, args, 5);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5 };
- return Func(p0, p1, args, 6);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6 };
- return Func(p0, p1, args, 7);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7 };
- return Func(p0, p1, args, 8);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8 };
- return Func(p0, p1, args, 9);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9 };
- return Func(p0, p1, args, 10);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10 };
- return Func(p0, p1, args, 11);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11 };
- return Func(p0, p1, args, 12);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12 };
- return Func(p0, p1, args, 13);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13 };
- return Func(p0, p1, args, 14);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14 };
- return Func(p0, p1, args, 15);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15 };
- return Func(p0, p1, args, 16);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16 };
- return Func(p0, p1, args, 17);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17 };
- return Func(p0, p1, args, 18);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18 };
- return Func(p0, p1, args, 19);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19 };
- return Func(p0, p1, args, 20);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19,
- &a20 };
- return Func(p0, p1, args, 21);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21 };
- return Func(p0, p1, args, 22);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21,
- const Arg& a22) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21, &a22 };
- return Func(p0, p1, args, 23);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21,
- const Arg& a22, const Arg& a23) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21, &a22, &a23 };
- return Func(p0, p1, args, 24);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21,
- const Arg& a22, const Arg& a23, const Arg& a24) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21, &a22, &a23, &a24 };
- return Func(p0, p1, args, 25);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21,
- const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21, &a22, &a23, &a24, &a25 };
- return Func(p0, p1, args, 26);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21,
- const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25,
- const Arg& a26) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21, &a22, &a23, &a24, &a25, &a26 };
- return Func(p0, p1, args, 27);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21,
- const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25,
- const Arg& a26, const Arg& a27) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21, &a22, &a23, &a24, &a25, &a26, &a27 };
- return Func(p0, p1, args, 28);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21,
- const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25,
- const Arg& a26, const Arg& a27, const Arg& a28) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21, &a22, &a23, &a24, &a25, &a26, &a27, &a28 };
- return Func(p0, p1, args, 29);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21,
- const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25,
- const Arg& a26, const Arg& a27, const Arg& a28, const Arg& a29) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21, &a22, &a23, &a24, &a25, &a26, &a27, &a28, &a29 };
- return Func(p0, p1, args, 30);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21,
- const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25,
- const Arg& a26, const Arg& a27, const Arg& a28, const Arg& a29,
- const Arg& a30) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21, &a22, &a23, &a24, &a25, &a26, &a27, &a28, &a29, &a30 };
- return Func(p0, p1, args, 31);
- }
-
- Result operator()(Param0 p0, Param1 p1, const Arg& a0, const Arg& a1,
- const Arg& a2, const Arg& a3, const Arg& a4, const Arg& a5,
- const Arg& a6, const Arg& a7, const Arg& a8, const Arg& a9,
- const Arg& a10, const Arg& a11, const Arg& a12, const Arg& a13,
- const Arg& a14, const Arg& a15, const Arg& a16, const Arg& a17,
- const Arg& a18, const Arg& a19, const Arg& a20, const Arg& a21,
- const Arg& a22, const Arg& a23, const Arg& a24, const Arg& a25,
- const Arg& a26, const Arg& a27, const Arg& a28, const Arg& a29,
- const Arg& a30, const Arg& a31) const {
- const Arg* const args[] = { &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8,
- &a9, &a10, &a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
- &a21, &a22, &a23, &a24, &a25, &a26, &a27, &a28, &a29, &a30, &a31 };
- return Func(p0, p1, args, 32);
- }
-};
-
-} // namespace re2
-
-#endif // RE2_VARIADIC_FUNCTION_H_
diff --git a/re2/walker-inl.h b/re2/walker-inl.h
index 4d2045f..032b8ac 100644
--- a/re2/walker-inl.h
+++ b/re2/walker-inl.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef RE2_WALKER_INL_H_
+#define RE2_WALKER_INL_H_
+
// Helper class for traversing Regexps without recursion.
// Clients should declare their own subclasses that override
// the PreVisit and PostVisit methods, which are called before
@@ -10,9 +13,9 @@
// Not quite the Visitor pattern, because (among other things)
// the Visitor pattern is recursive.
-#ifndef RE2_WALKER_INL_H__
-#define RE2_WALKER_INL_H__
+#include <stack>
+#include "util/logging.h"
#include "re2/regexp.h"
namespace re2 {
@@ -86,13 +89,14 @@ template<typename T> class Regexp::Walker {
private:
// Walk state for the entire traversal.
- stack<WalkState<T> >* stack_;
+ std::stack<WalkState<T> >* stack_;
bool stopped_early_;
int max_visits_;
T WalkInternal(Regexp* re, T top_arg, bool use_copy);
- DISALLOW_EVIL_CONSTRUCTORS(Walker);
+ Walker(const Walker&) = delete;
+ Walker& operator=(const Walker&) = delete;
};
template<typename T> T Regexp::Walker<T>::PreVisit(Regexp* re,
@@ -130,7 +134,7 @@ template<typename T> struct WalkState {
};
template<typename T> Regexp::Walker<T>::Walker() {
- stack_ = new stack<WalkState<T> >;
+ stack_ = new std::stack<WalkState<T> >;
stopped_early_ = false;
}
@@ -187,7 +191,7 @@ template<typename T> T Regexp::Walker<T>::WalkInternal(Regexp* re, T top_arg,
s->child_args = &s->child_arg;
else if (re->nsub_ > 1)
s->child_args = new T[re->nsub_];
- // Fall through.
+ FALLTHROUGH_INTENDED;
}
default: {
if (re->nsub_ > 0) {
@@ -241,4 +245,4 @@ template<typename T> T Regexp::Walker<T>::WalkExponential(Regexp* re, T top_arg,
} // namespace re2
-#endif // RE2_WALKER_INL_H__
+#endif // RE2_WALKER_INL_H_
diff --git a/re2_test.bzl b/re2_test.bzl
new file mode 100644
index 0000000..c0eb654
--- /dev/null
+++ b/re2_test.bzl
@@ -0,0 +1,12 @@
+# Copyright 2009 The RE2 Authors. All Rights Reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Defines a Bazel macro that instantiates a native cc_test rule for an RE2 test.
+def re2_test(name, deps=[], size="medium"):
+ native.cc_test(
+ name=name,
+ srcs=["re2/testing/%s.cc" % (name)],
+ deps=[":test"] + deps,
+ size=size,
+ )
diff --git a/runtests b/runtests
index aadcb92..94584a6 100755
--- a/runtests
+++ b/runtests
@@ -1,11 +1,22 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
+
+# System Integrity Protection on Darwin complicated these matters somewhat.
+# See https://github.com/google/re2/issues/175 for details.
+if [ "x$1" = "x-shared-library-path" ]; then
+ if [ "x$(uname)" = "xDarwin" ]; then
+ DYLD_LIBRARY_PATH="$2:$DYLD_LIBRARY_PATH"
+ export DYLD_LIBRARY_PATH
+ else
+ LD_LIBRARY_PATH="$2:$LD_LIBRARY_PATH"
+ export LD_LIBRARY_PATH
+ fi
+ shift 2
+fi
success=true
-for i
-do
+for i; do
printf "%-40s" $i
- if sh -c "$i >$i.log 2>&1" 2>/dev/null
- then
+ if $($i >$i.log 2>&1) 2>/dev/null; then
echo PASS
else
echo FAIL';' output in $i.log
@@ -16,6 +27,7 @@ done
if $success; then
echo 'ALL TESTS PASSED.'
exit 0
+else
+ echo 'TESTS FAILED.'
+ exit 1
fi
-echo 'TESTS FAILED.'
-exit 1
diff --git a/testinstall.cc b/testinstall.cc
index 17edfb4..47db4e6 100644
--- a/testinstall.cc
+++ b/testinstall.cc
@@ -6,14 +6,14 @@
#include <re2/filtered_re2.h>
#include <stdio.h>
-using namespace re2;
-
int main(void) {
- FilteredRE2 f;
+ re2::FilteredRE2 f;
int id;
f.Add("a.*b.*c", RE2::DefaultOptions, &id);
- vector<string> v;
+ std::vector<std::string> v;
f.Compile(&v);
+ std::vector<int> ids;
+ f.FirstMatch("abbccc", ids);
if(RE2::FullMatch("axbyc", "a.*b.*c")) {
printf("PASS\n");
diff --git a/util/arena.cc b/util/arena.cc
deleted file mode 100644
index 25753c5..0000000
--- a/util/arena.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2000 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "util/util.h"
-
-namespace re2 {
-
-// ----------------------------------------------------------------------
-// UnsafeArena::UnsafeArena()
-// UnsafeArena::~UnsafeArena()
-// Destroying the arena automatically calls Reset()
-// ----------------------------------------------------------------------
-
-
-UnsafeArena::UnsafeArena(const size_t block_size)
- : block_size_(block_size),
- freestart_(NULL), // set for real in Reset()
- last_alloc_(NULL),
- remaining_(0),
- blocks_alloced_(1),
- overflow_blocks_(NULL) {
- assert(block_size > kDefaultAlignment);
-
- first_blocks_[0].mem = reinterpret_cast<char*>(malloc(block_size_));
- first_blocks_[0].size = block_size_;
-
- Reset();
-}
-
-UnsafeArena::~UnsafeArena() {
- FreeBlocks();
- assert(overflow_blocks_ == NULL); // FreeBlocks() should do that
- // The first X blocks stay allocated always by default. Delete them now.
- for (int i = 0; i < blocks_alloced_; i++)
- free(first_blocks_[i].mem);
-}
-
-// ----------------------------------------------------------------------
-// UnsafeArena::Reset()
-// Clears all the memory an arena is using.
-// ----------------------------------------------------------------------
-
-void UnsafeArena::Reset() {
- FreeBlocks();
- freestart_ = first_blocks_[0].mem;
- remaining_ = first_blocks_[0].size;
- last_alloc_ = NULL;
-
- // We do not know for sure whether or not the first block is aligned,
- // so we fix that right now.
- const int overage = reinterpret_cast<uintptr_t>(freestart_) &
- (kDefaultAlignment-1);
- if (overage > 0) {
- const int waste = kDefaultAlignment - overage;
- freestart_ += waste;
- remaining_ -= waste;
- }
- freestart_when_empty_ = freestart_;
- assert(!(reinterpret_cast<uintptr_t>(freestart_)&(kDefaultAlignment-1)));
-}
-
-// -------------------------------------------------------------
-// UnsafeArena::AllocNewBlock()
-// Adds and returns an AllocatedBlock.
-// The returned AllocatedBlock* is valid until the next call
-// to AllocNewBlock or Reset. (i.e. anything that might
-// affect overflow_blocks_).
-// -------------------------------------------------------------
-
-UnsafeArena::AllocatedBlock* UnsafeArena::AllocNewBlock(const size_t block_size) {
- AllocatedBlock *block;
- // Find the next block.
- if ( blocks_alloced_ < arraysize(first_blocks_) ) {
- // Use one of the pre-allocated blocks
- block = &first_blocks_[blocks_alloced_++];
- } else { // oops, out of space, move to the vector
- if (overflow_blocks_ == NULL) overflow_blocks_ = new vector<AllocatedBlock>;
- // Adds another block to the vector.
- overflow_blocks_->resize(overflow_blocks_->size()+1);
- // block points to the last block of the vector.
- block = &overflow_blocks_->back();
- }
-
- block->mem = reinterpret_cast<char*>(malloc(block_size));
- block->size = block_size;
-
- return block;
-}
-
-// ----------------------------------------------------------------------
-// UnsafeArena::GetMemoryFallback()
-// We take memory out of our pool, aligned on the byte boundary
-// requested. If we don't have space in our current pool, we
-// allocate a new block (wasting the remaining space in the
-// current block) and give you that. If your memory needs are
-// too big for a single block, we make a special your-memory-only
-// allocation -- this is equivalent to not using the arena at all.
-// ----------------------------------------------------------------------
-
-void* UnsafeArena::GetMemoryFallback(const size_t size, const int align) {
- if (size == 0)
- return NULL; // stl/stl_alloc.h says this is okay
-
- assert(align > 0 && 0 == (align & (align - 1))); // must be power of 2
-
- // If the object is more than a quarter of the block size, allocate
- // it separately to avoid wasting too much space in leftover bytes
- if (block_size_ == 0 || size > block_size_/4) {
- // then it gets its own block in the arena
- assert(align <= kDefaultAlignment); // because that's what new gives us
- // This block stays separate from the rest of the world; in particular
- // we don't update last_alloc_ so you can't reclaim space on this block.
- return AllocNewBlock(size)->mem;
- }
-
- const int overage =
- (reinterpret_cast<uintptr_t>(freestart_) & (align-1));
- if (overage) {
- const int waste = align - overage;
- freestart_ += waste;
- if (waste < remaining_) {
- remaining_ -= waste;
- } else {
- remaining_ = 0;
- }
- }
- if (size > remaining_) {
- AllocatedBlock *block = AllocNewBlock(block_size_);
- freestart_ = block->mem;
- remaining_ = block->size;
- }
- remaining_ -= size;
- last_alloc_ = freestart_;
- freestart_ += size;
- assert((reinterpret_cast<uintptr_t>(last_alloc_) & (align-1)) == 0);
- return reinterpret_cast<void*>(last_alloc_);
-}
-
-// ----------------------------------------------------------------------
-// UnsafeArena::FreeBlocks()
-// Unlike GetMemory(), which does actual work, ReturnMemory() is a
-// no-op: we don't "free" memory until Reset() is called. We do
-// update some stats, though. Note we do no checking that the
-// pointer you pass in was actually allocated by us, or that it
-// was allocated for the size you say, so be careful here!
-// FreeBlocks() does the work for Reset(), actually freeing all
-// memory allocated in one fell swoop.
-// ----------------------------------------------------------------------
-
-void UnsafeArena::FreeBlocks() {
- for ( int i = 1; i < blocks_alloced_; ++i ) { // keep first block alloced
- free(first_blocks_[i].mem);
- first_blocks_[i].mem = NULL;
- first_blocks_[i].size = 0;
- }
- blocks_alloced_ = 1;
- if (overflow_blocks_ != NULL) {
- vector<AllocatedBlock>::iterator it;
- for (it = overflow_blocks_->begin(); it != overflow_blocks_->end(); ++it) {
- free(it->mem);
- }
- delete overflow_blocks_; // These should be used very rarely
- overflow_blocks_ = NULL;
- }
-}
-
-} // namespace re2
diff --git a/util/arena.h b/util/arena.h
deleted file mode 100644
index 7eb385b..0000000
--- a/util/arena.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2000 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Sometimes it is necessary to allocate a large number of small
-// objects. Doing this the usual way (malloc, new) is slow,
-// especially for multithreaded programs. An UnsafeArena provides a
-// mark/release method of memory management: it asks for a large chunk
-// from the operating system and doles it out bit by bit as required.
-// Then you free all the memory at once by calling UnsafeArena::Reset().
-// The "Unsafe" refers to the fact that UnsafeArena is not safe to
-// call from multiple threads.
-//
-// The global operator new that can be used as follows:
-//
-// #include "lib/arena-inl.h"
-//
-// UnsafeArena arena(1000);
-// Foo* foo = new (AllocateInArena, &arena) Foo;
-//
-
-#ifndef RE2_UTIL_ARENA_H_
-#define RE2_UTIL_ARENA_H_
-
-namespace re2 {
-
-// This class is thread-compatible.
-class UnsafeArena {
- public:
- UnsafeArena(const size_t block_size);
- virtual ~UnsafeArena();
-
- void Reset();
-
- // This should be the worst-case alignment for any type. This is
- // good for IA-32, SPARC version 7 (the last one I know), and
- // supposedly Alpha. i386 would be more time-efficient with a
- // default alignment of 8, but ::operator new() uses alignment of 4,
- // and an assertion will fail below after the call to MakeNewBlock()
- // if you try to use a larger alignment.
-#ifdef __i386__
- static const int kDefaultAlignment = 4;
-#else
- static const int kDefaultAlignment = 8;
-#endif
-
- private:
- void* GetMemoryFallback(const size_t size, const int align);
-
- public:
- void* GetMemory(const size_t size, const int align) {
- if ( size > 0 && size < remaining_ && align == 1 ) { // common case
- last_alloc_ = freestart_;
- freestart_ += size;
- remaining_ -= size;
- return reinterpret_cast<void*>(last_alloc_);
- }
- return GetMemoryFallback(size, align);
- }
-
- private:
- struct AllocatedBlock {
- char *mem;
- size_t size;
- };
-
- // The returned AllocatedBlock* is valid until the next call to AllocNewBlock
- // or Reset (i.e. anything that might affect overflow_blocks_).
- AllocatedBlock *AllocNewBlock(const size_t block_size);
-
- const AllocatedBlock *IndexToBlock(int index) const;
-
- const size_t block_size_;
- char* freestart_; // beginning of the free space in most recent block
- char* freestart_when_empty_; // beginning of the free space when we're empty
- char* last_alloc_; // used to make sure ReturnBytes() is safe
- size_t remaining_;
- // STL vector isn't as efficient as it could be, so we use an array at first
- int blocks_alloced_; // how many of the first_blocks_ have been alloced
- AllocatedBlock first_blocks_[16]; // the length of this array is arbitrary
- // if the first_blocks_ aren't enough, expand into overflow_blocks_.
- vector<AllocatedBlock>* overflow_blocks_;
-
- void FreeBlocks(); // Frees all except first block
-
- DISALLOW_EVIL_CONSTRUCTORS(UnsafeArena);
-};
-
-// Operators for allocation on the arena
-// Syntax: new (AllocateInArena, arena) MyClass;
-// STL containers, etc.
-enum AllocateInArenaType { AllocateInArena };
-
-} // namespace re2
-
-inline void* operator new(size_t size,
- re2::AllocateInArenaType /* unused */,
- re2::UnsafeArena *arena) {
- return reinterpret_cast<char*>(arena->GetMemory(size, 1));
-}
-
-#endif // RE2_UTIL_ARENA_H_
-
diff --git a/util/atomicops.h b/util/atomicops.h
deleted file mode 100644
index 11c1196..0000000
--- a/util/atomicops.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2006-2008 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#ifndef RE2_UTIL_ATOMICOPS_H__
-#define RE2_UTIL_ATOMICOPS_H__
-
-#if defined(__i386__)
-
-static inline void WriteMemoryBarrier() {
- int x;
- __asm__ __volatile__("xchgl (%0),%0" // The lock prefix is implicit for xchg.
- :: "r" (&x));
-}
-
-#elif defined(__x86_64__)
-
-// 64-bit implementations of memory barrier can be simpler, because
-// "sfence" is guaranteed to exist.
-static inline void WriteMemoryBarrier() {
- __asm__ __volatile__("sfence" : : : "memory");
-}
-
-#elif defined(__ppc__)
-
-static inline void WriteMemoryBarrier() {
- __asm__ __volatile__("eieio" : : : "memory");
-}
-
-#elif defined(__alpha__)
-
-static inline void WriteMemoryBarrier() {
- __asm__ __volatile__("wmb" : : : "memory");
-}
-
-#else
-
-#include "util/mutex.h"
-
-static inline void WriteMemoryBarrier() {
- // Slight overkill, but good enough:
- // any mutex implementation must have
- // a read barrier after the lock operation and
- // a write barrier before the unlock operation.
- //
- // It may be worthwhile to write architecture-specific
- // barriers for the common platforms, as above, but
- // this is a correct fallback.
- re2::Mutex mu;
- re2::MutexLock l(&mu);
-}
-
-/*
-#error Need WriteMemoryBarrier for architecture.
-
-// Windows
-inline void WriteMemoryBarrier() {
- LONG x;
- ::InterlockedExchange(&x, 0);
-}
-*/
-
-#endif
-
-// Alpha has very weak memory ordering. If relying on WriteBarriers, must one
-// use read barriers for the readers too.
-#if defined(__alpha__)
-
-static inline void MaybeReadMemoryBarrier() {
- __asm__ __volatile__("mb" : : : "memory");
-}
-
-#else
-
-static inline void MaybeReadMemoryBarrier() {}
-
-#endif // __alpha__
-
-#endif // RE2_UTIL_ATOMICOPS_H__
diff --git a/util/benchmark.cc b/util/benchmark.cc
index c3aad7e..144f550 100644
--- a/util/benchmark.cc
+++ b/util/benchmark.cc
@@ -2,6 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <algorithm>
+#include <chrono>
+#include <thread>
+
#include "util/util.h"
#include "util/flags.h"
#include "util/benchmark.h"
@@ -9,8 +16,11 @@
DEFINE_string(test_tmpdir, "/var/tmp", "temp directory");
+#ifdef _WIN32
+#define snprintf _snprintf
+#endif
+
using testing::Benchmark;
-using namespace re2;
static Benchmark* benchmarks[10000];
static int nbenchmarks;
@@ -24,19 +34,17 @@ void Benchmark::Register() {
nbenchmarks++;
}
-static int64 nsec() {
- struct timeval tv;
- if(gettimeofday(&tv, 0) < 0)
- return -1;
- return (int64)tv.tv_sec*1000*1000*1000 + tv.tv_usec*1000;
+static int64_t nsec() {
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(
+ std::chrono::steady_clock::now().time_since_epoch()).count();
}
-static int64 bytes;
-static int64 ns;
-static int64 t0;
-static int64 items;
+static int64_t bytes;
+static int64_t ns;
+static int64_t t0;
+static int64_t items;
-void SetBenchmarkBytesProcessed(long long x) {
+void SetBenchmarkBytesProcessed(int64_t x) {
bytes = x;
}
@@ -60,7 +68,7 @@ void BenchmarkMemoryUsage() {
}
int NumCPUs() {
- return 1;
+ return static_cast<int>(std::thread::hardware_concurrency());
}
static void runN(Benchmark *b, int n, int siz) {
@@ -74,7 +82,7 @@ static void runN(Benchmark *b, int n, int siz) {
b->fnr(n, siz);
else {
fprintf(stderr, "%s: missing function\n", b->name);
- exit(2);
+ abort();
}
if(t0 != 0)
ns += nsec() - t0;
@@ -105,11 +113,11 @@ void RunBench(Benchmark* b, int nthread, int siz) {
while(ns < (int)1e9 && n < (int)1e9) {
last = n;
if(ns/n == 0)
- n = 1e9;
+ n = (int)1e9;
else
- n = 1e9 / (ns/n);
+ n = (int)1e9 / static_cast<int>(ns/n);
- n = max(last+1, min(n+n/2, 100*last));
+ n = std::max(last+1, std::min(n+n/2, 100*last));
n = round(n);
runN(b, n, siz);
}
@@ -146,7 +154,7 @@ int main(int argc, const char** argv) {
Benchmark* b = benchmarks[i];
if(match(b->name, argc, argv))
for(int j = b->threadlo; j <= b->threadhi; j++)
- for(int k = max(b->lo, 1); k <= max(b->hi, 1); k<<=1)
+ for(int k = std::max(b->lo, 1); k <= std::max(b->hi, 1); k<<=1)
RunBench(b, j, k);
}
}
diff --git a/util/benchmark.h b/util/benchmark.h
index 31bbd53..fba30b9 100644
--- a/util/benchmark.h
+++ b/util/benchmark.h
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#ifndef RE2_UTIL_BENCHMARK_H__
-#define RE2_UTIL_BENCHMARK_H__
+#ifndef UTIL_BENCHMARK_H_
+#define UTIL_BENCHMARK_H_
+
+#include <stdint.h>
namespace testing {
struct Benchmark {
@@ -14,7 +16,7 @@ struct Benchmark {
int hi;
int threadlo;
int threadhi;
-
+
void Register();
Benchmark(const char* name, void (*f)(int)) { Clear(name); fn = f; Register(); }
Benchmark(const char* name, void (*f)(int, int), int l, int h) { Clear(name); fnr = f; lo = l; hi = h; Register(); }
@@ -23,7 +25,7 @@ struct Benchmark {
};
} // namespace testing
-void SetBenchmarkBytesProcessed(long long);
+void SetBenchmarkBytesProcessed(int64_t);
void StopBenchmarkTiming();
void StartBenchmarkTiming();
void BenchmarkMemoryUsage();
@@ -38,4 +40,4 @@ int NumCPUs();
::testing::Benchmark* _benchmark_##f = \
(new ::testing::Benchmark(#f, f, lo, hi))
-#endif // RE2_UTIL_BENCHMARK_H__
+#endif // UTIL_BENCHMARK_H_
diff --git a/util/flags.h b/util/flags.h
index 77a06a2..5af1320 100644
--- a/util/flags.h
+++ b/util/flags.h
@@ -2,13 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef UTIL_FLAGS_H_
+#define UTIL_FLAGS_H_
+
// Simplified version of Google's command line flags.
// Does not support parsing the command line.
// If you want to do that, see
-// http://code.google.com/p/google-gflags
+// https://gflags.github.io/gflags/
-#ifndef RE2_UTIL_FLAGS_H__
-#define RE2_UTIL_FLAGS_H__
+#include <stdint.h>
#define DEFINE_flag(type, name, deflt, desc) \
namespace re2 { type FLAGS_##name = deflt; }
@@ -17,11 +19,11 @@
namespace re2 { extern type FLAGS_##name; }
#define DEFINE_bool(name, deflt, desc) DEFINE_flag(bool, name, deflt, desc)
-#define DEFINE_int32(name, deflt, desc) DEFINE_flag(int32, name, deflt, desc)
+#define DEFINE_int32(name, deflt, desc) DEFINE_flag(int32_t, name, deflt, desc)
#define DEFINE_string(name, deflt, desc) DEFINE_flag(string, name, deflt, desc)
#define DECLARE_bool(name) DECLARE_flag(bool, name)
-#define DECLARE_int32(name) DECLARE_flag(int32, name)
+#define DECLARE_int32(name) DECLARE_flag(int32_t, name)
#define DECLARE_string(name) DECLARE_flag(string, name)
-#endif // RE2_UTIL_FLAGS_H__
+#endif // UTIL_FLAGS_H_
diff --git a/util/fuzz.cc b/util/fuzz.cc
new file mode 100644
index 0000000..9cac118
--- /dev/null
+++ b/util/fuzz.cc
@@ -0,0 +1,21 @@
+// Copyright 2016 The RE2 Authors. All Rights Reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+// Entry point for libFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
+
+int main(int argc, char** argv) {
+ uint8_t data[32];
+ for (int i = 0; i < 32; i++) {
+ for (int j = 0; j < 32; j++) {
+ data[j] = random() & 0xFF;
+ }
+ LLVMFuzzerTestOneInput(data, 32);
+ }
+ return 0;
+}
diff --git a/util/hash.cc b/util/hash.cc
deleted file mode 100644
index dfef7b7..0000000
--- a/util/hash.cc
+++ /dev/null
@@ -1,231 +0,0 @@
-// Modified by Russ Cox to add "namespace re2".
-// Also threw away all but hashword and hashword2.
-// http://burtleburtle.net/bob/c/lookup3.c
-
-/*
--------------------------------------------------------------------------------
-lookup3.c, by Bob Jenkins, May 2006, Public Domain.
-
-These are functions for producing 32-bit hashes for hash table lookup.
-hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
-are externally useful functions. Routines to test the hash are included
-if SELF_TEST is defined. You can use this free for any purpose. It's in
-the public domain. It has no warranty.
-
-You probably want to use hashlittle(). hashlittle() and hashbig()
-hash byte arrays. hashlittle() is is faster than hashbig() on
-little-endian machines. Intel and AMD are little-endian machines.
-On second thought, you probably want hashlittle2(), which is identical to
-hashlittle() except it returns two 32-bit hashes for the price of one.
-You could implement hashbig2() if you wanted but I haven't bothered here.
-
-If you want to find a hash of, say, exactly 7 integers, do
- a = i1; b = i2; c = i3;
- mix(a,b,c);
- a += i4; b += i5; c += i6;
- mix(a,b,c);
- a += i7;
- final(a,b,c);
-then use c as the hash value. If you have a variable length array of
-4-byte integers to hash, use hashword(). If you have a byte array (like
-a character string), use hashlittle(). If you have several byte arrays, or
-a mix of things, see the comments above hashlittle().
-
-Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
-then mix those integers. This is fast (you can do a lot more thorough
-mixing with 12*3 instructions on 3 integers than you can with 3 instructions
-on 1 byte), but shoehorning those bytes into integers efficiently is messy.
--------------------------------------------------------------------------------
-*/
-
-#include "util/util.h"
-
-#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
-
-/*
--------------------------------------------------------------------------------
-mix -- mix 3 32-bit values reversibly.
-
-This is reversible, so any information in (a,b,c) before mix() is
-still in (a,b,c) after mix().
-
-If four pairs of (a,b,c) inputs are run through mix(), or through
-mix() in reverse, there are at least 32 bits of the output that
-are sometimes the same for one pair and different for another pair.
-This was tested for:
-* pairs that differed by one bit, by two bits, in any combination
- of top bits of (a,b,c), or in any combination of bottom bits of
- (a,b,c).
-* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
- the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
- is commonly produced by subtraction) look like a single 1-bit
- difference.
-* the base values were pseudorandom, all zero but one bit set, or
- all zero plus a counter that starts at zero.
-
-Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
-satisfy this are
- 4 6 8 16 19 4
- 9 15 3 18 27 15
- 14 9 3 7 17 3
-Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
-for "differ" defined as + with a one-bit base and a two-bit delta. I
-used http://burtleburtle.net/bob/hash/avalanche.html to choose
-the operations, constants, and arrangements of the variables.
-
-This does not achieve avalanche. There are input bits of (a,b,c)
-that fail to affect some output bits of (a,b,c), especially of a. The
-most thoroughly mixed value is c, but it doesn't really even achieve
-avalanche in c.
-
-This allows some parallelism. Read-after-writes are good at doubling
-the number of bits affected, so the goal of mixing pulls in the opposite
-direction as the goal of parallelism. I did what I could. Rotates
-seem to cost as much as shifts on every machine I could lay my hands
-on, and rotates are much kinder to the top and bottom bits, so I used
-rotates.
--------------------------------------------------------------------------------
-*/
-#define mix(a,b,c) \
-{ \
- a -= c; a ^= rot(c, 4); c += b; \
- b -= a; b ^= rot(a, 6); a += c; \
- c -= b; c ^= rot(b, 8); b += a; \
- a -= c; a ^= rot(c,16); c += b; \
- b -= a; b ^= rot(a,19); a += c; \
- c -= b; c ^= rot(b, 4); b += a; \
-}
-
-/*
--------------------------------------------------------------------------------
-final -- final mixing of 3 32-bit values (a,b,c) into c
-
-Pairs of (a,b,c) values differing in only a few bits will usually
-produce values of c that look totally different. This was tested for
-* pairs that differed by one bit, by two bits, in any combination
- of top bits of (a,b,c), or in any combination of bottom bits of
- (a,b,c).
-* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
- the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
- is commonly produced by subtraction) look like a single 1-bit
- difference.
-* the base values were pseudorandom, all zero but one bit set, or
- all zero plus a counter that starts at zero.
-
-These constants passed:
- 14 11 25 16 4 14 24
- 12 14 25 16 4 14 24
-and these came close:
- 4 8 15 26 3 22 24
- 10 8 15 26 3 22 24
- 11 8 15 26 3 22 24
--------------------------------------------------------------------------------
-*/
-#define final(a,b,c) \
-{ \
- c ^= b; c -= rot(b,14); \
- a ^= c; a -= rot(c,11); \
- b ^= a; b -= rot(a,25); \
- c ^= b; c -= rot(b,16); \
- a ^= c; a -= rot(c,4); \
- b ^= a; b -= rot(a,14); \
- c ^= b; c -= rot(b,24); \
-}
-
-namespace re2 {
-
-/*
---------------------------------------------------------------------
- This works on all machines. To be useful, it requires
- -- that the key be an array of uint32_t's, and
- -- that the length be the number of uint32_t's in the key
-
- The function hashword() is identical to hashlittle() on little-endian
- machines, and identical to hashbig() on big-endian machines,
- except that the length has to be measured in uint32_ts rather than in
- bytes. hashlittle() is more complicated than hashword() only because
- hashlittle() has to dance around fitting the key bytes into registers.
---------------------------------------------------------------------
-*/
-uint32 hashword(
-const uint32 *k, /* the key, an array of uint32_t values */
-size_t length, /* the length of the key, in uint32_ts */
-uint32 initval) /* the previous hash, or an arbitrary value */
-{
- uint32_t a,b,c;
-
- /* Set up the internal state */
- a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;
-
- /*------------------------------------------------- handle most of the key */
- while (length > 3)
- {
- a += k[0];
- b += k[1];
- c += k[2];
- mix(a,b,c);
- length -= 3;
- k += 3;
- }
-
- /*------------------------------------------- handle the last 3 uint32_t's */
- switch(length) /* all the case statements fall through */
- {
- case 3 : c+=k[2];
- case 2 : b+=k[1];
- case 1 : a+=k[0];
- final(a,b,c);
- case 0: /* case 0: nothing left to add */
- break;
- }
- /*------------------------------------------------------ report the result */
- return c;
-}
-
-
-/*
---------------------------------------------------------------------
-hashword2() -- same as hashword(), but take two seeds and return two
-32-bit values. pc and pb must both be nonnull, and *pc and *pb must
-both be initialized with seeds. If you pass in (*pb)==0, the output
-(*pc) will be the same as the return value from hashword().
---------------------------------------------------------------------
-*/
-void hashword2 (
-const uint32 *k, /* the key, an array of uint32_t values */
-size_t length, /* the length of the key, in uint32_ts */
-uint32 *pc, /* IN: seed OUT: primary hash value */
-uint32 *pb) /* IN: more seed OUT: secondary hash value */
-{
- uint32_t a,b,c;
-
- /* Set up the internal state */
- a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc;
- c += *pb;
-
- /*------------------------------------------------- handle most of the key */
- while (length > 3)
- {
- a += k[0];
- b += k[1];
- c += k[2];
- mix(a,b,c);
- length -= 3;
- k += 3;
- }
-
- /*------------------------------------------- handle the last 3 uint32_t's */
- switch(length) /* all the case statements fall through */
- {
- case 3 : c+=k[2];
- case 2 : b+=k[1];
- case 1 : a+=k[0];
- final(a,b,c);
- case 0: /* case 0: nothing left to add */
- break;
- }
- /*------------------------------------------------------ report the result */
- *pc=c; *pb=b;
-}
-
-} // namespace re2
diff --git a/util/logging.h b/util/logging.h
index 4443f7c..c78f6c1 100644
--- a/util/logging.h
+++ b/util/logging.h
@@ -2,14 +2,19 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Simplified version of Google's logging.
+#ifndef UTIL_LOGGING_H_
+#define UTIL_LOGGING_H_
-#ifndef RE2_UTIL_LOGGING_H__
-#define RE2_UTIL_LOGGING_H__
+// Simplified version of Google's logging.
-#include <unistd.h> /* for write */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ostream>
#include <sstream>
+#include "util/util.h"
+
// Debug-only checking.
#define DCHECK(condition) assert(condition)
#define DCHECK_EQ(val1, val2) assert((val1) == (val2))
@@ -29,33 +34,37 @@
#define CHECK_NE(x, y) CHECK((x) != (y))
#define LOG_INFO LogMessage(__FILE__, __LINE__)
-#define LOG_ERROR LOG_INFO
-#define LOG_WARNING LOG_INFO
+#define LOG_WARNING LogMessage(__FILE__, __LINE__)
+#define LOG_ERROR LogMessage(__FILE__, __LINE__)
#define LOG_FATAL LogMessageFatal(__FILE__, __LINE__)
#define LOG_QFATAL LOG_FATAL
-#define VLOG(x) if((x)>0){}else LOG_INFO.stream()
+// It seems that one of the Windows header files defines ERROR as 0.
+#ifdef _WIN32
+#define LOG_0 LOG_INFO
+#endif
#ifdef NDEBUG
-#define DEBUG_MODE 0
#define LOG_DFATAL LOG_ERROR
#else
-#define DEBUG_MODE 1
#define LOG_DFATAL LOG_FATAL
#endif
#define LOG(severity) LOG_ ## severity.stream()
+#define VLOG(x) if((x)>0){}else LOG_INFO.stream()
+
class LogMessage {
public:
- LogMessage(const char* file, int line) : flushed_(false) {
+ LogMessage(const char* file, int line)
+ : flushed_(false) {
stream() << file << ":" << line << ": ";
}
void Flush() {
stream() << "\n";
string s = str_.str();
- int n = (int)s.size(); // shut up msvc
- if(write(2, s.data(), n) < 0) {} // shut up gcc
+ size_t n = s.size();
+ if (fwrite(s.data(), 1, n, stderr) < n) {} // shut up gcc
flushed_ = true;
}
~LogMessage() {
@@ -63,24 +72,38 @@ class LogMessage {
Flush();
}
}
- ostream& stream() { return str_; }
-
+ std::ostream& stream() { return str_; }
+
private:
bool flushed_;
std::ostringstream str_;
- DISALLOW_EVIL_CONSTRUCTORS(LogMessage);
+
+ LogMessage(const LogMessage&) = delete;
+ LogMessage& operator=(const LogMessage&) = delete;
};
+// Silence "destructor never returns" warning for ~LogMessageFatal().
+// Since this is a header file, push and then pop to limit the scope.
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4722)
+#endif
+
class LogMessageFatal : public LogMessage {
public:
LogMessageFatal(const char* file, int line)
- : LogMessage(file, line) { }
- ~LogMessageFatal() {
+ : LogMessage(file, line) {}
+ ATTRIBUTE_NORETURN ~LogMessageFatal() {
Flush();
abort();
}
private:
- DISALLOW_EVIL_CONSTRUCTORS(LogMessageFatal);
+ LogMessageFatal(const LogMessageFatal&) = delete;
+ LogMessageFatal& operator=(const LogMessageFatal&) = delete;
};
-#endif // RE2_UTIL_LOGGING_H__
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif // UTIL_LOGGING_H_
diff --git a/util/mix.h b/util/mix.h
new file mode 100644
index 0000000..d85c172
--- /dev/null
+++ b/util/mix.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The RE2 Authors. All Rights Reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifndef UTIL_MIX_H_
+#define UTIL_MIX_H_
+
+#include <stddef.h>
+#include <limits>
+
+namespace re2 {
+
+// Silence "truncation of constant value" warning for kMul in 32-bit mode.
+// Since this is a header file, push and then pop to limit the scope.
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4309)
+#endif
+
+class HashMix {
+ public:
+ HashMix() : hash_(1) {}
+ explicit HashMix(size_t val) : hash_(val + 83) {}
+ void Mix(size_t val) {
+ static const size_t kMul = static_cast<size_t>(0xdc3eb94af8ab4c93ULL);
+ hash_ *= kMul;
+ hash_ = ((hash_ << 19) |
+ (hash_ >> (std::numeric_limits<size_t>::digits - 19))) + val;
+ }
+ size_t get() const { return hash_; }
+ private:
+ size_t hash_;
+};
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+} // namespace re2
+
+#endif // UTIL_MIX_H_
diff --git a/util/mutex.h b/util/mutex.h
index 9787bfb..9c49158 100644
--- a/util/mutex.h
+++ b/util/mutex.h
@@ -2,62 +2,41 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef UTIL_MUTEX_H_
+#define UTIL_MUTEX_H_
+
/*
* A simple mutex wrapper, supporting locks and read-write locks.
* You should assume the locks are *not* re-entrant.
*/
-#ifndef RE2_UTIL_MUTEX_H_
-#define RE2_UTIL_MUTEX_H_
-
-namespace re2 {
+#if !defined(_WIN32)
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#endif
+#include <unistd.h>
+#if defined(_POSIX_READER_WRITER_LOCKS) && _POSIX_READER_WRITER_LOCKS > 0
+#define MUTEX_IS_PTHREAD_RWLOCK
+#endif
+#endif
-#define HAVE_PTHREAD 1
-#define HAVE_RWLOCK 1
-
-#if defined(NO_THREADS)
- typedef int MutexType; // to keep a lock-count
-#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
- // Needed for pthread_rwlock_*. If it causes problems, you could take it
- // out, but then you'd have to unset HAVE_RWLOCK (at least on linux -- it
- // *does* cause problems for FreeBSD, or MacOSX, but isn't needed
- // for locking there.)
-# ifdef __linux__
-# undef _XOPEN_SOURCE
-# define _XOPEN_SOURCE 500 // may be needed to get the rwlock calls
-# endif
-# include <pthread.h>
- typedef pthread_rwlock_t MutexType;
-#elif defined(HAVE_PTHREAD)
-# include <pthread.h>
- typedef pthread_mutex_t MutexType;
-#elif defined(WIN32)
-# define WIN32_LEAN_AND_MEAN // We only need minimal includes
-# ifdef GMUTEX_TRYLOCK
- // We need Windows NT or later for TryEnterCriticalSection(). If you
- // don't need that functionality, you can remove these _WIN32_WINNT
- // lines, and change TryLock() to assert(0) or something.
-# ifndef _WIN32_WINNT
-# define _WIN32_WINNT 0x0400
-# endif
-# endif
-# include <windows.h>
- typedef CRITICAL_SECTION MutexType;
+#if defined(MUTEX_IS_PTHREAD_RWLOCK)
+#include <pthread.h>
+#include <stdlib.h>
+typedef pthread_rwlock_t MutexType;
#else
-# error Need to implement mutex.h for your architecture, or #define NO_THREADS
+#include <mutex>
+typedef std::mutex MutexType;
#endif
+namespace re2 {
+
class Mutex {
public:
- // Create a Mutex that is not held by anybody.
inline Mutex();
-
- // Destructor
inline ~Mutex();
-
inline void Lock(); // Block if needed until free then acquire exclusively
inline void Unlock(); // Release a lock acquired via Lock()
- inline bool TryLock(); // If free, Lock() and return true, else return false
// Note that on systems that don't support read-write locks, these may
// be implemented as synonyms to Lock() and Unlock(). So you can use
// these for efficiency, but don't use them anyplace where being able
@@ -66,82 +45,44 @@ class Mutex {
inline void ReaderUnlock(); // Release a read share of this Mutex
inline void WriterLock() { Lock(); } // Acquire an exclusive lock
inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock()
- inline void AssertHeld() { }
private:
MutexType mutex_;
// Catch the error of writing Mutex when intending MutexLock.
Mutex(Mutex *ignored);
- // Disallow "evil" constructors
- Mutex(const Mutex&);
- void operator=(const Mutex&);
+
+ Mutex(const Mutex&) = delete;
+ Mutex& operator=(const Mutex&) = delete;
};
-// Now the implementation of Mutex for various systems
-#if defined(NO_THREADS)
-
-// When we don't have threads, we can be either reading or writing,
-// but not both. We can have lots of readers at once (in no-threads
-// mode, that's most likely to happen in recursive function calls),
-// but only one writer. We represent this by having mutex_ be -1 when
-// writing and a number > 0 when reading (and 0 when no lock is held).
-//
-// In debug mode, we assert these invariants, while in non-debug mode
-// we do nothing, for efficiency. That's why everything is in an
-// assert.
-#include <assert.h>
-
-Mutex::Mutex() : mutex_(0) { }
-Mutex::~Mutex() { assert(mutex_ == 0); }
-void Mutex::Lock() { assert(--mutex_ == -1); }
-void Mutex::Unlock() { assert(mutex_++ == -1); }
-bool Mutex::TryLock() { if (mutex_) return false; Lock(); return true; }
-void Mutex::ReaderLock() { assert(++mutex_ > 0); }
-void Mutex::ReaderUnlock() { assert(mutex_-- > 0); }
-
-#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
-
-#include <stdlib.h> // for abort()
-#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0)
+#if defined(MUTEX_IS_PTHREAD_RWLOCK)
+
+#define SAFE_PTHREAD(fncall) \
+ do { \
+ if ((fncall) != 0) abort(); \
+ } while (0)
Mutex::Mutex() { SAFE_PTHREAD(pthread_rwlock_init(&mutex_, NULL)); }
Mutex::~Mutex() { SAFE_PTHREAD(pthread_rwlock_destroy(&mutex_)); }
void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock(&mutex_)); }
void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); }
-bool Mutex::TryLock() { return pthread_rwlock_trywrlock(&mutex_) == 0; }
void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock(&mutex_)); }
void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); }
#undef SAFE_PTHREAD
-#elif defined(HAVE_PTHREAD)
-
-#include <stdlib.h> // for abort()
-#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0)
-
-Mutex::Mutex() { SAFE_PTHREAD(pthread_mutex_init(&mutex_, NULL)); }
-Mutex::~Mutex() { SAFE_PTHREAD(pthread_mutex_destroy(&mutex_)); }
-void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock(&mutex_)); }
-void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock(&mutex_)); }
-bool Mutex::TryLock() { return pthread_mutex_trylock(&mutex_) == 0; }
-void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks
-void Mutex::ReaderUnlock() { Unlock(); }
-#undef SAFE_PTHREAD
-
-#elif defined(WIN32)
+#else
-Mutex::Mutex() { InitializeCriticalSection(&mutex_); }
-Mutex::~Mutex() { DeleteCriticalSection(&mutex_); }
-void Mutex::Lock() { EnterCriticalSection(&mutex_); }
-void Mutex::Unlock() { LeaveCriticalSection(&mutex_); }
-bool Mutex::TryLock() { return TryEnterCriticalSection(&mutex_) != 0; }
-void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks
+Mutex::Mutex() { }
+Mutex::~Mutex() { }
+void Mutex::Lock() { mutex_.lock(); }
+void Mutex::Unlock() { mutex_.unlock(); }
+void Mutex::ReaderLock() { Lock(); } // C++11 doesn't have std::shared_mutex.
void Mutex::ReaderUnlock() { Unlock(); }
#endif
-
// --------------------------------------------------------------------------
// Some helper classes
@@ -152,9 +93,9 @@ class MutexLock {
~MutexLock() { mu_->Unlock(); }
private:
Mutex * const mu_;
- // Disallow "evil" constructors
- MutexLock(const MutexLock&);
- void operator=(const MutexLock&);
+
+ MutexLock(const MutexLock&) = delete;
+ MutexLock& operator=(const MutexLock&) = delete;
};
// ReaderMutexLock and WriterMutexLock do the same, for rwlocks
@@ -164,9 +105,9 @@ class ReaderMutexLock {
~ReaderMutexLock() { mu_->ReaderUnlock(); }
private:
Mutex * const mu_;
- // Disallow "evil" constructors
- ReaderMutexLock(const ReaderMutexLock&);
- void operator=(const ReaderMutexLock&);
+
+ ReaderMutexLock(const ReaderMutexLock&) = delete;
+ ReaderMutexLock& operator=(const ReaderMutexLock&) = delete;
};
class WriterMutexLock {
@@ -175,37 +116,16 @@ class WriterMutexLock {
~WriterMutexLock() { mu_->WriterUnlock(); }
private:
Mutex * const mu_;
- // Disallow "evil" constructors
- WriterMutexLock(const WriterMutexLock&);
- void operator=(const WriterMutexLock&);
+
+ WriterMutexLock(const WriterMutexLock&) = delete;
+ WriterMutexLock& operator=(const WriterMutexLock&) = delete;
};
// Catch bug where variable name is omitted, e.g. MutexLock (&mu);
-#define MutexLock(x) COMPILE_ASSERT(0, mutex_lock_decl_missing_var_name)
-#define ReaderMutexLock(x) COMPILE_ASSERT(0, rmutex_lock_decl_missing_var_name)
-#define WriterMutexLock(x) COMPILE_ASSERT(0, wmutex_lock_decl_missing_var_name)
-
-// Provide safe way to declare and use global, linker-initialized mutex. Sigh.
-#ifdef HAVE_PTHREAD
-
-#define GLOBAL_MUTEX(name) \
- static pthread_mutex_t (name) = PTHREAD_MUTEX_INITIALIZER
-#define GLOBAL_MUTEX_LOCK(name) \
- pthread_mutex_lock(&(name))
-#define GLOBAL_MUTEX_UNLOCK(name) \
- pthread_mutex_unlock(&(name))
-
-#else
-
-#define GLOBAL_MUTEX(name) \
- static Mutex name
-#define GLOBAL_MUTEX_LOCK(name) \
- name.Lock()
-#define GLOBAL_MUTEX_UNLOCK(name) \
- name.Unlock()
-
-#endif
+#define MutexLock(x) static_assert(false, "MutexLock declaration missing variable name")
+#define ReaderMutexLock(x) static_assert(false, "ReaderMutexLock declaration missing variable name")
+#define WriterMutexLock(x) static_assert(false, "WriterMutexLock declaration missing variable name")
} // namespace re2
-#endif /* #define RE2_UTIL_MUTEX_H_ */
+#endif // UTIL_MUTEX_H_
diff --git a/util/pcre.cc b/util/pcre.cc
index 5e67e1f..78de292 100644
--- a/util/pcre.cc
+++ b/util/pcre.cc
@@ -6,10 +6,27 @@
// The main changes are the addition of the HitLimit method and
// compilation as PCRE in namespace re2.
+#include <assert.h>
+#include <ctype.h>
#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits>
+#include <string>
+#include <utility>
+
#include "util/util.h"
#include "util/flags.h"
+#include "util/logging.h"
#include "util/pcre.h"
+#include "util/strutil.h"
+
+// Silence warnings about the wacky formatting in the operator() functions.
+// Note that we test for Clang first because it defines __GNUC__ as well.
+#if defined(__clang__)
+#elif defined(__GNUC__) && __GNUC__ >= 6
+#pragma GCC diagnostic ignored "-Wmisleading-indentation"
+#endif
#define PCREPORT(level) LOG(level)
@@ -22,6 +39,42 @@ DEFINE_int32(regexp_stack_limit, 256<<10, "default PCRE stack limit (bytes)");
DEFINE_int32(regexp_match_limit, 1000000,
"default PCRE match limit (function calls)");
+#ifndef USEPCRE
+
+// Fake just enough of the PCRE API to allow this file to build. :)
+
+struct pcre_extra {
+ int flags;
+ int match_limit;
+ int match_limit_recursion;
+};
+
+#define PCRE_EXTRA_MATCH_LIMIT 0
+#define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0
+#define PCRE_ANCHORED 0
+#define PCRE_NOTEMPTY 0
+#define PCRE_ERROR_NOMATCH 1
+#define PCRE_ERROR_MATCHLIMIT 2
+#define PCRE_ERROR_RECURSIONLIMIT 3
+#define PCRE_INFO_CAPTURECOUNT 0
+
+void pcre_free(void*) {
+}
+
+pcre* pcre_compile(const char*, int, const char**, int*, const unsigned char*) {
+ return NULL;
+}
+
+int pcre_exec(const pcre*, const pcre_extra*, const char*, int, int, int, int*, int) {
+ return 0;
+}
+
+int pcre_fullinfo(const pcre*, const pcre_extra*, int, void*) {
+ return 0;
+}
+
+#endif
+
namespace re2 {
// Maximum number of args we can set
@@ -113,7 +166,7 @@ pcre* PCRE::Compile(Anchor anchor) {
// ANCHOR_BOTH Tack a "\z" to the end of the original pattern
// and use a pcre anchored match.
- const char* error;
+ const char* error = "";
int eoffset;
pcre* re;
if (anchor != ANCHOR_BOTH) {
@@ -177,8 +230,8 @@ bool PCRE::FullMatchFunctor::operator ()(const StringPiece& text,
if (&a15 == &no_more_args) goto done; args[n++] = &a15;
done:
- int consumed;
- int vec[kVecSize];
+ size_t consumed;
+ int vec[kVecSize] = {};
return re.DoMatchImpl(text, ANCHOR_BOTH, &consumed, args, n, vec, kVecSize);
}
@@ -220,8 +273,8 @@ bool PCRE::PartialMatchFunctor::operator ()(const StringPiece& text,
if (&a15 == &no_more_args) goto done; args[n++] = &a15;
done:
- int consumed;
- int vec[kVecSize];
+ size_t consumed;
+ int vec[kVecSize] = {};
return re.DoMatchImpl(text, UNANCHORED, &consumed, args, n, vec, kVecSize);
}
@@ -263,8 +316,8 @@ bool PCRE::ConsumeFunctor::operator ()(StringPiece* input,
if (&a15 == &no_more_args) goto done; args[n++] = &a15;
done:
- int consumed;
- int vec[kVecSize];
+ size_t consumed;
+ int vec[kVecSize] = {};
if (pattern.DoMatchImpl(*input, ANCHOR_START, &consumed,
args, n, vec, kVecSize)) {
input->remove_prefix(consumed);
@@ -312,8 +365,8 @@ bool PCRE::FindAndConsumeFunctor::operator ()(StringPiece* input,
if (&a15 == &no_more_args) goto done; args[n++] = &a15;
done:
- int consumed;
- int vec[kVecSize];
+ size_t consumed;
+ int vec[kVecSize] = {};
if (pattern.DoMatchImpl(*input, UNANCHORED, &consumed,
args, n, vec, kVecSize)) {
input->remove_prefix(consumed);
@@ -326,7 +379,7 @@ done:
bool PCRE::Replace(string *str,
const PCRE& pattern,
const StringPiece& rewrite) {
- int vec[kVecSize];
+ int vec[kVecSize] = {};
int matches = pattern.TryMatch(*str, 0, UNANCHORED, true, vec, kVecSize);
if (matches == 0)
return false;
@@ -345,12 +398,12 @@ int PCRE::GlobalReplace(string *str,
const PCRE& pattern,
const StringPiece& rewrite) {
int count = 0;
- int vec[kVecSize];
+ int vec[kVecSize] = {};
string out;
- int start = 0;
+ size_t start = 0;
bool last_match_was_empty_string = false;
- for (; start <= str->length();) {
+ while (start <= str->size()) {
// If the previous match was for the empty string, we shouldn't
// just match again: we'll match in the same way and get an
// infinite loop. Instead, we do the match in a special way:
@@ -366,18 +419,19 @@ int PCRE::GlobalReplace(string *str,
matches = pattern.TryMatch(*str, start, ANCHOR_START, false,
vec, kVecSize);
if (matches <= 0) {
- if (start < str->length())
+ if (start < str->size())
out.push_back((*str)[start]);
start++;
last_match_was_empty_string = false;
continue;
}
} else {
- matches = pattern.TryMatch(*str, start, UNANCHORED, true, vec, kVecSize);
+ matches = pattern.TryMatch(*str, start, UNANCHORED, true,
+ vec, kVecSize);
if (matches <= 0)
break;
}
- int matchstart = vec[0], matchend = vec[1];
+ size_t matchstart = vec[0], matchend = vec[1];
assert(matchstart >= start);
assert(matchend >= matchstart);
@@ -391,8 +445,9 @@ int PCRE::GlobalReplace(string *str,
if (count == 0)
return 0;
- if (start < str->length())
- out.append(*str, start, str->length() - start);
+ if (start < str->size())
+ out.append(*str, start, str->size() - start);
+ using std::swap;
swap(out, *str);
return count;
}
@@ -401,7 +456,7 @@ bool PCRE::Extract(const StringPiece &text,
const PCRE& pattern,
const StringPiece &rewrite,
string *out) {
- int vec[kVecSize];
+ int vec[kVecSize] = {};
int matches = pattern.TryMatch(text, 0, UNANCHORED, true, vec, kVecSize);
if (matches == 0)
return false;
@@ -420,7 +475,7 @@ string PCRE::QuoteMeta(const StringPiece& unquoted) {
// that. (This also makes it identical to the perl function of the
// same name except for the null-character special case;
// see `perldoc -f quotemeta`.)
- for (int ii = 0; ii < unquoted.length(); ++ii) {
+ for (size_t ii = 0; ii < unquoted.size(); ++ii) {
// Note that using 'isalnum' here raises the benchmark time from
// 32ns to 58ns:
if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') &&
@@ -447,7 +502,7 @@ string PCRE::QuoteMeta(const StringPiece& unquoted) {
/***** Actual matching and rewriting code *****/
bool PCRE::HitLimit() {
- return hit_limit_;
+ return hit_limit_ != 0;
}
void PCRE::ClearHitLimit() {
@@ -455,11 +510,11 @@ void PCRE::ClearHitLimit() {
}
int PCRE::TryMatch(const StringPiece& text,
- int startpos,
- Anchor anchor,
- bool empty_ok,
- int *vec,
- int vecsize) const {
+ size_t startpos,
+ Anchor anchor,
+ bool empty_ok,
+ int *vec,
+ int vecsize) const {
pcre* re = (anchor == ANCHOR_BOTH) ? re_full_ : re_partial_;
if (re == NULL) {
PCREPORT(ERROR) << "Matching against invalid re: " << *error_;
@@ -495,8 +550,8 @@ int PCRE::TryMatch(const StringPiece& text,
int rc = pcre_exec(re, // The regular expression object
&extra,
(text.data() == NULL) ? "" : text.data(),
- text.size(),
- startpos,
+ static_cast<int>(text.size()),
+ static_cast<int>(startpos),
options,
vec,
vecsize);
@@ -551,13 +606,18 @@ int PCRE::TryMatch(const StringPiece& text,
}
bool PCRE::DoMatchImpl(const StringPiece& text,
- Anchor anchor,
- int* consumed,
- const Arg* const* args,
- int n,
- int* vec,
- int vecsize) const {
+ Anchor anchor,
+ size_t* consumed,
+ const Arg* const* args,
+ int n,
+ int* vec,
+ int vecsize) const {
assert((1 + n) * 3 <= vecsize); // results + PCRE workspace
+ if (NumberOfCapturingGroups() < n) {
+ // RE has fewer capturing groups than number of Arg pointers passed in.
+ return false;
+ }
+
int matches = TryMatch(text, 0, anchor, true, vec, vecsize);
assert(matches >= 0); // TryMatch never returns negatives
if (matches == 0)
@@ -569,10 +629,6 @@ bool PCRE::DoMatchImpl(const StringPiece& text,
// We are not interested in results
return true;
}
- if (NumberOfCapturingGroups() < n) {
- // PCRE has fewer capturing groups than number of arg pointers passed in
- return false;
- }
// If we got here, we must have matched the whole pattern.
// We do not need (can not do) any more checks on the value of 'matches' here
@@ -580,7 +636,17 @@ bool PCRE::DoMatchImpl(const StringPiece& text,
for (int i = 0; i < n; i++) {
const int start = vec[2*(i+1)];
const int limit = vec[2*(i+1)+1];
- if (!args[i]->Parse(text.data() + start, limit-start)) {
+
+ // Avoid invoking undefined behavior when text.data() happens
+ // to be null and start happens to be -1, the latter being the
+ // case for an unmatched subexpression. Even if text.data() is
+ // not null, pointing one byte before was a longstanding bug.
+ const char* addr = NULL;
+ if (start != -1) {
+ addr = text.data() + start;
+ }
+
+ if (!args[i]->Parse(addr, limit-start)) {
// TODO: Should we indicate what the error was?
return false;
}
@@ -590,14 +656,14 @@ bool PCRE::DoMatchImpl(const StringPiece& text,
}
bool PCRE::DoMatch(const StringPiece& text,
- Anchor anchor,
- int* consumed,
- const Arg* const args[],
- int n) const {
+ Anchor anchor,
+ size_t* consumed,
+ const Arg* const args[],
+ int n) const {
assert(n >= 0);
- size_t const vecsize = (1 + n) * 3; // results + PCRE workspace
- // (as for kVecSize)
- int *vec = new int[vecsize];
+ const int vecsize = (1 + n) * 3; // results + PCRE workspace
+ // (as for kVecSize)
+ int* vec = new int[vecsize];
bool b = DoMatchImpl(text, anchor, consumed, args, n, vec, vecsize);
delete[] vec;
return b;
@@ -682,41 +748,52 @@ int PCRE::NumberOfCapturingGroups() const {
if (re_partial_ == NULL) return -1;
int result;
- CHECK(pcre_fullinfo(re_partial_, // The regular expression object
- NULL, // We did not study the pattern
- PCRE_INFO_CAPTURECOUNT,
- &result) == 0);
+ int rc = pcre_fullinfo(re_partial_, // The regular expression object
+ NULL, // We did not study the pattern
+ PCRE_INFO_CAPTURECOUNT,
+ &result);
+ if (rc != 0) {
+ PCREPORT(ERROR) << "Unexpected return code: " << rc;
+ return -1;
+ }
return result;
}
/***** Parsers for various types *****/
-bool PCRE::Arg::parse_null(const char* str, int n, void* dest) {
+bool PCRE::Arg::parse_null(const char* str, size_t n, void* dest) {
// We fail if somebody asked us to store into a non-NULL void* pointer
return (dest == NULL);
}
-bool PCRE::Arg::parse_string(const char* str, int n, void* dest) {
+bool PCRE::Arg::parse_string(const char* str, size_t n, void* dest) {
if (dest == NULL) return true;
reinterpret_cast<string*>(dest)->assign(str, n);
return true;
}
-bool PCRE::Arg::parse_stringpiece(const char* str, int n, void* dest) {
+bool PCRE::Arg::parse_stringpiece(const char* str, size_t n, void* dest) {
if (dest == NULL) return true;
- reinterpret_cast<StringPiece*>(dest)->set(str, n);
+ *(reinterpret_cast<StringPiece*>(dest)) = StringPiece(str, n);
return true;
}
-bool PCRE::Arg::parse_char(const char* str, int n, void* dest) {
+bool PCRE::Arg::parse_char(const char* str, size_t n, void* dest) {
if (n != 1) return false;
if (dest == NULL) return true;
*(reinterpret_cast<char*>(dest)) = str[0];
return true;
}
-bool PCRE::Arg::parse_uchar(const char* str, int n, void* dest) {
+bool PCRE::Arg::parse_schar(const char* str, size_t n, void* dest) {
+ if (n != 1) return false;
+ if (dest == NULL) return true;
+ *(reinterpret_cast<signed char*>(dest)) = str[0];
+ return true;
+}
+
+bool PCRE::Arg::parse_uchar(const char* str, size_t n, void* dest) {
if (n != 1) return false;
if (dest == NULL) return true;
*(reinterpret_cast<unsigned char*>(dest)) = str[0];
@@ -733,7 +810,7 @@ static const int kMaxNumberLength = 32;
// a. "str" if no termination is needed
// b. "buf" if the string was copied and null-terminated
// c. "" if the input was invalid and has no hope of being parsed
-static const char* TerminateNumber(char* buf, const char* str, int n) {
+static const char* TerminateNumber(char* buf, const char* str, size_t n) {
if ((n > 0) && isspace(*str)) {
// We are less forgiving than the strtoxxx() routines and do not
// allow leading spaces.
@@ -756,9 +833,9 @@ static const char* TerminateNumber(char* buf, const char* str, int n) {
}
bool PCRE::Arg::parse_long_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
if (n == 0) return false;
char buf[kMaxNumberLength+1];
str = TerminateNumber(buf, str, n);
@@ -773,16 +850,16 @@ bool PCRE::Arg::parse_long_radix(const char* str,
}
bool PCRE::Arg::parse_ulong_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
if (n == 0) return false;
char buf[kMaxNumberLength+1];
str = TerminateNumber(buf, str, n);
if (str[0] == '-') {
- // strtoul() will silently accept negative numbers and parse
- // them. This module is more strict and treats them as errors.
- return false;
+ // strtoul() will silently accept negative numbers and parse
+ // them. This module is more strict and treats them as errors.
+ return false;
}
char* end;
@@ -796,74 +873,74 @@ bool PCRE::Arg::parse_ulong_radix(const char* str,
}
bool PCRE::Arg::parse_short_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
long r;
- if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
- if ((short)r != r) return false; // Out of range
+ if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
+ if ((short)r != r) return false; // Out of range
if (dest == NULL) return true;
- *(reinterpret_cast<short*>(dest)) = r;
+ *(reinterpret_cast<short*>(dest)) = (short)r;
return true;
}
bool PCRE::Arg::parse_ushort_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
unsigned long r;
- if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
- if ((ushort)r != r) return false; // Out of range
+ if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
+ if ((unsigned short)r != r) return false; // Out of range
if (dest == NULL) return true;
- *(reinterpret_cast<unsigned short*>(dest)) = r;
+ *(reinterpret_cast<unsigned short*>(dest)) = (unsigned short)r;
return true;
}
bool PCRE::Arg::parse_int_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
long r;
- if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
- if ((int)r != r) return false; // Out of range
+ if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
+ if ((int)r != r) return false; // Out of range
if (dest == NULL) return true;
- *(reinterpret_cast<int*>(dest)) = r;
+ *(reinterpret_cast<int*>(dest)) = (int)r;
return true;
}
bool PCRE::Arg::parse_uint_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
unsigned long r;
- if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
- if ((uint)r != r) return false; // Out of range
+ if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
+ if ((unsigned int)r != r) return false; // Out of range
if (dest == NULL) return true;
- *(reinterpret_cast<unsigned int*>(dest)) = r;
+ *(reinterpret_cast<unsigned int*>(dest)) = (unsigned int)r;
return true;
}
bool PCRE::Arg::parse_longlong_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
if (n == 0) return false;
char buf[kMaxNumberLength+1];
str = TerminateNumber(buf, str, n);
char* end;
errno = 0;
- int64 r = strtoll(str, &end, radix);
+ long long r = strtoll(str, &end, radix);
if (end != str + n) return false; // Leftover junk
if (errno) return false;
if (dest == NULL) return true;
- *(reinterpret_cast<int64*>(dest)) = r;
+ *(reinterpret_cast<long long*>(dest)) = r;
return true;
}
bool PCRE::Arg::parse_ulonglong_radix(const char* str,
- int n,
- void* dest,
- int radix) {
+ size_t n,
+ void* dest,
+ int radix) {
if (n == 0) return false;
char buf[kMaxNumberLength+1];
str = TerminateNumber(buf, str, n);
@@ -874,26 +951,32 @@ bool PCRE::Arg::parse_ulonglong_radix(const char* str,
}
char* end;
errno = 0;
- uint64 r = strtoull(str, &end, radix);
+ unsigned long long r = strtoull(str, &end, radix);
if (end != str + n) return false; // Leftover junk
if (errno) return false;
if (dest == NULL) return true;
- *(reinterpret_cast<uint64*>(dest)) = r;
+ *(reinterpret_cast<unsigned long long*>(dest)) = r;
return true;
}
-bool PCRE::Arg::parse_double(const char* str, int n, void* dest) {
+static bool parse_double_float(const char* str, size_t n, bool isfloat,
+ void* dest) {
if (n == 0) return false;
static const int kMaxLength = 200;
char buf[kMaxLength];
if (n >= kMaxLength) return false;
memcpy(buf, str, n);
buf[n] = '\0';
- errno = 0;
char* end;
- double r = strtod(buf, &end);
+ errno = 0;
+ double r;
+ if (isfloat) {
+ r = strtof(buf, &end);
+ } else {
+ r = strtod(buf, &end);
+ }
if (end != buf + n) {
-#ifdef COMPILER_MSVC
+#ifdef _WIN32
// Microsoft's strtod() doesn't handle inf and nan, so we have to
// handle it explicitly. Speed is not important here because this
// code is only called in unit tests.
@@ -905,12 +988,12 @@ bool PCRE::Arg::parse_double(const char* str, int n, void* dest) {
} else if ('+' == *i) {
++i;
}
- if (0 == stricmp(i, "inf") || 0 == stricmp(i, "infinity")) {
- r = numeric_limits<double>::infinity();
+ if (0 == _stricmp(i, "inf") || 0 == _stricmp(i, "infinity")) {
+ r = std::numeric_limits<double>::infinity();
if (!pos)
r = -r;
- } else if (0 == stricmp(i, "nan")) {
- r = numeric_limits<double>::quiet_NaN();
+ } else if (0 == _stricmp(i, "nan")) {
+ r = std::numeric_limits<double>::quiet_NaN();
} else {
return false;
}
@@ -920,42 +1003,47 @@ bool PCRE::Arg::parse_double(const char* str, int n, void* dest) {
}
if (errno) return false;
if (dest == NULL) return true;
- *(reinterpret_cast<double*>(dest)) = r;
+ if (isfloat) {
+ *(reinterpret_cast<float*>(dest)) = (float)r;
+ } else {
+ *(reinterpret_cast<double*>(dest)) = r;
+ }
return true;
}
-bool PCRE::Arg::parse_float(const char* str, int n, void* dest) {
- double r;
- if (!parse_double(str, n, &r)) return false;
- if (dest == NULL) return true;
- *(reinterpret_cast<float*>(dest)) = static_cast<float>(r);
- return true;
+bool PCRE::Arg::parse_double(const char* str, size_t n, void* dest) {
+ return parse_double_float(str, n, false, dest);
}
+bool PCRE::Arg::parse_float(const char* str, size_t n, void* dest) {
+ return parse_double_float(str, n, true, dest);
+}
-#define DEFINE_INTEGER_PARSERS(name) \
- bool PCRE::Arg::parse_##name(const char* str, int n, void* dest) { \
- return parse_##name##_radix(str, n, dest, 10); \
- } \
- bool PCRE::Arg::parse_##name##_hex(const char* str, int n, void* dest) { \
- return parse_##name##_radix(str, n, dest, 16); \
- } \
- bool PCRE::Arg::parse_##name##_octal(const char* str, int n, void* dest) { \
- return parse_##name##_radix(str, n, dest, 8); \
- } \
- bool PCRE::Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \
- return parse_##name##_radix(str, n, dest, 0); \
+#define DEFINE_INTEGER_PARSER(name) \
+ bool PCRE::Arg::parse_##name(const char* str, size_t n, void* dest) { \
+ return parse_##name##_radix(str, n, dest, 10); \
+ } \
+ bool PCRE::Arg::parse_##name##_hex(const char* str, size_t n, void* dest) { \
+ return parse_##name##_radix(str, n, dest, 16); \
+ } \
+ bool PCRE::Arg::parse_##name##_octal(const char* str, size_t n, \
+ void* dest) { \
+ return parse_##name##_radix(str, n, dest, 8); \
+ } \
+ bool PCRE::Arg::parse_##name##_cradix(const char* str, size_t n, \
+ void* dest) { \
+ return parse_##name##_radix(str, n, dest, 0); \
}
-DEFINE_INTEGER_PARSERS(short);
-DEFINE_INTEGER_PARSERS(ushort);
-DEFINE_INTEGER_PARSERS(int);
-DEFINE_INTEGER_PARSERS(uint);
-DEFINE_INTEGER_PARSERS(long);
-DEFINE_INTEGER_PARSERS(ulong);
-DEFINE_INTEGER_PARSERS(longlong);
-DEFINE_INTEGER_PARSERS(ulonglong);
+DEFINE_INTEGER_PARSER(short);
+DEFINE_INTEGER_PARSER(ushort);
+DEFINE_INTEGER_PARSER(int);
+DEFINE_INTEGER_PARSER(uint);
+DEFINE_INTEGER_PARSER(long);
+DEFINE_INTEGER_PARSER(ulong);
+DEFINE_INTEGER_PARSER(longlong);
+DEFINE_INTEGER_PARSER(ulonglong);
-#undef DEFINE_INTEGER_PARSERS
+#undef DEFINE_INTEGER_PARSER
} // namespace re2
diff --git a/util/pcre.h b/util/pcre.h
index 4dda95d..10ec4f2 100644
--- a/util/pcre.h
+++ b/util/pcre.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef UTIL_PCRE_H_
+#define UTIL_PCRE_H_
+
// This is a variant of PCRE's pcrecpp.h, originally written at Google.
// The main changes are the addition of the HitLimit method and
// compilation as PCRE in namespace re2.
@@ -58,9 +61,9 @@
// CHECK(PCRE::FullMatch(utf8_string, re));
//
// -----------------------------------------------------------------------
-// MATCHING WITH SUB-STRING EXTRACTION:
+// MATCHING WITH SUBSTRING EXTRACTION:
//
-// You can supply extra pointer arguments to extract matched subpieces.
+// You can supply extra pointer arguments to extract matched substrings.
//
// Example: extracts "ruby" into "s" and 1234 into "i"
// int i;
@@ -167,22 +170,9 @@ namespace re2 {
const bool UsingPCRE = true;
} // namespace re2
#else
+struct pcre; // opaque
namespace re2 {
const bool UsingPCRE = false;
-struct pcre;
-struct pcre_extra { int flags, match_limit, match_limit_recursion; };
-#define pcre_free(x) {}
-#define PCRE_EXTRA_MATCH_LIMIT 0
-#define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0
-#define PCRE_ANCHORED 0
-#define PCRE_NOTEMPTY 0
-#define PCRE_ERROR_NOMATCH 1
-#define PCRE_ERROR_MATCHLIMIT 2
-#define PCRE_ERROR_RECURSIONLIMIT 3
-#define PCRE_INFO_CAPTURECOUNT 0
-#define pcre_compile(a,b,c,d,e) ({ (void)(a); (void)(b); *(c)=""; *(d)=0; (void)(e); ((pcre*)0); })
-#define pcre_exec(a, b, c, d, e, f, g, h) ({ (void)(a); (void)(b); (void)(c); (void)(d); (void)(e); (void)(f); (void)(g); (void)(h); 0; })
-#define pcre_fullinfo(a, b, c, d) ({ (void)(a); (void)(b); (void)(c); *(d) = 0; 0; })
} // namespace re2
#endif
@@ -258,7 +248,7 @@ class PCRE {
// type, or one of:
// string (matched piece is copied to string)
// StringPiece (StringPiece is mutated to point to matched piece)
- // T (where "bool T::ParseFrom(const char*, int)" exists)
+ // T (where "bool T::ParseFrom(const char*, size_t)" exists)
// (void*)NULL (the corresponding matched sub-pattern is not copied)
//
// Returns true iff all of the following conditions are satisfied:
@@ -452,7 +442,7 @@ class PCRE {
// "*consumed" if successful.
bool DoMatch(const StringPiece& text,
Anchor anchor,
- int* consumed,
+ size_t* consumed,
const Arg* const* args, int n) const;
// Return the number of capturing subpatterns, or -1 if the
@@ -475,7 +465,7 @@ class PCRE {
// When matching PCRE("(foo)|hello") against "hello", it will return 1.
// But the values for all subpattern are filled in into "vec".
int TryMatch(const StringPiece& text,
- int startpos,
+ size_t startpos,
Anchor anchor,
bool empty_ok,
int *vec,
@@ -492,7 +482,7 @@ class PCRE {
// internal implementation for DoMatch
bool DoMatchImpl(const StringPiece& text,
Anchor anchor,
- int* consumed,
+ size_t* consumed,
const Arg* const args[],
int n,
int* vec,
@@ -509,8 +499,10 @@ class PCRE {
bool report_errors_; // Silences error logging if false
int match_limit_; // Limit on execution resources
int stack_limit_; // Limit on stack resources (bytes)
- mutable int32_t hit_limit_; // Hit limit during execution (bool)?
- DISALLOW_EVIL_CONSTRUCTORS(PCRE);
+ mutable int32_t hit_limit_; // Hit limit during execution (bool)?
+
+ PCRE(const PCRE&) = delete;
+ PCRE& operator=(const PCRE&) = delete;
};
// PCRE_Options allow you to set the PCRE::Options, plus any pcre
@@ -565,7 +557,7 @@ class PCRE_Options {
template <class T>
class _PCRE_MatchObject {
public:
- static inline bool Parse(const char* str, int n, void* dest) {
+ static inline bool Parse(const char* str, size_t n, void* dest) {
if (dest == NULL) return true;
T* object = reinterpret_cast<T*>(dest);
return object->ParseFrom(str, n);
@@ -580,16 +572,21 @@ class PCRE::Arg {
// Constructor specially designed for NULL arguments
Arg(void*);
- typedef bool (*Parser)(const char* str, int n, void* dest);
+ typedef bool (*Parser)(const char* str, size_t n, void* dest);
// Type-specific parsers
-#define MAKE_PARSER(type,name) \
- Arg(type* p) : arg_(p), parser_(name) { } \
- Arg(type* p, Parser parser) : arg_(p), parser_(parser) { } \
-
+#define MAKE_PARSER(type, name) \
+ Arg(type* p) : arg_(p), parser_(name) {} \
+ Arg(type* p, Parser parser) : arg_(p), parser_(parser) {}
MAKE_PARSER(char, parse_char);
+ MAKE_PARSER(signed char, parse_schar);
MAKE_PARSER(unsigned char, parse_uchar);
+ MAKE_PARSER(float, parse_float);
+ MAKE_PARSER(double, parse_double);
+ MAKE_PARSER(string, parse_string);
+ MAKE_PARSER(StringPiece, parse_stringpiece);
+
MAKE_PARSER(short, parse_short);
MAKE_PARSER(unsigned short, parse_ushort);
MAKE_PARSER(int, parse_int);
@@ -598,10 +595,6 @@ class PCRE::Arg {
MAKE_PARSER(unsigned long, parse_ulong);
MAKE_PARSER(long long, parse_longlong);
MAKE_PARSER(unsigned long long, parse_ulonglong);
- MAKE_PARSER(float, parse_float);
- MAKE_PARSER(double, parse_double);
- MAKE_PARSER(string, parse_string);
- MAKE_PARSER(StringPiece, parse_stringpiece);
#undef MAKE_PARSER
@@ -613,29 +606,31 @@ class PCRE::Arg {
}
// Parse the data
- bool Parse(const char* str, int n) const;
+ bool Parse(const char* str, size_t n) const;
private:
void* arg_;
Parser parser_;
- static bool parse_null (const char* str, int n, void* dest);
- static bool parse_char (const char* str, int n, void* dest);
- static bool parse_uchar (const char* str, int n, void* dest);
- static bool parse_float (const char* str, int n, void* dest);
- static bool parse_double (const char* str, int n, void* dest);
- static bool parse_string (const char* str, int n, void* dest);
- static bool parse_stringpiece (const char* str, int n, void* dest);
-
-#define DECLARE_INTEGER_PARSER(name) \
- private: \
- static bool parse_ ## name(const char* str, int n, void* dest); \
- static bool parse_ ## name ## _radix( \
- const char* str, int n, void* dest, int radix); \
- public: \
- static bool parse_ ## name ## _hex(const char* str, int n, void* dest); \
- static bool parse_ ## name ## _octal(const char* str, int n, void* dest); \
- static bool parse_ ## name ## _cradix(const char* str, int n, void* dest)
+ static bool parse_null (const char* str, size_t n, void* dest);
+ static bool parse_char (const char* str, size_t n, void* dest);
+ static bool parse_schar (const char* str, size_t n, void* dest);
+ static bool parse_uchar (const char* str, size_t n, void* dest);
+ static bool parse_float (const char* str, size_t n, void* dest);
+ static bool parse_double (const char* str, size_t n, void* dest);
+ static bool parse_string (const char* str, size_t n, void* dest);
+ static bool parse_stringpiece (const char* str, size_t n, void* dest);
+
+#define DECLARE_INTEGER_PARSER(name) \
+ private: \
+ static bool parse_##name(const char* str, size_t n, void* dest); \
+ static bool parse_##name##_radix(const char* str, size_t n, void* dest, \
+ int radix); \
+ \
+ public: \
+ static bool parse_##name##_hex(const char* str, size_t n, void* dest); \
+ static bool parse_##name##_octal(const char* str, size_t n, void* dest); \
+ static bool parse_##name##_cradix(const char* str, size_t n, void* dest)
DECLARE_INTEGER_PARSER(short);
DECLARE_INTEGER_PARSER(ushort);
@@ -647,23 +642,27 @@ class PCRE::Arg {
DECLARE_INTEGER_PARSER(ulonglong);
#undef DECLARE_INTEGER_PARSER
+
};
inline PCRE::Arg::Arg() : arg_(NULL), parser_(parse_null) { }
inline PCRE::Arg::Arg(void* p) : arg_(p), parser_(parse_null) { }
-inline bool PCRE::Arg::Parse(const char* str, int n) const {
+inline bool PCRE::Arg::Parse(const char* str, size_t n) const {
return (*parser_)(str, n, arg_);
}
// This part of the parser, appropriate only for ints, deals with bases
-#define MAKE_INTEGER_PARSER(type, name) \
- inline PCRE::Arg Hex(type* ptr) { \
- return PCRE::Arg(ptr, PCRE::Arg::parse_ ## name ## _hex); } \
- inline PCRE::Arg Octal(type* ptr) { \
- return PCRE::Arg(ptr, PCRE::Arg::parse_ ## name ## _octal); } \
- inline PCRE::Arg CRadix(type* ptr) { \
- return PCRE::Arg(ptr, PCRE::Arg::parse_ ## name ## _cradix); }
+#define MAKE_INTEGER_PARSER(type, name) \
+ inline PCRE::Arg Hex(type* ptr) { \
+ return PCRE::Arg(ptr, PCRE::Arg::parse_##name##_hex); \
+ } \
+ inline PCRE::Arg Octal(type* ptr) { \
+ return PCRE::Arg(ptr, PCRE::Arg::parse_##name##_octal); \
+ } \
+ inline PCRE::Arg CRadix(type* ptr) { \
+ return PCRE::Arg(ptr, PCRE::Arg::parse_##name##_cradix); \
+ }
MAKE_INTEGER_PARSER(short, short);
MAKE_INTEGER_PARSER(unsigned short, ushort);
@@ -677,3 +676,5 @@ MAKE_INTEGER_PARSER(unsigned long long, ulonglong);
#undef MAKE_INTEGER_PARSER
} // namespace re2
+
+#endif // UTIL_PCRE_H_
diff --git a/util/pod_array.h b/util/pod_array.h
new file mode 100644
index 0000000..eaf492d
--- /dev/null
+++ b/util/pod_array.h
@@ -0,0 +1,55 @@
+// Copyright 2018 The RE2 Authors. All Rights Reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifndef UTIL_POD_ARRAY_H_
+#define UTIL_POD_ARRAY_H_
+
+#include <memory>
+#include <type_traits>
+
+namespace re2 {
+
+template <typename T>
+class PODArray {
+ public:
+ static_assert(std::is_pod<T>::value,
+ "T must be POD");
+
+ PODArray()
+ : ptr_() {}
+ explicit PODArray(int len)
+ : ptr_(std::allocator<T>().allocate(len), Deleter(len)) {}
+
+ T* data() const {
+ return ptr_.get();
+ }
+
+ int size() const {
+ return ptr_.get_deleter().len_;
+ }
+
+ T& operator[](int pos) const {
+ return ptr_[pos];
+ }
+
+ private:
+ struct Deleter {
+ Deleter()
+ : len_(0) {}
+ explicit Deleter(int len)
+ : len_(len) {}
+
+ void operator()(T* ptr) const {
+ std::allocator<T>().deallocate(ptr, len_);
+ }
+
+ int len_;
+ };
+
+ std::unique_ptr<T[], Deleter> ptr_;
+};
+
+} // namespace re2
+
+#endif // UTIL_POD_ARRAY_H_
diff --git a/util/random.cc b/util/random.cc
deleted file mode 100644
index 49d6195..0000000
--- a/util/random.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2005-2009 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Modified from Google perftools's tcmalloc_unittest.cc.
-
-#include "util/random.h"
-
-namespace re2 {
-
-int32 ACMRandom::Next() {
- const int32 M = 2147483647L; // 2^31-1
- const int32 A = 16807;
- // In effect, we are computing seed_ = (seed_ * A) % M, where M = 2^31-1
- uint32 lo = A * (int32)(seed_ & 0xFFFF);
- uint32 hi = A * (int32)((uint32)seed_ >> 16);
- lo += (hi & 0x7FFF) << 16;
- if (lo > M) {
- lo &= M;
- ++lo;
- }
- lo += hi >> 15;
- if (lo > M) {
- lo &= M;
- ++lo;
- }
- return (seed_ = (int32) lo);
-}
-
-int32 ACMRandom::Uniform(int32 n) {
- return Next() % n;
-}
-
-} // namespace re2
diff --git a/util/random.h b/util/random.h
deleted file mode 100644
index 6c6e701..0000000
--- a/util/random.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2005-2009 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Modified from Google perftools's tcmalloc_unittest.cc.
-
-#ifndef RE2_UTIL_RANDOM_H__
-#define RE2_UTIL_RANDOM_H__
-
-#include "util/util.h"
-
-namespace re2 {
-
-// ACM minimal standard random number generator. (re-entrant.)
-class ACMRandom {
- public:
- ACMRandom(int32 seed) : seed_(seed) {}
- int32 Next();
- int32 Uniform(int32);
-
- void Reset(int32 seed) { seed_ = seed; }
-
- private:
- int32 seed_;
-};
-
-} // namespace re2
-
-#endif // RE2_UTIL_RANDOM_H__
diff --git a/util/rune.cc b/util/rune.cc
index 26442b0..4f625ea 100644
--- a/util/rune.cc
+++ b/util/rune.cc
@@ -11,8 +11,10 @@
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
+
#include <stdarg.h>
#include <string.h>
+
#include "util/utf.h"
namespace re2 {
@@ -133,7 +135,7 @@ runetochar(char *str, const Rune *rune)
*/
c = *rune;
if(c <= Rune1) {
- str[0] = c;
+ str[0] = static_cast<char>(c);
return 1;
}
@@ -142,7 +144,7 @@ runetochar(char *str, const Rune *rune)
* 0080-07FF => T2 Tx
*/
if(c <= Rune2) {
- str[0] = T2 | (c >> 1*Bitx);
+ str[0] = T2 | static_cast<char>(c >> 1*Bitx);
str[1] = Tx | (c & Maskx);
return 2;
}
@@ -161,9 +163,9 @@ runetochar(char *str, const Rune *rune)
* 0800-FFFF => T3 Tx Tx
*/
if (c <= Rune3) {
- str[0] = T3 | (c >> 2*Bitx);
+ str[0] = T3 | static_cast<char>(c >> 2*Bitx);
str[1] = Tx | ((c >> 1*Bitx) & Maskx);
- str[2] = Tx | (c & Maskx);
+ str[2] = Tx | (c & Maskx);
return 3;
}
@@ -171,7 +173,7 @@ runetochar(char *str, const Rune *rune)
* four character sequence (21-bit value)
* 10000-1FFFFF => T4 Tx Tx Tx
*/
- str[0] = T4 | (c >> 3*Bitx);
+ str[0] = T4 | static_cast<char>(c >> 3*Bitx);
str[1] = Tx | ((c >> 2*Bitx) & Maskx);
str[2] = Tx | ((c >> 1*Bitx) & Maskx);
str[3] = Tx | (c & Maskx);
diff --git a/util/sparse_array.h b/util/sparse_array.h
index 3e33f89..c81c9f3 100644
--- a/util/sparse_array.h
+++ b/util/sparse_array.h
@@ -2,97 +2,107 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef UTIL_SPARSE_ARRAY_H_
+#define UTIL_SPARSE_ARRAY_H_
+
// DESCRIPTION
-//
+//
// SparseArray<T>(m) is a map from integers in [0, m) to T values.
// It requires (sizeof(T)+sizeof(int))*m memory, but it provides
// fast iteration through the elements in the array and fast clearing
// of the array. The array has a concept of certain elements being
// uninitialized (having no value).
-//
+//
// Insertion and deletion are constant time operations.
-//
-// Allocating the array is a constant time operation
+//
+// Allocating the array is a constant time operation
// when memory allocation is a constant time operation.
-//
+//
// Clearing the array is a constant time operation (unusual!).
-//
+//
// Iterating through the array is an O(n) operation, where n
// is the number of items in the array (not O(m)).
//
-// The array iterator visits entries in the order they were first
+// The array iterator visits entries in the order they were first
// inserted into the array. It is safe to add items to the array while
// using an iterator: the iterator will visit indices added to the array
// during the iteration, but will not re-visit indices whose values
// change after visiting. Thus SparseArray can be a convenient
// implementation of a work queue.
-//
+//
// The SparseArray implementation is NOT thread-safe. It is up to the
// caller to make sure only one thread is accessing the array. (Typically
// these arrays are temporary values and used in situations where speed is
// important.)
-//
+//
// The SparseArray interface does not present all the usual STL bells and
// whistles.
-//
+//
// Implemented with reference to Briggs & Torczon, An Efficient
// Representation for Sparse Sets, ACM Letters on Programming Languages
// and Systems, Volume 2, Issue 1-4 (March-Dec. 1993), pp. 59-69.
-//
+//
// Briggs & Torczon popularized this technique, but it had been known
// long before their paper. They point out that Aho, Hopcroft, and
// Ullman's 1974 Design and Analysis of Computer Algorithms and Bentley's
// 1986 Programming Pearls both hint at the technique in exercises to the
// reader (in Aho & Hopcroft, exercise 2.12; in Bentley, column 1
// exercise 8).
-//
+//
// Briggs & Torczon describe a sparse set implementation. I have
// trivially generalized it to create a sparse array (actually the original
// target of the AHU and Bentley exercises).
// IMPLEMENTATION
//
-// SparseArray uses a vector dense_ and an array sparse_to_dense_, both of
-// size max_size_. At any point, the number of elements in the sparse array is
-// size_.
-//
-// The vector dense_ contains the size_ elements in the sparse array (with
+// SparseArray is an array dense_ and an array sparse_ of identical size.
+// At any point, the number of elements in the sparse array is size_.
+//
+// The array dense_ contains the size_ elements in the sparse array (with
// their indices),
// in the order that the elements were first inserted. This array is dense:
// the size_ pairs are dense_[0] through dense_[size_-1].
//
-// The array sparse_to_dense_ maps from indices in [0,m) to indices in
-// [0,size_).
-// For indices present in the array, dense_[sparse_to_dense_[i]].index_ == i.
-// For indices not present in the array, sparse_to_dense_ can contain
-// any value at all, perhaps outside the range [0, size_) but perhaps not.
-//
-// The lax requirement on sparse_to_dense_ values makes clearing
-// the array very easy: set size_ to 0. Lookups are slightly more
-// complicated. An index i has a value in the array if and only if:
-// sparse_to_dense_[i] is in [0, size_) AND
-// dense_[sparse_to_dense_[i]].index_ == i.
-// If both these properties hold, only then it is safe to refer to
-// dense_[sparse_to_dense_[i]].value_
+// The array sparse_ maps from indices in [0,m) to indices in [0,size_).
+// For indices present in the array, dense_[sparse_[i]].index_ == i.
+// For indices not present in the array, sparse_ can contain any value at all,
+// perhaps outside the range [0, size_) but perhaps not.
+//
+// The lax requirement on sparse_ values makes clearing the array very easy:
+// set size_ to 0. Lookups are slightly more complicated.
+// An index i has a value in the array if and only if:
+// sparse_[i] is in [0, size_) AND
+// dense_[sparse_[i]].index_ == i.
+// If both these properties hold, only then it is safe to refer to
+// dense_[sparse_[i]].value_
// as the value associated with index i.
//
-// To insert a new entry, set sparse_to_dense_[i] to size_,
+// To insert a new entry, set sparse_[i] to size_,
// initialize dense_[size_], and then increment size_.
//
-// Deletion of specific values from the array is implemented by
-// swapping dense_[size_-1] and the dense_ being deleted and then
-// updating the appropriate sparse_to_dense_ entries.
-//
// To make the sparse array as efficient as possible for non-primitive types,
// elements may or may not be destroyed when they are deleted from the sparse
-// array through a call to erase(), erase_existing() or resize(). They
-// immediately become inaccessible, but they are only guaranteed to be
-// destroyed when the SparseArray destructor is called.
+// array through a call to resize(). They immediately become inaccessible, but
+// they are only guaranteed to be destroyed when the SparseArray destructor is
+// called.
+//
+// A moved-from SparseArray will be empty.
-#ifndef RE2_UTIL_SPARSE_ARRAY_H__
-#define RE2_UTIL_SPARSE_ARRAY_H__
+// Doing this simplifies the logic below.
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
-#include "util/util.h"
+#include <assert.h>
+#include <stdint.h>
+#if __has_feature(memory_sanitizer)
+#include <sanitizer/msan_interface.h>
+#endif
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "util/pod_array.h"
namespace re2 {
@@ -100,46 +110,57 @@ template<typename Value>
class SparseArray {
public:
SparseArray();
- SparseArray(int max_size);
+ explicit SparseArray(int max_size);
~SparseArray();
// IndexValue pairs: exposed in SparseArray::iterator.
class IndexValue;
- typedef IndexValue value_type;
- typedef typename vector<IndexValue>::iterator iterator;
- typedef typename vector<IndexValue>::const_iterator const_iterator;
+ typedef IndexValue* iterator;
+ typedef const IndexValue* const_iterator;
+
+ SparseArray(const SparseArray& src);
+ SparseArray(SparseArray&& src);
- inline const IndexValue& iv(int i) const;
+ SparseArray& operator=(const SparseArray& src);
+ SparseArray& operator=(SparseArray&& src);
// Return the number of entries in the array.
int size() const {
return size_;
}
+ // Indicate whether the array is empty.
+ int empty() const {
+ return size_ == 0;
+ }
+
// Iterate over the array.
iterator begin() {
- return dense_.begin();
+ return dense_.data();
}
iterator end() {
- return dense_.begin() + size_;
+ return dense_.data() + size_;
}
const_iterator begin() const {
- return dense_.begin();
+ return dense_.data();
}
const_iterator end() const {
- return dense_.begin() + size_;
+ return dense_.data() + size_;
}
// Change the maximum size of the array.
// Invalidates all iterators.
- void resize(int max_size);
+ void resize(int new_max_size);
// Return the maximum size of the array.
// Indices can be in the range [0, max_size).
int max_size() const {
- return max_size_;
+ if (dense_.data() != NULL)
+ return dense_.size();
+ else
+ return 0;
}
// Clear the array.
@@ -148,298 +169,216 @@ class SparseArray {
}
// Check whether index i is in the array.
- inline bool has_index(int i) const;
+ bool has_index(int i) const;
// Comparison function for sorting.
// Can sort the sparse array so that future iterations
// will visit indices in increasing order using
- // sort(arr.begin(), arr.end(), arr.less);
+ // std::sort(arr.begin(), arr.end(), arr.less);
static bool less(const IndexValue& a, const IndexValue& b);
public:
// Set the value at index i to v.
- inline iterator set(int i, Value v);
-
- pair<iterator, bool> insert(const value_type& new_value);
-
- // Returns the value at index i
- // or defaultv if index i is not initialized in the array.
- inline Value get(int i, Value defaultv) const;
-
- iterator find(int i);
-
- const_iterator find(int i) const;
-
- // Change the value at index i to v.
- // Fast but unsafe: only use if has_index(i) is true.
- inline iterator set_existing(int i, Value v);
+ iterator set(int i, const Value& v) {
+ return SetInternal(true, i, v);
+ }
- // Set the value at the new index i to v.
+ // Set the value at new index i to v.
// Fast but unsafe: only use if has_index(i) is false.
- inline iterator set_new(int i, Value v);
+ iterator set_new(int i, const Value& v) {
+ return SetInternal(false, i, v);
+ }
- // Get the value at index i from the array..
+ // Set the value at index i to v.
// Fast but unsafe: only use if has_index(i) is true.
- inline Value get_existing(int i) const;
-
- // Erasing items from the array during iteration is in general
- // NOT safe. There is one special case, which is that the current
- // index-value pair can be erased as long as the iterator is then
- // checked for being at the end before being incremented.
- // For example:
- //
- // for (i = m.begin(); i != m.end(); ++i) {
- // if (ShouldErase(i->index(), i->value())) {
- // m.erase(i->index());
- // --i;
- // }
- // }
- //
- // Except in the specific case just described, elements must
- // not be erased from the array (including clearing the array)
- // while iterators are walking over the array. Otherwise,
- // the iterators could walk past the end of the array.
-
- // Erases the element at index i from the array.
- inline void erase(int i);
-
- // Erases the element at index i from the array.
+ iterator set_existing(int i, const Value& v) {
+ return SetExistingInternal(i, v);
+ }
+
+ // Get the value at index i.
// Fast but unsafe: only use if has_index(i) is true.
- inline void erase_existing(int i);
+ Value& get_existing(int i) {
+ assert(has_index(i));
+ return dense_[sparse_[i]].value_;
+ }
+ const Value& get_existing(int i) const {
+ assert(has_index(i));
+ return dense_[sparse_[i]].value_;
+ }
private:
+ iterator SetInternal(bool allow_existing, int i, const Value& v) {
+ DebugCheckInvariants();
+ if (static_cast<uint32_t>(i) >= static_cast<uint32_t>(max_size())) {
+ assert(false && "illegal index");
+ // Semantically, end() would be better here, but we already know
+ // the user did something stupid, so begin() insulates them from
+ // dereferencing an invalid pointer.
+ return begin();
+ }
+ if (!allow_existing) {
+ assert(!has_index(i));
+ create_index(i);
+ } else {
+ if (!has_index(i))
+ create_index(i);
+ }
+ return SetExistingInternal(i, v);
+ }
+
+ iterator SetExistingInternal(int i, const Value& v) {
+ DebugCheckInvariants();
+ assert(has_index(i));
+ dense_[sparse_[i]].value_ = v;
+ DebugCheckInvariants();
+ return dense_.data() + sparse_[i];
+ }
+
// Add the index i to the array.
// Only use if has_index(i) is known to be false.
// Since it doesn't set the value associated with i,
// this function is private, only intended as a helper
// for other methods.
- inline void create_index(int i);
+ void create_index(int i);
// In debug mode, verify that some invariant properties of the class
// are being maintained. This is called at the end of the constructor
// and at the beginning and end of all public non-const member functions.
- inline void DebugCheckInvariants() const;
-
- int size_;
- int max_size_;
- int* sparse_to_dense_;
- vector<IndexValue> dense_;
- bool valgrind_;
+ void DebugCheckInvariants() const;
+
+ // Initializes memory for elements [min, max).
+ void MaybeInitializeMemory(int min, int max) {
+#if __has_feature(memory_sanitizer)
+ __msan_unpoison(sparse_.data() + min, (max - min) * sizeof sparse_[0]);
+#elif defined(RE2_ON_VALGRIND)
+ for (int i = min; i < max; i++) {
+ sparse_[i] = 0xababababU;
+ }
+#endif
+ }
- DISALLOW_EVIL_CONSTRUCTORS(SparseArray);
+ int size_ = 0;
+ PODArray<int> sparse_;
+ PODArray<IndexValue> dense_;
};
template<typename Value>
-SparseArray<Value>::SparseArray()
- : size_(0), max_size_(0), sparse_to_dense_(NULL), dense_(), valgrind_(RunningOnValgrind()) {}
-
-// IndexValue pairs: exposed in SparseArray::iterator.
-template<typename Value>
-class SparseArray<Value>::IndexValue {
- friend class SparseArray;
- public:
- typedef int first_type;
- typedef Value second_type;
-
- IndexValue() {}
- IndexValue(int index, const Value& value) : second(value), index_(index) {}
-
- int index() const { return index_; }
- Value value() const { return second; }
-
- // Provide the data in the 'second' member so that the utilities
- // in map-util work.
- Value second;
-
- private:
- int index_;
-};
+SparseArray<Value>::SparseArray() = default;
template<typename Value>
-const typename SparseArray<Value>::IndexValue&
-SparseArray<Value>::iv(int i) const {
- DCHECK_GE(i, 0);
- DCHECK_LT(i, size_);
- return dense_[i];
+SparseArray<Value>::SparseArray(const SparseArray& src)
+ : size_(src.size_),
+ sparse_(src.max_size()),
+ dense_(src.max_size()) {
+ std::copy_n(src.sparse_.data(), src.max_size(), sparse_.data());
+ std::copy_n(src.dense_.data(), src.max_size(), dense_.data());
}
-// Change the maximum size of the array.
-// Invalidates all iterators.
template<typename Value>
-void SparseArray<Value>::resize(int new_max_size) {
- DebugCheckInvariants();
- if (new_max_size > max_size_) {
- int* a = new int[new_max_size];
- if (sparse_to_dense_) {
- memmove(a, sparse_to_dense_, max_size_*sizeof a[0]);
- // Don't need to zero the memory but appease Valgrind.
- if (valgrind_) {
- for (int i = max_size_; i < new_max_size; i++)
- a[i] = 0xababababU;
- }
- delete[] sparse_to_dense_;
- }
- sparse_to_dense_ = a;
-
- dense_.resize(new_max_size);
- }
- max_size_ = new_max_size;
- if (size_ > max_size_)
- size_ = max_size_;
- DebugCheckInvariants();
-}
-
-// Check whether index i is in the array.
-template<typename Value>
-bool SparseArray<Value>::has_index(int i) const {
- DCHECK_GE(i, 0);
- DCHECK_LT(i, max_size_);
- if (static_cast<uint>(i) >= max_size_) {
- return false;
- }
- // Unsigned comparison avoids checking sparse_to_dense_[i] < 0.
- return (uint)sparse_to_dense_[i] < (uint)size_ &&
- dense_[sparse_to_dense_[i]].index_ == i;
-}
-
-// Set the value at index i to v.
-template<typename Value>
-typename SparseArray<Value>::iterator SparseArray<Value>::set(int i, Value v) {
- DebugCheckInvariants();
- if (static_cast<uint>(i) >= max_size_) {
- // Semantically, end() would be better here, but we already know
- // the user did something stupid, so begin() insulates them from
- // dereferencing an invalid pointer.
- return begin();
- }
- if (!has_index(i))
- create_index(i);
- return set_existing(i, v);
+SparseArray<Value>::SparseArray(SparseArray&& src)
+ : size_(src.size_),
+ sparse_(std::move(src.sparse_)),
+ dense_(std::move(src.dense_)) {
+ src.size_ = 0;
}
template<typename Value>
-pair<typename SparseArray<Value>::iterator, bool> SparseArray<Value>::insert(
- const value_type& new_value) {
- DebugCheckInvariants();
- pair<typename SparseArray<Value>::iterator, bool> p;
- if (has_index(new_value.index_)) {
- p = make_pair(dense_.begin() + sparse_to_dense_[new_value.index_], false);
- } else {
- p = make_pair(set_new(new_value.index_, new_value.second), true);
- }
- DebugCheckInvariants();
- return p;
+SparseArray<Value>& SparseArray<Value>::operator=(const SparseArray& src) {
+ // Construct these first for exception safety.
+ PODArray<int> a(src.max_size());
+ PODArray<IndexValue> b(src.max_size());
+
+ size_ = src.size_;
+ sparse_ = std::move(a);
+ dense_ = std::move(b);
+ std::copy_n(src.sparse_.data(), src.max_size(), sparse_.data());
+ std::copy_n(src.dense_.data(), src.max_size(), dense_.data());
+ return *this;
}
template<typename Value>
-Value SparseArray<Value>::get(int i, Value defaultv) const {
- if (!has_index(i))
- return defaultv;
- return get_existing(i);
+SparseArray<Value>& SparseArray<Value>::operator=(SparseArray&& src) {
+ size_ = src.size_;
+ sparse_ = std::move(src.sparse_);
+ dense_ = std::move(src.dense_);
+ src.size_ = 0;
+ return *this;
}
+// IndexValue pairs: exposed in SparseArray::iterator.
template<typename Value>
-typename SparseArray<Value>::iterator SparseArray<Value>::find(int i) {
- if (has_index(i))
- return dense_.begin() + sparse_to_dense_[i];
- return end();
-}
+class SparseArray<Value>::IndexValue {
+ public:
+ int index() const { return index_; }
+ Value& value() { return value_; }
+ const Value& value() const { return value_; }
-template<typename Value>
-typename SparseArray<Value>::const_iterator
-SparseArray<Value>::find(int i) const {
- if (has_index(i)) {
- return dense_.begin() + sparse_to_dense_[i];
- }
- return end();
-}
+ private:
+ friend class SparseArray;
+ int index_;
+ Value value_;
+};
+// Change the maximum size of the array.
+// Invalidates all iterators.
template<typename Value>
-typename SparseArray<Value>::iterator
-SparseArray<Value>::set_existing(int i, Value v) {
- DebugCheckInvariants();
- DCHECK(has_index(i));
- dense_[sparse_to_dense_[i]].second = v;
+void SparseArray<Value>::resize(int new_max_size) {
DebugCheckInvariants();
- return dense_.begin() + sparse_to_dense_[i];
-}
+ if (new_max_size > max_size()) {
+ const int old_max_size = max_size();
-template<typename Value>
-typename SparseArray<Value>::iterator
-SparseArray<Value>::set_new(int i, Value v) {
- DebugCheckInvariants();
- if (static_cast<uint>(i) >= max_size_) {
- // Semantically, end() would be better here, but we already know
- // the user did something stupid, so begin() insulates them from
- // dereferencing an invalid pointer.
- return begin();
- }
- DCHECK(!has_index(i));
- create_index(i);
- return set_existing(i, v);
-}
+ // Construct these first for exception safety.
+ PODArray<int> a(new_max_size);
+ PODArray<IndexValue> b(new_max_size);
-template<typename Value>
-Value SparseArray<Value>::get_existing(int i) const {
- DCHECK(has_index(i));
- return dense_[sparse_to_dense_[i]].second;
-}
+ std::copy_n(sparse_.data(), old_max_size, a.data());
+ std::copy_n(dense_.data(), old_max_size, b.data());
-template<typename Value>
-void SparseArray<Value>::erase(int i) {
- DebugCheckInvariants();
- if (has_index(i))
- erase_existing(i);
+ sparse_ = std::move(a);
+ dense_ = std::move(b);
+
+ MaybeInitializeMemory(old_max_size, new_max_size);
+ }
+ if (size_ > new_max_size)
+ size_ = new_max_size;
DebugCheckInvariants();
}
+// Check whether index i is in the array.
template<typename Value>
-void SparseArray<Value>::erase_existing(int i) {
- DebugCheckInvariants();
- DCHECK(has_index(i));
- int di = sparse_to_dense_[i];
- if (di < size_ - 1) {
- dense_[di] = dense_[size_ - 1];
- sparse_to_dense_[dense_[di].index_] = di;
+bool SparseArray<Value>::has_index(int i) const {
+ assert(i >= 0);
+ assert(i < max_size());
+ if (static_cast<uint32_t>(i) >= static_cast<uint32_t>(max_size())) {
+ return false;
}
- size_--;
- DebugCheckInvariants();
+ // Unsigned comparison avoids checking sparse_[i] < 0.
+ return (uint32_t)sparse_[i] < (uint32_t)size_ &&
+ dense_[sparse_[i]].index_ == i;
}
template<typename Value>
void SparseArray<Value>::create_index(int i) {
- DCHECK(!has_index(i));
- DCHECK_LT(size_, max_size_);
- sparse_to_dense_[i] = size_;
+ assert(!has_index(i));
+ assert(size_ < max_size());
+ sparse_[i] = size_;
dense_[size_].index_ = i;
size_++;
}
-template<typename Value> SparseArray<Value>::SparseArray(int max_size) {
- max_size_ = max_size;
- sparse_to_dense_ = new int[max_size];
- valgrind_ = RunningOnValgrind();
- dense_.resize(max_size);
- // Don't need to zero the new memory, but appease Valgrind.
- if (valgrind_) {
- for (int i = 0; i < max_size; i++) {
- sparse_to_dense_[i] = 0xababababU;
- dense_[i].index_ = 0xababababU;
- }
- }
- size_ = 0;
+template<typename Value> SparseArray<Value>::SparseArray(int max_size) :
+ sparse_(max_size), dense_(max_size) {
+ MaybeInitializeMemory(size_, max_size);
DebugCheckInvariants();
}
template<typename Value> SparseArray<Value>::~SparseArray() {
DebugCheckInvariants();
- delete[] sparse_to_dense_;
}
template<typename Value> void SparseArray<Value>::DebugCheckInvariants() const {
- DCHECK_LE(0, size_);
- DCHECK_LE(size_, max_size_);
- DCHECK(size_ == 0 || sparse_to_dense_ != NULL);
+ assert(0 <= size_);
+ assert(size_ <= max_size());
}
// Comparison function for sorting.
@@ -450,4 +389,4 @@ template<typename Value> bool SparseArray<Value>::less(const IndexValue& a,
} // namespace re2
-#endif // RE2_UTIL_SPARSE_ARRAY_H__
+#endif // UTIL_SPARSE_ARRAY_H_
diff --git a/util/sparse_array_test.cc b/util/sparse_array_test.cc
deleted file mode 100644
index bc7a19f..0000000
--- a/util/sparse_array_test.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2006 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Simple tests that SparseArray behaves.
-
-#include "util/util.h"
-#include "utest/utest.h"
-
-namespace re2 {
-
-static const string kNotFound = "NOT FOUND";
-
-TEST(SparseArray, BasicOperations) {
- static const int n = 50;
- SparseArray<int> set(n);
-
- int order[n];
- int value[n];
- for (int i = 0; i < n; i++)
- order[i] = i;
- for (int i = 0; i < n; i++)
- value[i] = rand()%1000 + 1;
- for (int i = 1; i < n; i++) {
- int j = rand()%i;
- int t = order[i];
- order[i] = order[j];
- order[j] = t;
- }
-
- for (int i = 0;; i++) {
- for (int j = 0; j < i; j++) {
- ASSERT_TRUE(set.has_index(order[j]));
- ASSERT_EQ(value[order[j]], set.get(order[j], -1));
- }
- if (i >= n)
- break;
- for (int j = i; j < n; j++)
- ASSERT_FALSE(set.has_index(order[j]));
- set.set(order[i], value[order[i]]);
- }
-
- int nn = 0;
- for (SparseArray<int>::iterator i = set.begin(); i != set.end(); ++i) {
- ASSERT_EQ(order[nn++], i->index());
- ASSERT_EQ(value[i->index()], i->value());
- }
- ASSERT_EQ(nn, n);
-
- set.clear();
- for (int i = 0; i < n; i++)
- ASSERT_FALSE(set.has_index(i));
-
- ASSERT_EQ(0, set.size());
- ASSERT_EQ(0, distance(set.begin(), set.end()));
-}
-
-class SparseArrayStringTest : public testing::Test {
- protected:
- SparseArrayStringTest()
- : str_map_(10) {
- InsertOrUpdate(&str_map_, 1, "a");
- InsertOrUpdate(&str_map_, 5, "b");
- InsertOrUpdate(&str_map_, 2, "c");
- InsertOrUpdate(&str_map_, 7, "d");
- }
-
- SparseArray<string> str_map_;
- typedef SparseArray<string>::iterator iterator;
-};
-
-TEST_F(SparseArrayStringTest, FindGetsPresentElement) {
- iterator it = str_map_.find(2);
- ASSERT_TRUE(str_map_.end() != it);
- EXPECT_EQ("c", it->second);
-}
-
-TEST_F(SparseArrayStringTest, FindDoesNotFindAbsentElement) {
- iterator it = str_map_.find(3);
- ASSERT_TRUE(str_map_.end() == it);
-}
-
-TEST_F(SparseArrayStringTest, ContainsKey) {
- EXPECT_TRUE(ContainsKey(str_map_, 1));
- EXPECT_TRUE(ContainsKey(str_map_, 2));
- EXPECT_FALSE(ContainsKey(str_map_, 3));
-}
-
-TEST_F(SparseArrayStringTest, InsertIfNotPresent) {
- EXPECT_FALSE(ContainsKey(str_map_, 3));
- EXPECT_TRUE(InsertIfNotPresent(&str_map_, 3, "r"));
- EXPECT_EQ("r", FindWithDefault(str_map_, 3, kNotFound));
- EXPECT_FALSE(InsertIfNotPresent(&str_map_, 3, "other value"));
- EXPECT_EQ("r", FindWithDefault(str_map_, 3, kNotFound));
-}
-
-TEST(SparseArrayTest, Erase) {
- SparseArray<string> str_map(5);
- str_map.set(1, "a");
- str_map.set(2, "b");
- EXPECT_EQ("a", FindWithDefault(str_map, 1, kNotFound));
- EXPECT_EQ("b", FindWithDefault(str_map, 2, kNotFound));
- str_map.erase(1);
- EXPECT_EQ("NOT FOUND", FindWithDefault(str_map, 1, kNotFound));
- EXPECT_EQ("b", FindWithDefault(str_map, 2, kNotFound));
-}
-
-typedef SparseArrayStringTest SparseArrayStringSurvivesInvalidIndexTest;
-// TODO(jyasskin): Cover invalid arguments to every method.
-
-TEST_F(SparseArrayStringSurvivesInvalidIndexTest, SetNegative) {
- EXPECT_DEBUG_DEATH(str_map_.set(-123456789, "hi"),
- "\\(jyasskin\\) Illegal index -123456789 passed to"
- " SparseArray\\(10\\).set\\(\\).");
- EXPECT_EQ(4, str_map_.size());
-}
-
-TEST_F(SparseArrayStringSurvivesInvalidIndexTest, SetTooBig) {
- EXPECT_DEBUG_DEATH(str_map_.set(12345678, "hi"),
- "\\(jyasskin\\) Illegal index 12345678 passed to"
- " SparseArray\\(10\\).set\\(\\).");
- EXPECT_EQ(4, str_map_.size());
-}
-
-TEST_F(SparseArrayStringSurvivesInvalidIndexTest, SetNew_Negative) {
- EXPECT_DEBUG_DEATH(str_map_.set_new(-123456789, "hi"),
- "\\(jyasskin\\) Illegal index -123456789 passed to"
- " SparseArray\\(10\\).set_new\\(\\).");
- EXPECT_EQ(4, str_map_.size());
-}
-
-TEST_F(SparseArrayStringSurvivesInvalidIndexTest, SetNew_Existing) {
- EXPECT_DEBUG_DEATH({
- str_map_.set_new(2, "hi");
- EXPECT_EQ("hi", FindWithDefault(str_map_, 2, kNotFound));
-
- // The old value for 2 is still present, but can never be removed.
- // This risks crashing later, if the map fills up.
- EXPECT_EQ(5, str_map_.size());
- }, "Check failed: !has_index\\(i\\)");
-}
-
-TEST_F(SparseArrayStringSurvivesInvalidIndexTest, SetNew_TooBig) {
- EXPECT_DEBUG_DEATH(str_map_.set_new(12345678, "hi"),
- "\\(jyasskin\\) Illegal index 12345678 passed to"
- " SparseArray\\(10\\).set_new\\(\\).");
- EXPECT_EQ(4, str_map_.size());
-}
-
-} // namespace re2
diff --git a/util/sparse_set.h b/util/sparse_set.h
index 165dd09..0d5ad51 100644
--- a/util/sparse_set.h
+++ b/util/sparse_set.h
@@ -2,178 +2,263 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#ifndef UTIL_SPARSE_SET_H_
+#define UTIL_SPARSE_SET_H_
+
// DESCRIPTION
-//
-// SparseSet<T>(m) is a set of integers in [0, m).
+//
+// SparseSet(m) is a set of integers in [0, m).
// It requires sizeof(int)*m memory, but it provides
// fast iteration through the elements in the set and fast clearing
// of the set.
-//
+//
// Insertion and deletion are constant time operations.
-//
-// Allocating the set is a constant time operation
+//
+// Allocating the set is a constant time operation
// when memory allocation is a constant time operation.
-//
+//
// Clearing the set is a constant time operation (unusual!).
-//
+//
// Iterating through the set is an O(n) operation, where n
// is the number of items in the set (not O(m)).
//
-// The set iterator visits entries in the order they were first
-// inserted into the array. It is safe to add items to the set while
+// The set iterator visits entries in the order they were first
+// inserted into the set. It is safe to add items to the set while
// using an iterator: the iterator will visit indices added to the set
// during the iteration, but will not re-visit indices whose values
// change after visiting. Thus SparseSet can be a convenient
// implementation of a work queue.
-//
+//
// The SparseSet implementation is NOT thread-safe. It is up to the
// caller to make sure only one thread is accessing the set. (Typically
// these sets are temporary values and used in situations where speed is
// important.)
-//
+//
// The SparseSet interface does not present all the usual STL bells and
// whistles.
-//
+//
// Implemented with reference to Briggs & Torczon, An Efficient
// Representation for Sparse Sets, ACM Letters on Programming Languages
// and Systems, Volume 2, Issue 1-4 (March-Dec. 1993), pp. 59-69.
-//
-// For a generalization to sparse array, see sparse_array.h.
+//
+// This is a specialization of sparse array; see sparse_array.h.
// IMPLEMENTATION
//
-// See sparse_array.h for implementation details
+// See sparse_array.h for implementation details.
-#ifndef RE2_UTIL_SPARSE_SET_H__
-#define RE2_UTIL_SPARSE_SET_H__
+// Doing this simplifies the logic below.
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
-#include "util/util.h"
+#include <assert.h>
+#include <stdint.h>
+#if __has_feature(memory_sanitizer)
+#include <sanitizer/msan_interface.h>
+#endif
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "util/pod_array.h"
namespace re2 {
-class SparseSet {
+template<typename Value>
+class SparseSetT {
public:
- SparseSet()
- : size_(0), max_size_(0), sparse_to_dense_(NULL), dense_(NULL), valgrind_(RunningOnValgrind()) {}
-
- SparseSet(int max_size) {
- max_size_ = max_size;
- sparse_to_dense_ = new int[max_size];
- dense_ = new int[max_size];
- valgrind_ = RunningOnValgrind();
- // Don't need to zero the memory, but do so anyway
- // to appease Valgrind.
- if (valgrind_) {
- for (int i = 0; i < max_size; i++) {
- dense_[i] = 0xababababU;
- sparse_to_dense_[i] = 0xababababU;
- }
- }
- size_ = 0;
+ SparseSetT();
+ explicit SparseSetT(int max_size);
+ ~SparseSetT();
+
+ typedef int* iterator;
+ typedef const int* const_iterator;
+
+ // Return the number of entries in the set.
+ int size() const {
+ return size_;
}
- ~SparseSet() {
- delete[] sparse_to_dense_;
- delete[] dense_;
+ // Indicate whether the set is empty.
+ int empty() const {
+ return size_ == 0;
}
- typedef int* iterator;
- typedef const int* const_iterator;
+ // Iterate over the set.
+ iterator begin() {
+ return dense_.data();
+ }
+ iterator end() {
+ return dense_.data() + size_;
+ }
- int size() const { return size_; }
- iterator begin() { return dense_; }
- iterator end() { return dense_ + size_; }
- const_iterator begin() const { return dense_; }
- const_iterator end() const { return dense_ + size_; }
+ const_iterator begin() const {
+ return dense_.data();
+ }
+ const_iterator end() const {
+ return dense_.data() + size_;
+ }
- // Change the maximum size of the array.
+ // Change the maximum size of the set.
// Invalidates all iterators.
- void resize(int new_max_size) {
- if (size_ > new_max_size)
- size_ = new_max_size;
- if (new_max_size > max_size_) {
- int* a = new int[new_max_size];
- if (sparse_to_dense_) {
- memmove(a, sparse_to_dense_, max_size_*sizeof a[0]);
- if (valgrind_) {
- for (int i = max_size_; i < new_max_size; i++)
- a[i] = 0xababababU;
- }
- delete[] sparse_to_dense_;
- }
- sparse_to_dense_ = a;
-
- a = new int[new_max_size];
- if (dense_) {
- memmove(a, dense_, size_*sizeof a[0]);
- if (valgrind_) {
- for (int i = size_; i < new_max_size; i++)
- a[i] = 0xababababU;
- }
- delete[] dense_;
- }
- dense_ = a;
- }
- max_size_ = new_max_size;
- }
+ void resize(int new_max_size);
- // Return the maximum size of the array.
+ // Return the maximum size of the set.
// Indices can be in the range [0, max_size).
- int max_size() const { return max_size_; }
-
- // Clear the array.
- void clear() { size_ = 0; }
+ int max_size() const {
+ if (dense_.data() != NULL)
+ return dense_.size();
+ else
+ return 0;
+ }
- // Check whether i is in the array.
- bool contains(int i) const {
- DCHECK_GE(i, 0);
- DCHECK_LT(i, max_size_);
- if (static_cast<uint>(i) >= max_size_) {
- return false;
- }
- // Unsigned comparison avoids checking sparse_to_dense_[i] < 0.
- return (uint)sparse_to_dense_[i] < (uint)size_ &&
- dense_[sparse_to_dense_[i]] == i;
+ // Clear the set.
+ void clear() {
+ size_ = 0;
}
- // Adds i to the set.
- void insert(int i) {
- if (!contains(i))
- insert_new(i);
+ // Check whether index i is in the set.
+ bool contains(int i) const;
+
+ // Comparison function for sorting.
+ // Can sort the sparse set so that future iterations
+ // will visit indices in increasing order using
+ // std::sort(arr.begin(), arr.end(), arr.less);
+ static bool less(int a, int b);
+
+ public:
+ // Insert index i into the set.
+ iterator insert(int i) {
+ return InsertInternal(true, i);
}
- // Set the value at the new index i to v.
+ // Insert index i into the set.
// Fast but unsafe: only use if contains(i) is false.
- void insert_new(int i) {
- if (static_cast<uint>(i) >= max_size_) {
+ iterator insert_new(int i) {
+ return InsertInternal(false, i);
+ }
+
+ private:
+ iterator InsertInternal(bool allow_existing, int i) {
+ DebugCheckInvariants();
+ if (static_cast<uint32_t>(i) >= static_cast<uint32_t>(max_size())) {
+ assert(false && "illegal index");
// Semantically, end() would be better here, but we already know
// the user did something stupid, so begin() insulates them from
// dereferencing an invalid pointer.
- return;
+ return begin();
+ }
+ if (!allow_existing) {
+ assert(!contains(i));
+ create_index(i);
+ } else {
+ if (!contains(i))
+ create_index(i);
}
- DCHECK(!contains(i));
- DCHECK_LT(size_, max_size_);
- sparse_to_dense_[i] = size_;
- dense_[size_] = i;
- size_++;
+ DebugCheckInvariants();
+ return dense_.data() + sparse_[i];
}
- // Comparison function for sorting.
- // Can sort the sparse array so that future iterations
- // will visit indices in increasing order using
- // sort(arr.begin(), arr.end(), arr.less);
- static bool less(int a, int b) { return a < b; }
+ // Add the index i to the set.
+ // Only use if contains(i) is known to be false.
+ // This function is private, only intended as a helper
+ // for other methods.
+ void create_index(int i);
- private:
- int size_;
- int max_size_;
- int* sparse_to_dense_;
- int* dense_;
- bool valgrind_;
+ // In debug mode, verify that some invariant properties of the class
+ // are being maintained. This is called at the end of the constructor
+ // and at the beginning and end of all public non-const member functions.
+ void DebugCheckInvariants() const;
- DISALLOW_EVIL_CONSTRUCTORS(SparseSet);
+ // Initializes memory for elements [min, max).
+ void MaybeInitializeMemory(int min, int max) {
+#if __has_feature(memory_sanitizer)
+ __msan_unpoison(sparse_.data() + min, (max - min) * sizeof sparse_[0]);
+#elif defined(RE2_ON_VALGRIND)
+ for (int i = min; i < max; i++) {
+ sparse_[i] = 0xababababU;
+ }
+#endif
+ }
+
+ int size_ = 0;
+ PODArray<int> sparse_;
+ PODArray<int> dense_;
};
+template<typename Value>
+SparseSetT<Value>::SparseSetT() = default;
+
+// Change the maximum size of the set.
+// Invalidates all iterators.
+template<typename Value>
+void SparseSetT<Value>::resize(int new_max_size) {
+ DebugCheckInvariants();
+ if (new_max_size > max_size()) {
+ const int old_max_size = max_size();
+
+ // Construct these first for exception safety.
+ PODArray<int> a(new_max_size);
+ PODArray<int> b(new_max_size);
+
+ std::copy_n(sparse_.data(), old_max_size, a.data());
+ std::copy_n(dense_.data(), old_max_size, b.data());
+
+ sparse_ = std::move(a);
+ dense_ = std::move(b);
+
+ MaybeInitializeMemory(old_max_size, new_max_size);
+ }
+ if (size_ > new_max_size)
+ size_ = new_max_size;
+ DebugCheckInvariants();
+}
+
+// Check whether index i is in the set.
+template<typename Value>
+bool SparseSetT<Value>::contains(int i) const {
+ assert(i >= 0);
+ assert(i < max_size());
+ if (static_cast<uint32_t>(i) >= static_cast<uint32_t>(max_size())) {
+ return false;
+ }
+ // Unsigned comparison avoids checking sparse_[i] < 0.
+ return (uint32_t)sparse_[i] < (uint32_t)size_ &&
+ dense_[sparse_[i]] == i;
+}
+
+template<typename Value>
+void SparseSetT<Value>::create_index(int i) {
+ assert(!contains(i));
+ assert(size_ < max_size());
+ sparse_[i] = size_;
+ dense_[size_] = i;
+ size_++;
+}
+
+template<typename Value> SparseSetT<Value>::SparseSetT(int max_size) :
+ sparse_(max_size), dense_(max_size) {
+ MaybeInitializeMemory(size_, max_size);
+ DebugCheckInvariants();
+}
+
+template<typename Value> SparseSetT<Value>::~SparseSetT() {
+ DebugCheckInvariants();
+}
+
+template<typename Value> void SparseSetT<Value>::DebugCheckInvariants() const {
+ assert(0 <= size_);
+ assert(size_ <= max_size());
+}
+
+// Comparison function for sorting.
+template<typename Value> bool SparseSetT<Value>::less(int a, int b) {
+ return a < b;
+}
+
+typedef SparseSetT<void> SparseSet;
+
} // namespace re2
-#endif // RE2_UTIL_SPARSE_SET_H__
+#endif // UTIL_SPARSE_SET_H_
diff --git a/util/stringpiece.cc b/util/stringpiece.cc
deleted file mode 100644
index 37895b0..0000000
--- a/util/stringpiece.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2004 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "re2/stringpiece.h"
-#include "util/util.h"
-
-using re2::StringPiece;
-
-std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
- o.write(piece.data(), piece.size());
- return o;
-}
-
-bool StringPiece::_equal(const StringPiece& x, const StringPiece& y) {
- int len = x.size();
- if (len != y.size()) {
- return false;
- }
- const char* p = x.data();
- const char* p2 = y.data();
- // Test last byte in case strings share large common prefix
- if ((len > 0) && (p[len-1] != p2[len-1])) return false;
- const char* p_limit = p + len;
- for (; p < p_limit; p++, p2++) {
- if (*p != *p2)
- return false;
- }
- return true;
-}
-
-void StringPiece::CopyToString(string* target) const {
- target->assign(ptr_, length_);
-}
-
-int StringPiece::copy(char* buf, size_type n, size_type pos) const {
- int ret = min(length_ - pos, n);
- memcpy(buf, ptr_ + pos, ret);
- return ret;
-}
-
-int StringPiece::find(const StringPiece& s, size_type pos) const {
- if (length_ < 0 || pos > static_cast<size_type>(length_))
- return npos;
-
- const char* result = std::search(ptr_ + pos, ptr_ + length_,
- s.ptr_, s.ptr_ + s.length_);
- const size_type xpos = result - ptr_;
- return xpos + s.length_ <= length_ ? xpos : npos;
-}
-
-int StringPiece::find(char c, size_type pos) const {
- if (length_ <= 0 || pos >= static_cast<size_type>(length_)) {
- return npos;
- }
- const char* result = std::find(ptr_ + pos, ptr_ + length_, c);
- return result != ptr_ + length_ ? result - ptr_ : npos;
-}
-
-int StringPiece::rfind(const StringPiece& s, size_type pos) const {
- if (length_ < s.length_) return npos;
- const size_t ulen = length_;
- if (s.length_ == 0) return min(ulen, pos);
-
- const char* last = ptr_ + min(ulen - s.length_, pos) + s.length_;
- const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
- return result != last ? result - ptr_ : npos;
-}
-
-int StringPiece::rfind(char c, size_type pos) const {
- if (length_ <= 0) return npos;
- for (int i = min(pos, static_cast<size_type>(length_ - 1));
- i >= 0; --i) {
- if (ptr_[i] == c) {
- return i;
- }
- }
- return npos;
-}
-
-StringPiece StringPiece::substr(size_type pos, size_type n) const {
- if (pos > length_) pos = length_;
- if (n > length_ - pos) n = length_ - pos;
- return StringPiece(ptr_ + pos, n);
-}
-
-const StringPiece::size_type StringPiece::npos = size_type(-1);
diff --git a/util/stringprintf.cc b/util/stringprintf.cc
deleted file mode 100644
index c908181..0000000
--- a/util/stringprintf.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2002 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "util/util.h"
-
-namespace re2 {
-
-static void StringAppendV(string* dst, const char* format, va_list ap) {
- // First try with a small fixed size buffer
- char space[1024];
-
- // It's possible for methods that use a va_list to invalidate
- // the data in it upon use. The fix is to make a copy
- // of the structure before using it and use that copy instead.
- va_list backup_ap;
- va_copy(backup_ap, ap);
- int result = vsnprintf(space, sizeof(space), format, backup_ap);
- va_end(backup_ap);
-
- if ((result >= 0) && (result < sizeof(space))) {
- // It fit
- dst->append(space, result);
- return;
- }
-
- // Repeatedly increase buffer size until it fits
- int length = sizeof(space);
- while (true) {
- if (result < 0) {
- // Older behavior: just try doubling the buffer size
- length *= 2;
- } else {
- // We need exactly "result+1" characters
- length = result+1;
- }
- char* buf = new char[length];
-
- // Restore the va_list before we use it again
- va_copy(backup_ap, ap);
- result = vsnprintf(buf, length, format, backup_ap);
- va_end(backup_ap);
-
- if ((result >= 0) && (result < length)) {
- // It fit
- dst->append(buf, result);
- delete[] buf;
- return;
- }
- delete[] buf;
- }
-}
-
-string StringPrintf(const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- string result;
- StringAppendV(&result, format, ap);
- va_end(ap);
- return result;
-}
-
-void SStringPrintf(string* dst, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- dst->clear();
- StringAppendV(dst, format, ap);
- va_end(ap);
-}
-
-void StringAppendF(string* dst, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- StringAppendV(dst, format, ap);
- va_end(ap);
-}
-
-} // namespace re2
diff --git a/util/strutil.cc b/util/strutil.cc
index 6ab79b3..8eabfa4 100644
--- a/util/strutil.cc
+++ b/util/strutil.cc
@@ -2,8 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "util/util.h"
-#include "re2/stringpiece.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "util/strutil.h"
+
+#ifdef _WIN32
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
namespace re2 {
@@ -12,16 +19,16 @@ namespace re2 {
// Copies 'src' to 'dest', escaping dangerous characters using
// C-style escape sequences. 'src' and 'dest' should not overlap.
// Returns the number of bytes written to 'dest' (not including the \0)
-// or -1 if there was insufficient space.
+// or (size_t)-1 if there was insufficient space.
// ----------------------------------------------------------------------
-int CEscapeString(const char* src, int src_len, char* dest,
- int dest_len) {
+static size_t CEscapeString(const char* src, size_t src_len,
+ char* dest, size_t dest_len) {
const char* src_end = src + src_len;
- int used = 0;
+ size_t used = 0;
for (; src < src_end; src++) {
- if (dest_len - used < 2) // Need space for two letter escape
- return -1;
+ if (dest_len - used < 2) // space for two-character escape
+ return (size_t)-1;
unsigned char c = *src;
switch (c) {
@@ -36,9 +43,9 @@ int CEscapeString(const char* src, int src_len, char* dest,
// digit then that digit must be escaped too to prevent it being
// interpreted as part of the character code by C.
if (c < ' ' || c > '~') {
- if (dest_len - used < 4) // need space for 4 letter escape
- return -1;
- sprintf(dest + used, "\\%03o", c);
+ if (dest_len - used < 5) // space for four-character escape + \0
+ return (size_t)-1;
+ snprintf(dest + used, 5, "\\%03o", c);
used += 4;
} else {
dest[used++] = c; break;
@@ -47,51 +54,111 @@ int CEscapeString(const char* src, int src_len, char* dest,
}
if (dest_len - used < 1) // make sure that there is room for \0
- return -1;
+ return (size_t)-1;
dest[used] = '\0'; // doesn't count towards return value though
return used;
}
-
// ----------------------------------------------------------------------
// CEscape()
// Copies 'src' to result, escaping dangerous characters using
-// C-style escape sequences. 'src' and 'dest' should not overlap.
+// C-style escape sequences. 'src' and 'dest' should not overlap.
// ----------------------------------------------------------------------
string CEscape(const StringPiece& src) {
- const int dest_length = src.size() * 4 + 1; // Maximum possible expansion
- char* dest = new char[dest_length];
- const int len = CEscapeString(src.data(), src.size(),
- dest, dest_length);
- string s = string(dest, len);
+ const size_t dest_len = src.size() * 4 + 1; // Maximum possible expansion
+ char* dest = new char[dest_len];
+ const size_t used = CEscapeString(src.data(), src.size(),
+ dest, dest_len);
+ string s = string(dest, used);
delete[] dest;
return s;
}
-string PrefixSuccessor(const StringPiece& prefix) {
+void PrefixSuccessor(string* prefix) {
// We can increment the last character in the string and be done
// unless that character is 255, in which case we have to erase the
// last character and increment the previous character, unless that
// is 255, etc. If the string is empty or consists entirely of
// 255's, we just return the empty string.
- bool done = false;
- string limit(prefix.data(), prefix.size());
- int index = limit.length() - 1;
- while (!done && index >= 0) {
- if ((limit[index]&255) == 255) {
- limit.erase(index);
- index--;
+ while (!prefix->empty()) {
+ char& c = prefix->back();
+ if (c == '\xff') { // char literal avoids signed/unsigned.
+ prefix->pop_back();
} else {
- limit[index]++;
- done = true;
+ ++c;
+ break;
}
}
- if (!done) {
- return "";
- } else {
- return limit;
+}
+
+static void StringAppendV(string* dst, const char* format, va_list ap) {
+ // First try with a small fixed size buffer
+ char space[1024];
+
+ // It's possible for methods that use a va_list to invalidate
+ // the data in it upon use. The fix is to make a copy
+ // of the structure before using it and use that copy instead.
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ int result = vsnprintf(space, sizeof(space), format, backup_ap);
+ va_end(backup_ap);
+
+ if ((result >= 0) && (static_cast<size_t>(result) < sizeof(space))) {
+ // It fit
+ dst->append(space, result);
+ return;
}
+
+ // Repeatedly increase buffer size until it fits
+ int length = sizeof(space);
+ while (true) {
+ if (result < 0) {
+ // Older behavior: just try doubling the buffer size
+ length *= 2;
+ } else {
+ // We need exactly "result+1" characters
+ length = result+1;
+ }
+ char* buf = new char[length];
+
+ // Restore the va_list before we use it again
+ va_copy(backup_ap, ap);
+ result = vsnprintf(buf, length, format, backup_ap);
+ va_end(backup_ap);
+
+ if ((result >= 0) && (result < length)) {
+ // It fit
+ dst->append(buf, result);
+ delete[] buf;
+ return;
+ }
+ delete[] buf;
+ }
+}
+
+string StringPrintf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ string result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+
+void SStringPrintf(string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+void StringAppendF(string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
}
} // namespace re2
diff --git a/util/strutil.h b/util/strutil.h
new file mode 100644
index 0000000..2c3c104
--- /dev/null
+++ b/util/strutil.h
@@ -0,0 +1,23 @@
+// Copyright 2016 The RE2 Authors. All Rights Reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifndef UTIL_STRUTIL_H_
+#define UTIL_STRUTIL_H_
+
+#include <string>
+
+#include "re2/stringpiece.h"
+#include "util/util.h"
+
+namespace re2 {
+
+string CEscape(const StringPiece& src);
+void PrefixSuccessor(string* prefix);
+string StringPrintf(const char* format, ...);
+void SStringPrintf(string* dst, const char* format, ...);
+void StringAppendF(string* dst, const char* format, ...);
+
+} // namespace re2
+
+#endif // UTIL_STRUTIL_H_
diff --git a/util/test.cc b/util/test.cc
index 0644829..29c8b41 100644
--- a/util/test.cc
+++ b/util/test.cc
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
#include <stdio.h>
-#include <sys/resource.h>
+
#include "util/test.h"
DEFINE_string(test_tmpdir, "/var/tmp", "temp directory");
@@ -21,15 +21,7 @@ void RegisterTest(void (*fn)(void), const char *name) {
tests[ntests++].name = name;
}
-namespace re2 {
-int64 VirtualProcessSize() {
- struct rusage ru;
- getrusage(RUSAGE_SELF, &ru);
- return (int64)ru.ru_maxrss*1024;
-}
-} // namespace re2
-
-int main(int argc, char **argv) {
+int main(int argc, char** argv) {
for (int i = 0; i < ntests; i++) {
printf("%s\n", tests[i].name);
tests[i].fn();
diff --git a/util/test.h b/util/test.h
index 0f93865..5242e94 100644
--- a/util/test.h
+++ b/util/test.h
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#ifndef RE2_UTIL_TEST_H__
-#define RE2_UTIL_TEST_H__
+#ifndef UTIL_TEST_H_
+#define UTIL_TEST_H_
#include "util/util.h"
#include "util/flags.h"
+#include "util/logging.h"
#define TEST(x, y) \
void x##y(void); \
@@ -22,36 +23,36 @@ class TestRegisterer {
}
};
-// TODO(rsc): Do a better job.
-#define EXPECT_EQ CHECK_EQ
+// fatal assertions
+#define ASSERT_TRUE CHECK
+#define ASSERT_FALSE(x) CHECK(!(x))
+#define ASSERT_EQ CHECK_EQ
+#define ASSERT_NE CHECK_NE
+#define ASSERT_LT CHECK_LT
+#define ASSERT_LE CHECK_LE
+#define ASSERT_GT CHECK_GT
+#define ASSERT_GE CHECK_GE
+
+// nonfatal assertions
+// TODO(rsc): Do a better job?
#define EXPECT_TRUE CHECK
+#define EXPECT_FALSE(x) CHECK(!(x))
+#define EXPECT_EQ CHECK_EQ
+#define EXPECT_NE CHECK_NE
#define EXPECT_LT CHECK_LT
-#define EXPECT_GT CHECK_GT
#define EXPECT_LE CHECK_LE
+#define EXPECT_GT CHECK_GT
#define EXPECT_GE CHECK_GE
-#define EXPECT_FALSE(x) CHECK(!(x))
-
-#define ARRAYSIZE arraysize
-#define EXPECT_TRUE_M(x, y) CHECK(x) << (y)
-#define EXPECT_FALSE_M(x, y) CHECK(!(x)) << (y)
-#define ASSERT_TRUE_M(x, y) CHECK(x) << (y)
-#define ASSERT_EQUALS(x, y) CHECK_EQ(x, y)
-
-const bool UsingMallocCounter = false;
namespace testing {
class MallocCounter {
public:
- MallocCounter(int x) { }
+ MallocCounter(int x) {}
static const int THIS_THREAD_ONLY = 0;
long long HeapGrowth() { return 0; }
long long PeakHeapGrowth() { return 0; }
- void Reset() { }
+ void Reset() {}
};
} // namespace testing
-namespace re2 {
-int64 VirtualProcessSize();
-} // namespace re2
-
-#endif // RE2_UTIL_TEST_H__
+#endif // UTIL_TEST_H_
diff --git a/util/thread.cc b/util/thread.cc
deleted file mode 100644
index 7349991..0000000
--- a/util/thread.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2009 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include <pthread.h>
-
-#include "util/util.h"
-#include "util/thread.h"
-
-Thread::Thread() {
- pid_ = 0;
- running_ = 0;
- joinable_ = 0;
-}
-
-Thread::~Thread() {
-}
-
-void *startThread(void *v) {
- Thread* t = (Thread*)v;
- t->Run();
- return 0;
-}
-
-void Thread::Start() {
- CHECK(!running_);
- pthread_create(&pid_, 0, startThread, this);
- running_ = true;
- if (!joinable_)
- pthread_detach(pid_);
-}
-
-void Thread::Join() {
- CHECK(running_);
- CHECK(joinable_);
- void *val;
- pthread_join(pid_, &val);
- running_ = 0;
-}
-
-void Thread::SetJoinable(bool j) {
- CHECK(!running_);
- joinable_ = j;
-}
diff --git a/util/thread.h b/util/thread.h
deleted file mode 100644
index b9610e0..0000000
--- a/util/thread.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2009 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#ifndef RE2_UTIL_THREAD_H__
-#define RE2_UTIL_THREAD_H__
-
-#include <pthread.h>
-
-class Thread {
- public:
- Thread();
- virtual ~Thread();
- void Start();
- void Join();
- void SetJoinable(bool);
- virtual void Run() = 0;
-
- private:
- pthread_t pid_;
- bool running_;
- bool joinable_;
-};
-
-#endif // RE2_UTIL_THREAD_H__
-
diff --git a/util/utf.h b/util/utf.h
index 06ff8f0..85b4297 100644
--- a/util/utf.h
+++ b/util/utf.h
@@ -14,8 +14,9 @@
* This file and rune.cc have been converted to compile as C++ code
* in name space re2.
*/
-#ifndef RE2_UTIL_UTF_H__
-#define RE2_UTIL_UTF_H__
+
+#ifndef UTIL_UTF_H_
+#define UTIL_UTF_H_
#include <stdint.h>
@@ -40,4 +41,4 @@ char* utfrune(const char*, Rune);
} // namespace re2
-#endif // RE2_UTIL_UTF_H__
+#endif // UTIL_UTF_H_
diff --git a/util/util.h b/util/util.h
index 463cbfb..33d100a 100644
--- a/util/util.h
+++ b/util/util.h
@@ -2,139 +2,37 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#ifndef RE2_UTIL_UTIL_H__
-#define RE2_UTIL_UTIL_H__
+#ifndef UTIL_UTIL_H_
+#define UTIL_UTIL_H_
-// C
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <stddef.h> // For size_t
-#include <assert.h>
-#include <stdarg.h>
-#include <sys/time.h>
-#include <time.h>
-#include <ctype.h> // For isdigit, isalpha.
-
-// C++
-#include <vector>
+// TODO(junyer): Get rid of this.
#include <string>
-#include <algorithm>
-#include <iosfwd>
-#include <map>
-#include <stack>
-#include <ostream>
-#include <utility>
-#include <set>
-
-// Use std names.
-using std::set;
-using std::pair;
-using std::vector;
using std::string;
-using std::min;
-using std::max;
-using std::ostream;
-using std::map;
-using std::stack;
-using std::sort;
-using std::swap;
-using std::make_pair;
-#if defined(ANDROID)
+#define arraysize(array) (int)(sizeof(array)/sizeof((array)[0]))
-#if defined(_STLPORT_VERSION)
-#include <unordered_set> // using stlport
+#ifndef ATTRIBUTE_NORETURN
+#if defined(__GNUC__)
+#define ATTRIBUTE_NORETURN __attribute__((noreturn))
+#elif defined(_MSC_VER)
+#define ATTRIBUTE_NORETURN __declspec(noreturn)
#else
-#include <tr1/unordered_set> // using gnustl
+#define ATTRIBUTE_NORETURN
+#endif
#endif
-using std::tr1::unordered_set;
-
-#elif defined(__GNUC__) && !defined(USE_CXX0X)
-
-#include <tr1/unordered_set>
-using std::tr1::unordered_set;
+#ifndef FALLTHROUGH_INTENDED
+#if defined(__clang__)
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]]
+#elif defined(__GNUC__) && __GNUC__ >= 7
+#define FALLTHROUGH_INTENDED [[gnu::fallthrough]]
#else
-
-#include <unordered_set>
-using std::unordered_set;
-
+#define FALLTHROUGH_INTENDED do {} while (0)
+#endif
#endif
-namespace re2 {
-
-typedef int8_t int8;
-typedef uint8_t uint8;
-typedef int16_t int16;
-typedef uint16_t uint16;
-typedef int32_t int32;
-typedef uint32_t uint32;
-typedef int64_t int64;
-typedef uint64_t uint64;
-
-typedef unsigned long ulong;
-typedef unsigned int uint;
-typedef unsigned short ushort;
-
-// COMPILE_ASSERT causes a compile error about msg if expr is not true.
-template<bool> struct CompileAssert {};
-#define COMPILE_ASSERT(expr, msg) \
- typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
-
-// DISALLOW_EVIL_CONSTRUCTORS disallows the copy and operator= functions.
-// It goes in the private: declarations in a class.
-#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-
-#define arraysize(array) (sizeof(array)/sizeof((array)[0]))
-
-// Fake lock annotations. For real ones, see
-// http://code.google.com/p/data-race-test/
-#ifndef ANNOTATE_PUBLISH_MEMORY_RANGE
-#define ANNOTATE_PUBLISH_MEMORY_RANGE(a, b)
-#define ANNOTATE_IGNORE_WRITES_BEGIN()
-#define ANNOTATE_IGNORE_WRITES_END()
-#define ANNOTATE_BENIGN_RACE(a, b)
+#ifndef NO_THREAD_SAFETY_ANALYSIS
#define NO_THREAD_SAFETY_ANALYSIS
-#define ANNOTATE_HAPPENS_BEFORE(x)
-#define ANNOTATE_HAPPENS_AFTER(x)
-#define ANNOTATE_UNPROTECTED_READ(x) (x)
#endif
-class StringPiece;
-
-string CEscape(const StringPiece& src);
-int CEscapeString(const char* src, int src_len, char* dest, int dest_len);
-
-extern string StringPrintf(const char* format, ...);
-extern void SStringPrintf(string* dst, const char* format, ...);
-extern void StringAppendF(string* dst, const char* format, ...);
-extern string PrefixSuccessor(const StringPiece& prefix);
-
-uint32 hashword(const uint32*, size_t, uint32);
-void hashword2(const uint32*, size_t, uint32*, uint32*);
-
-static inline uint32 Hash32StringWithSeed(const char* s, int len, uint32 seed) {
- return hashword((uint32*)s, len/4, seed);
-}
-
-static inline uint64 Hash64StringWithSeed(const char* s, int len, uint32 seed) {
- uint32 x, y;
- x = seed;
- y = 0;
- hashword2((uint32*)s, len/4, &x, &y);
- return ((uint64)x << 32) | y;
-}
-
-int RunningOnValgrind();
-
-} // namespace re2
-
-#include "util/arena.h"
-#include "util/logging.h"
-#include "util/mutex.h"
-#include "util/utf.h"
-
-#endif // RE2_UTIL_UTIL_H__
+#endif // UTIL_UTIL_H_
diff --git a/util/valgrind.cc b/util/valgrind.cc
deleted file mode 100644
index 46f804b..0000000
--- a/util/valgrind.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2009 The RE2 Authors. All Rights Reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "util/util.h"
-#include "util/valgrind.h"
-
-namespace re2 {
-
-int RunningOnValgrind() {
-#ifdef RUNNING_ON_VALGRIND
- return RUNNING_ON_VALGRIND;
-#else
- return 0;
-#endif
-}
-
-} // namespace re2
diff --git a/util/valgrind.h b/util/valgrind.h
deleted file mode 100644
index b52442c..0000000
--- a/util/valgrind.h
+++ /dev/null
@@ -1,4517 +0,0 @@
-/* -*- c -*-
- ----------------------------------------------------------------
-
- Notice that the following BSD-style license applies to this one
- file (valgrind.h) only. The rest of Valgrind is licensed under the
- terms of the GNU General Public License, version 2, unless
- otherwise indicated. See the COPYING file in the source
- distribution for details.
-
- ----------------------------------------------------------------
-
- This file is part of Valgrind, a dynamic binary instrumentation
- framework.
-
- Copyright (C) 2000-2009 Julian Seward. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- 2. The origin of this software must not be misrepresented; you must
- not claim that you wrote the original software. If you use this
- software in a product, an acknowledgment in the product
- documentation would be appreciated but is not required.
-
- 3. Altered source versions must be plainly marked as such, and must
- not be misrepresented as being the original software.
-
- 4. The name of the author may not be used to endorse or promote
- products derived from this software without specific prior written
- permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
- OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- ----------------------------------------------------------------
-
- Notice that the above BSD-style license applies to this one file
- (valgrind.h) only. The entire rest of Valgrind is licensed under
- the terms of the GNU General Public License, version 2. See the
- COPYING file in the source distribution for details.
-
- ----------------------------------------------------------------
-*/
-
-
-/* This file is for inclusion into client (your!) code.
-
- You can use these macros to manipulate and query Valgrind's
- execution inside your own programs.
-
- The resulting executables will still run without Valgrind, just a
- little bit more slowly than they otherwise would, but otherwise
- unchanged. When not running on valgrind, each client request
- consumes very few (eg. 7) instructions, so the resulting performance
- loss is negligible unless you plan to execute client requests
- millions of times per second. Nevertheless, if that is still a
- problem, you can compile with the NVALGRIND symbol defined (gcc
- -DNVALGRIND) so that client requests are not even compiled in. */
-
-#ifndef __VALGRIND_H
-#define __VALGRIND_H
-
-#include <stdarg.h>
-
-/* Nb: this file might be included in a file compiled with -ansi. So
- we can't use C++ style "//" comments nor the "asm" keyword (instead
- use "__asm__"). */
-
-/* Derive some tags indicating what the target platform is. Note
- that in this file we're using the compiler's CPP symbols for
- identifying architectures, which are different to the ones we use
- within the rest of Valgrind. Note, __powerpc__ is active for both
- 32 and 64-bit PPC, whereas __powerpc64__ is only active for the
- latter (on Linux, that is).
-
- Misc note: how to find out what's predefined in gcc by default:
- gcc -Wp,-dM somefile.c
-*/
-#undef PLAT_ppc64_aix5
-#undef PLAT_ppc32_aix5
-#undef PLAT_x86_darwin
-#undef PLAT_amd64_darwin
-#undef PLAT_x86_linux
-#undef PLAT_amd64_linux
-#undef PLAT_ppc32_linux
-#undef PLAT_ppc64_linux
-#undef PLAT_arm_linux
-
-#if defined(_AIX) && defined(__64BIT__)
-# define PLAT_ppc64_aix5 1
-#elif defined(_AIX) && !defined(__64BIT__)
-# define PLAT_ppc32_aix5 1
-#elif defined(__APPLE__) && defined(__i386__)
-# define PLAT_x86_darwin 1
-#elif defined(__APPLE__) && defined(__x86_64__)
-# define PLAT_amd64_darwin 1
-#elif defined(__linux__) && defined(__i386__)
-# define PLAT_x86_linux 1
-#elif defined(__linux__) && defined(__x86_64__)
-# define PLAT_amd64_linux 1
-#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__)
-# define PLAT_ppc32_linux 1
-#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__)
-# define PLAT_ppc64_linux 1
-#elif defined(__linux__) && defined(__arm__) && !defined(__ARM_ARCH_5__)
-# define PLAT_arm_linux 1
-#else
-/* If we're not compiling for our target platform, don't generate
- any inline asms. */
-# if !defined(NVALGRIND)
-# define NVALGRIND 1
-# endif
-#endif
-
-
-/* ------------------------------------------------------------------ */
-/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */
-/* in here of use to end-users -- skip to the next section. */
-/* ------------------------------------------------------------------ */
-
-#if defined(NVALGRIND)
-
-/* Define NVALGRIND to completely remove the Valgrind magic sequence
- from the compiled code (analogous to NDEBUG's effects on
- assert()) */
-#define VALGRIND_DO_CLIENT_REQUEST( \
- _zzq_rlval, _zzq_default, _zzq_request, \
- _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
- { \
- (_zzq_rlval) = (_zzq_default); \
- }
-
-#else /* ! NVALGRIND */
-
-/* The following defines the magic code sequences which the JITter
- spots and handles magically. Don't look too closely at them as
- they will rot your brain.
-
- The assembly code sequences for all architectures is in this one
- file. This is because this file must be stand-alone, and we don't
- want to have multiple files.
-
- For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default
- value gets put in the return slot, so that everything works when
- this is executed not under Valgrind. Args are passed in a memory
- block, and so there's no intrinsic limit to the number that could
- be passed, but it's currently five.
-
- The macro args are:
- _zzq_rlval result lvalue
- _zzq_default default value (result returned when running on real CPU)
- _zzq_request request code
- _zzq_arg1..5 request params
-
- The other two macros are used to support function wrapping, and are
- a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the
- guest's NRADDR pseudo-register and whatever other information is
- needed to safely run the call original from the wrapper: on
- ppc64-linux, the R2 value at the divert point is also needed. This
- information is abstracted into a user-visible type, OrigFn.
-
- VALGRIND_CALL_NOREDIR_* behaves the same as the following on the
- guest, but guarantees that the branch instruction will not be
- redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64:
- branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a
- complete inline asm, since it needs to be combined with more magic
- inline asm stuff to be useful.
-*/
-
-/* ------------------------- x86-{linux,darwin} ---------------- */
-
-#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin)
-
-typedef
- struct {
- unsigned int nraddr; /* where's the code? */
- }
- OrigFn;
-
-#define __SPECIAL_INSTRUCTION_PREAMBLE \
- "roll $3, %%edi ; roll $13, %%edi\n\t" \
- "roll $29, %%edi ; roll $19, %%edi\n\t"
-
-#define VALGRIND_DO_CLIENT_REQUEST( \
- _zzq_rlval, _zzq_default, _zzq_request, \
- _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
- { volatile unsigned int _zzq_args[6]; \
- volatile unsigned int _zzq_result; \
- _zzq_args[0] = (unsigned int)(_zzq_request); \
- _zzq_args[1] = (unsigned int)(_zzq_arg1); \
- _zzq_args[2] = (unsigned int)(_zzq_arg2); \
- _zzq_args[3] = (unsigned int)(_zzq_arg3); \
- _zzq_args[4] = (unsigned int)(_zzq_arg4); \
- _zzq_args[5] = (unsigned int)(_zzq_arg5); \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %EDX = client_request ( %EAX ) */ \
- "xchgl %%ebx,%%ebx" \
- : "=d" (_zzq_result) \
- : "a" (&_zzq_args[0]), "0" (_zzq_default) \
- : "cc", "memory" \
- ); \
- _zzq_rlval = _zzq_result; \
- }
-
-#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
- { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
- volatile unsigned int __addr; \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %EAX = guest_NRADDR */ \
- "xchgl %%ecx,%%ecx" \
- : "=a" (__addr) \
- : \
- : "cc", "memory" \
- ); \
- _zzq_orig->nraddr = __addr; \
- }
-
-#define VALGRIND_CALL_NOREDIR_EAX \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* call-noredir *%EAX */ \
- "xchgl %%edx,%%edx\n\t"
-#endif /* PLAT_x86_linux || PLAT_x86_darwin */
-
-/* ------------------------ amd64-{linux,darwin} --------------- */
-
-#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin)
-
-typedef
- struct {
- unsigned long long int nraddr; /* where's the code? */
- }
- OrigFn;
-
-#define __SPECIAL_INSTRUCTION_PREAMBLE \
- "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \
- "rolq $61, %%rdi ; rolq $51, %%rdi\n\t"
-
-#define VALGRIND_DO_CLIENT_REQUEST( \
- _zzq_rlval, _zzq_default, _zzq_request, \
- _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
- { volatile unsigned long long int _zzq_args[6]; \
- volatile unsigned long long int _zzq_result; \
- _zzq_args[0] = (unsigned long long int)(_zzq_request); \
- _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \
- _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \
- _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \
- _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \
- _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %RDX = client_request ( %RAX ) */ \
- "xchgq %%rbx,%%rbx" \
- : "=d" (_zzq_result) \
- : "a" (&_zzq_args[0]), "0" (_zzq_default) \
- : "cc", "memory" \
- ); \
- _zzq_rlval = _zzq_result; \
- }
-
-#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
- { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
- volatile unsigned long long int __addr; \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %RAX = guest_NRADDR */ \
- "xchgq %%rcx,%%rcx" \
- : "=a" (__addr) \
- : \
- : "cc", "memory" \
- ); \
- _zzq_orig->nraddr = __addr; \
- }
-
-#define VALGRIND_CALL_NOREDIR_RAX \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* call-noredir *%RAX */ \
- "xchgq %%rdx,%%rdx\n\t"
-#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */
-
-/* ------------------------ ppc32-linux ------------------------ */
-
-#if defined(PLAT_ppc32_linux)
-
-typedef
- struct {
- unsigned int nraddr; /* where's the code? */
- }
- OrigFn;
-
-#define __SPECIAL_INSTRUCTION_PREAMBLE \
- "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \
- "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t"
-
-#define VALGRIND_DO_CLIENT_REQUEST( \
- _zzq_rlval, _zzq_default, _zzq_request, \
- _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
- \
- { unsigned int _zzq_args[6]; \
- unsigned int _zzq_result; \
- unsigned int* _zzq_ptr; \
- _zzq_args[0] = (unsigned int)(_zzq_request); \
- _zzq_args[1] = (unsigned int)(_zzq_arg1); \
- _zzq_args[2] = (unsigned int)(_zzq_arg2); \
- _zzq_args[3] = (unsigned int)(_zzq_arg3); \
- _zzq_args[4] = (unsigned int)(_zzq_arg4); \
- _zzq_args[5] = (unsigned int)(_zzq_arg5); \
- _zzq_ptr = _zzq_args; \
- __asm__ volatile("mr 3,%1\n\t" /*default*/ \
- "mr 4,%2\n\t" /*ptr*/ \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = client_request ( %R4 ) */ \
- "or 1,1,1\n\t" \
- "mr %0,3" /*result*/ \
- : "=b" (_zzq_result) \
- : "b" (_zzq_default), "b" (_zzq_ptr) \
- : "cc", "memory", "r3", "r4"); \
- _zzq_rlval = _zzq_result; \
- }
-
-#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
- { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
- unsigned int __addr; \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = guest_NRADDR */ \
- "or 2,2,2\n\t" \
- "mr %0,3" \
- : "=b" (__addr) \
- : \
- : "cc", "memory", "r3" \
- ); \
- _zzq_orig->nraddr = __addr; \
- }
-
-#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* branch-and-link-to-noredir *%R11 */ \
- "or 3,3,3\n\t"
-#endif /* PLAT_ppc32_linux */
-
-/* ------------------------ ppc64-linux ------------------------ */
-
-#if defined(PLAT_ppc64_linux)
-
-typedef
- struct {
- unsigned long long int nraddr; /* where's the code? */
- unsigned long long int r2; /* what tocptr do we need? */
- }
- OrigFn;
-
-#define __SPECIAL_INSTRUCTION_PREAMBLE \
- "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \
- "rotldi 0,0,61 ; rotldi 0,0,51\n\t"
-
-#define VALGRIND_DO_CLIENT_REQUEST( \
- _zzq_rlval, _zzq_default, _zzq_request, \
- _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
- \
- { unsigned long long int _zzq_args[6]; \
- register unsigned long long int _zzq_result __asm__("r3"); \
- register unsigned long long int* _zzq_ptr __asm__("r4"); \
- _zzq_args[0] = (unsigned long long int)(_zzq_request); \
- _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \
- _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \
- _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \
- _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \
- _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \
- _zzq_ptr = _zzq_args; \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = client_request ( %R4 ) */ \
- "or 1,1,1" \
- : "=r" (_zzq_result) \
- : "0" (_zzq_default), "r" (_zzq_ptr) \
- : "cc", "memory"); \
- _zzq_rlval = _zzq_result; \
- }
-
-#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
- { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
- register unsigned long long int __addr __asm__("r3"); \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = guest_NRADDR */ \
- "or 2,2,2" \
- : "=r" (__addr) \
- : \
- : "cc", "memory" \
- ); \
- _zzq_orig->nraddr = __addr; \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = guest_NRADDR_GPR2 */ \
- "or 4,4,4" \
- : "=r" (__addr) \
- : \
- : "cc", "memory" \
- ); \
- _zzq_orig->r2 = __addr; \
- }
-
-#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* branch-and-link-to-noredir *%R11 */ \
- "or 3,3,3\n\t"
-
-#endif /* PLAT_ppc64_linux */
-
-/* ------------------------- arm-linux ------------------------- */
-
-#if defined(PLAT_arm_linux)
-
-typedef
- struct {
- unsigned int nraddr; /* where's the code? */
- }
- OrigFn;
-
-#define __SPECIAL_INSTRUCTION_PREAMBLE \
- "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \
- "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t"
-
-#define VALGRIND_DO_CLIENT_REQUEST( \
- _zzq_rlval, _zzq_default, _zzq_request, \
- _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
- \
- { volatile unsigned int _zzq_args[6]; \
- volatile unsigned int _zzq_result; \
- _zzq_args[0] = (unsigned int)(_zzq_request); \
- _zzq_args[1] = (unsigned int)(_zzq_arg1); \
- _zzq_args[2] = (unsigned int)(_zzq_arg2); \
- _zzq_args[3] = (unsigned int)(_zzq_arg3); \
- _zzq_args[4] = (unsigned int)(_zzq_arg4); \
- _zzq_args[5] = (unsigned int)(_zzq_arg5); \
- __asm__ volatile("mov r3, %1\n\t" /*default*/ \
- "mov r4, %2\n\t" /*ptr*/ \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* R3 = client_request ( R4 ) */ \
- "orr r10, r10, r10\n\t" \
- "mov %0, r3" /*result*/ \
- : "=r" (_zzq_result) \
- : "r" (_zzq_default), "r" (&_zzq_args[0]) \
- : "cc","memory", "r3", "r4"); \
- _zzq_rlval = _zzq_result; \
- }
-
-#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
- { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
- unsigned int __addr; \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* R3 = guest_NRADDR */ \
- "orr r11, r11, r11\n\t" \
- "mov %0, r3" \
- : "=r" (__addr) \
- : \
- : "cc", "memory", "r3" \
- ); \
- _zzq_orig->nraddr = __addr; \
- }
-
-#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* branch-and-link-to-noredir *%R4 */ \
- "orr r12, r12, r12\n\t"
-
-#endif /* PLAT_arm_linux */
-
-/* ------------------------ ppc32-aix5 ------------------------- */
-
-#if defined(PLAT_ppc32_aix5)
-
-typedef
- struct {
- unsigned int nraddr; /* where's the code? */
- unsigned int r2; /* what tocptr do we need? */
- }
- OrigFn;
-
-#define __SPECIAL_INSTRUCTION_PREAMBLE \
- "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \
- "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t"
-
-#define VALGRIND_DO_CLIENT_REQUEST( \
- _zzq_rlval, _zzq_default, _zzq_request, \
- _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
- \
- { unsigned int _zzq_args[7]; \
- register unsigned int _zzq_result; \
- register unsigned int* _zzq_ptr; \
- _zzq_args[0] = (unsigned int)(_zzq_request); \
- _zzq_args[1] = (unsigned int)(_zzq_arg1); \
- _zzq_args[2] = (unsigned int)(_zzq_arg2); \
- _zzq_args[3] = (unsigned int)(_zzq_arg3); \
- _zzq_args[4] = (unsigned int)(_zzq_arg4); \
- _zzq_args[5] = (unsigned int)(_zzq_arg5); \
- _zzq_args[6] = (unsigned int)(_zzq_default); \
- _zzq_ptr = _zzq_args; \
- __asm__ volatile("mr 4,%1\n\t" \
- "lwz 3, 24(4)\n\t" \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = client_request ( %R4 ) */ \
- "or 1,1,1\n\t" \
- "mr %0,3" \
- : "=b" (_zzq_result) \
- : "b" (_zzq_ptr) \
- : "r3", "r4", "cc", "memory"); \
- _zzq_rlval = _zzq_result; \
- }
-
-#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
- { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
- register unsigned int __addr; \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = guest_NRADDR */ \
- "or 2,2,2\n\t" \
- "mr %0,3" \
- : "=b" (__addr) \
- : \
- : "r3", "cc", "memory" \
- ); \
- _zzq_orig->nraddr = __addr; \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = guest_NRADDR_GPR2 */ \
- "or 4,4,4\n\t" \
- "mr %0,3" \
- : "=b" (__addr) \
- : \
- : "r3", "cc", "memory" \
- ); \
- _zzq_orig->r2 = __addr; \
- }
-
-#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* branch-and-link-to-noredir *%R11 */ \
- "or 3,3,3\n\t"
-
-#endif /* PLAT_ppc32_aix5 */
-
-/* ------------------------ ppc64-aix5 ------------------------- */
-
-#if defined(PLAT_ppc64_aix5)
-
-typedef
- struct {
- unsigned long long int nraddr; /* where's the code? */
- unsigned long long int r2; /* what tocptr do we need? */
- }
- OrigFn;
-
-#define __SPECIAL_INSTRUCTION_PREAMBLE \
- "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \
- "rotldi 0,0,61 ; rotldi 0,0,51\n\t"
-
-#define VALGRIND_DO_CLIENT_REQUEST( \
- _zzq_rlval, _zzq_default, _zzq_request, \
- _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
- \
- { unsigned long long int _zzq_args[7]; \
- register unsigned long long int _zzq_result; \
- register unsigned long long int* _zzq_ptr; \
- _zzq_args[0] = (unsigned int long long)(_zzq_request); \
- _zzq_args[1] = (unsigned int long long)(_zzq_arg1); \
- _zzq_args[2] = (unsigned int long long)(_zzq_arg2); \
- _zzq_args[3] = (unsigned int long long)(_zzq_arg3); \
- _zzq_args[4] = (unsigned int long long)(_zzq_arg4); \
- _zzq_args[5] = (unsigned int long long)(_zzq_arg5); \
- _zzq_args[6] = (unsigned int long long)(_zzq_default); \
- _zzq_ptr = _zzq_args; \
- __asm__ volatile("mr 4,%1\n\t" \
- "ld 3, 48(4)\n\t" \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = client_request ( %R4 ) */ \
- "or 1,1,1\n\t" \
- "mr %0,3" \
- : "=b" (_zzq_result) \
- : "b" (_zzq_ptr) \
- : "r3", "r4", "cc", "memory"); \
- _zzq_rlval = _zzq_result; \
- }
-
-#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
- { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
- register unsigned long long int __addr; \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = guest_NRADDR */ \
- "or 2,2,2\n\t" \
- "mr %0,3" \
- : "=b" (__addr) \
- : \
- : "r3", "cc", "memory" \
- ); \
- _zzq_orig->nraddr = __addr; \
- __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
- /* %R3 = guest_NRADDR_GPR2 */ \
- "or 4,4,4\n\t" \
- "mr %0,3" \
- : "=b" (__addr) \
- : \
- : "r3", "cc", "memory" \
- ); \
- _zzq_orig->r2 = __addr; \
- }
-
-#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- __SPECIAL_INSTRUCTION_PREAMBLE \
- /* branch-and-link-to-noredir *%R11 */ \
- "or 3,3,3\n\t"
-
-#endif /* PLAT_ppc64_aix5 */
-
-/* Insert assembly code for other platforms here... */
-
-#endif /* NVALGRIND */
-
-
-/* ------------------------------------------------------------------ */
-/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */
-/* ugly. It's the least-worst tradeoff I can think of. */
-/* ------------------------------------------------------------------ */
-
-/* This section defines magic (a.k.a appalling-hack) macros for doing
- guaranteed-no-redirection macros, so as to get from function
- wrappers to the functions they are wrapping. The whole point is to
- construct standard call sequences, but to do the call itself with a
- special no-redirect call pseudo-instruction that the JIT
- understands and handles specially. This section is long and
- repetitious, and I can't see a way to make it shorter.
-
- The naming scheme is as follows:
-
- CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc}
-
- 'W' stands for "word" and 'v' for "void". Hence there are
- different macros for calling arity 0, 1, 2, 3, 4, etc, functions,
- and for each, the possibility of returning a word-typed result, or
- no result.
-*/
-
-/* Use these to write the name of your wrapper. NOTE: duplicates
- VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. */
-
-/* Use an extra level of macroisation so as to ensure the soname/fnname
- args are fully macro-expanded before pasting them together. */
-#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd
-
-#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \
- VG_CONCAT4(_vgwZU_,soname,_,fnname)
-
-#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \
- VG_CONCAT4(_vgwZZ_,soname,_,fnname)
-
-/* Use this macro from within a wrapper function to collect the
- context (address and possibly other info) of the original function.
- Once you have that you can then use it in one of the CALL_FN_
- macros. The type of the argument _lval is OrigFn. */
-#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval)
-
-/* Derivatives of the main macros below, for calling functions
- returning void. */
-
-#define CALL_FN_v_v(fnptr) \
- do { volatile unsigned long _junk; \
- CALL_FN_W_v(_junk,fnptr); } while (0)
-
-#define CALL_FN_v_W(fnptr, arg1) \
- do { volatile unsigned long _junk; \
- CALL_FN_W_W(_junk,fnptr,arg1); } while (0)
-
-#define CALL_FN_v_WW(fnptr, arg1,arg2) \
- do { volatile unsigned long _junk; \
- CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0)
-
-#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \
- do { volatile unsigned long _junk; \
- CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0)
-
-#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \
- do { volatile unsigned long _junk; \
- CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0)
-
-#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \
- do { volatile unsigned long _junk; \
- CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0)
-
-#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \
- do { volatile unsigned long _junk; \
- CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0)
-
-#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \
- do { volatile unsigned long _junk; \
- CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0)
-
-/* ------------------------- x86-{linux,darwin} ---------------- */
-
-#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin)
-
-/* These regs are trashed by the hidden call. No need to mention eax
- as gcc can already see that, plus causes gcc to bomb. */
-#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx"
-
-/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned
- long) == 4. */
-
-#define CALL_FN_W_v(lval, orig) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[1]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- __asm__ volatile( \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_W(lval, orig, arg1) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[2]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- __asm__ volatile( \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $4, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- __asm__ volatile( \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $8, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[4]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- __asm__ volatile( \
- "pushl 12(%%eax)\n\t" \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $12, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[5]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- __asm__ volatile( \
- "pushl 16(%%eax)\n\t" \
- "pushl 12(%%eax)\n\t" \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $16, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[6]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- __asm__ volatile( \
- "pushl 20(%%eax)\n\t" \
- "pushl 16(%%eax)\n\t" \
- "pushl 12(%%eax)\n\t" \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $20, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[7]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- __asm__ volatile( \
- "pushl 24(%%eax)\n\t" \
- "pushl 20(%%eax)\n\t" \
- "pushl 16(%%eax)\n\t" \
- "pushl 12(%%eax)\n\t" \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $24, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[8]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- __asm__ volatile( \
- "pushl 28(%%eax)\n\t" \
- "pushl 24(%%eax)\n\t" \
- "pushl 20(%%eax)\n\t" \
- "pushl 16(%%eax)\n\t" \
- "pushl 12(%%eax)\n\t" \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $28, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[9]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- __asm__ volatile( \
- "pushl 32(%%eax)\n\t" \
- "pushl 28(%%eax)\n\t" \
- "pushl 24(%%eax)\n\t" \
- "pushl 20(%%eax)\n\t" \
- "pushl 16(%%eax)\n\t" \
- "pushl 12(%%eax)\n\t" \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $32, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[10]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- __asm__ volatile( \
- "pushl 36(%%eax)\n\t" \
- "pushl 32(%%eax)\n\t" \
- "pushl 28(%%eax)\n\t" \
- "pushl 24(%%eax)\n\t" \
- "pushl 20(%%eax)\n\t" \
- "pushl 16(%%eax)\n\t" \
- "pushl 12(%%eax)\n\t" \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $36, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[11]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- _argvec[10] = (unsigned long)(arg10); \
- __asm__ volatile( \
- "pushl 40(%%eax)\n\t" \
- "pushl 36(%%eax)\n\t" \
- "pushl 32(%%eax)\n\t" \
- "pushl 28(%%eax)\n\t" \
- "pushl 24(%%eax)\n\t" \
- "pushl 20(%%eax)\n\t" \
- "pushl 16(%%eax)\n\t" \
- "pushl 12(%%eax)\n\t" \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $40, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
- arg6,arg7,arg8,arg9,arg10, \
- arg11) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[12]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- _argvec[10] = (unsigned long)(arg10); \
- _argvec[11] = (unsigned long)(arg11); \
- __asm__ volatile( \
- "pushl 44(%%eax)\n\t" \
- "pushl 40(%%eax)\n\t" \
- "pushl 36(%%eax)\n\t" \
- "pushl 32(%%eax)\n\t" \
- "pushl 28(%%eax)\n\t" \
- "pushl 24(%%eax)\n\t" \
- "pushl 20(%%eax)\n\t" \
- "pushl 16(%%eax)\n\t" \
- "pushl 12(%%eax)\n\t" \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $44, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
- arg6,arg7,arg8,arg9,arg10, \
- arg11,arg12) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[13]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- _argvec[10] = (unsigned long)(arg10); \
- _argvec[11] = (unsigned long)(arg11); \
- _argvec[12] = (unsigned long)(arg12); \
- __asm__ volatile( \
- "pushl 48(%%eax)\n\t" \
- "pushl 44(%%eax)\n\t" \
- "pushl 40(%%eax)\n\t" \
- "pushl 36(%%eax)\n\t" \
- "pushl 32(%%eax)\n\t" \
- "pushl 28(%%eax)\n\t" \
- "pushl 24(%%eax)\n\t" \
- "pushl 20(%%eax)\n\t" \
- "pushl 16(%%eax)\n\t" \
- "pushl 12(%%eax)\n\t" \
- "pushl 8(%%eax)\n\t" \
- "pushl 4(%%eax)\n\t" \
- "movl (%%eax), %%eax\n\t" /* target->%eax */ \
- VALGRIND_CALL_NOREDIR_EAX \
- "addl $48, %%esp\n" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#endif /* PLAT_x86_linux || PLAT_x86_darwin */
-
-/* ------------------------ amd64-{linux,darwin} --------------- */
-
-#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin)
-
-/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */
-
-/* These regs are trashed by the hidden call. */
-#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \
- "rdi", "r8", "r9", "r10", "r11"
-
-/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned
- long) == 8. */
-
-/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_
- macros. In order not to trash the stack redzone, we need to drop
- %rsp by 128 before the hidden call, and restore afterwards. The
- nastyness is that it is only by luck that the stack still appears
- to be unwindable during the hidden call - since then the behaviour
- of any routine using this macro does not match what the CFI data
- says. Sigh.
-
- Why is this important? Imagine that a wrapper has a stack
- allocated local, and passes to the hidden call, a pointer to it.
- Because gcc does not know about the hidden call, it may allocate
- that local in the redzone. Unfortunately the hidden call may then
- trash it before it comes to use it. So we must step clear of the
- redzone, for the duration of the hidden call, to make it safe.
-
- Probably the same problem afflicts the other redzone-style ABIs too
- (ppc64-linux, ppc32-aix5, ppc64-aix5); but for those, the stack is
- self describing (none of this CFI nonsense) so at least messing
- with the stack pointer doesn't give a danger of non-unwindable
- stack. */
-
-#define CALL_FN_W_v(lval, orig) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[1]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_W(lval, orig, arg1) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[2]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[4]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "movq 24(%%rax), %%rdx\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[5]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "movq 32(%%rax), %%rcx\n\t" \
- "movq 24(%%rax), %%rdx\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[6]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "movq 40(%%rax), %%r8\n\t" \
- "movq 32(%%rax), %%rcx\n\t" \
- "movq 24(%%rax), %%rdx\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[7]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "movq 48(%%rax), %%r9\n\t" \
- "movq 40(%%rax), %%r8\n\t" \
- "movq 32(%%rax), %%rcx\n\t" \
- "movq 24(%%rax), %%rdx\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- "addq $128,%%rsp\n\t" \
- VALGRIND_CALL_NOREDIR_RAX \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[8]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "pushq 56(%%rax)\n\t" \
- "movq 48(%%rax), %%r9\n\t" \
- "movq 40(%%rax), %%r8\n\t" \
- "movq 32(%%rax), %%rcx\n\t" \
- "movq 24(%%rax), %%rdx\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $8, %%rsp\n" \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[9]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "pushq 64(%%rax)\n\t" \
- "pushq 56(%%rax)\n\t" \
- "movq 48(%%rax), %%r9\n\t" \
- "movq 40(%%rax), %%r8\n\t" \
- "movq 32(%%rax), %%rcx\n\t" \
- "movq 24(%%rax), %%rdx\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $16, %%rsp\n" \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[10]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "pushq 72(%%rax)\n\t" \
- "pushq 64(%%rax)\n\t" \
- "pushq 56(%%rax)\n\t" \
- "movq 48(%%rax), %%r9\n\t" \
- "movq 40(%%rax), %%r8\n\t" \
- "movq 32(%%rax), %%rcx\n\t" \
- "movq 24(%%rax), %%rdx\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $24, %%rsp\n" \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[11]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- _argvec[10] = (unsigned long)(arg10); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "pushq 80(%%rax)\n\t" \
- "pushq 72(%%rax)\n\t" \
- "pushq 64(%%rax)\n\t" \
- "pushq 56(%%rax)\n\t" \
- "movq 48(%%rax), %%r9\n\t" \
- "movq 40(%%rax), %%r8\n\t" \
- "movq 32(%%rax), %%rcx\n\t" \
- "movq 24(%%rax), %%rdx\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $32, %%rsp\n" \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10,arg11) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[12]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- _argvec[10] = (unsigned long)(arg10); \
- _argvec[11] = (unsigned long)(arg11); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "pushq 88(%%rax)\n\t" \
- "pushq 80(%%rax)\n\t" \
- "pushq 72(%%rax)\n\t" \
- "pushq 64(%%rax)\n\t" \
- "pushq 56(%%rax)\n\t" \
- "movq 48(%%rax), %%r9\n\t" \
- "movq 40(%%rax), %%r8\n\t" \
- "movq 32(%%rax), %%rcx\n\t" \
- "movq 24(%%rax), %%rdx\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $40, %%rsp\n" \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10,arg11,arg12) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[13]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- _argvec[10] = (unsigned long)(arg10); \
- _argvec[11] = (unsigned long)(arg11); \
- _argvec[12] = (unsigned long)(arg12); \
- __asm__ volatile( \
- "subq $128,%%rsp\n\t" \
- "pushq 96(%%rax)\n\t" \
- "pushq 88(%%rax)\n\t" \
- "pushq 80(%%rax)\n\t" \
- "pushq 72(%%rax)\n\t" \
- "pushq 64(%%rax)\n\t" \
- "pushq 56(%%rax)\n\t" \
- "movq 48(%%rax), %%r9\n\t" \
- "movq 40(%%rax), %%r8\n\t" \
- "movq 32(%%rax), %%rcx\n\t" \
- "movq 24(%%rax), %%rdx\n\t" \
- "movq 16(%%rax), %%rsi\n\t" \
- "movq 8(%%rax), %%rdi\n\t" \
- "movq (%%rax), %%rax\n\t" /* target->%rax */ \
- VALGRIND_CALL_NOREDIR_RAX \
- "addq $48, %%rsp\n" \
- "addq $128,%%rsp\n\t" \
- : /*out*/ "=a" (_res) \
- : /*in*/ "a" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */
-
-/* ------------------------ ppc32-linux ------------------------ */
-
-#if defined(PLAT_ppc32_linux)
-
-/* This is useful for finding out about the on-stack stuff:
-
- extern int f9 ( int,int,int,int,int,int,int,int,int );
- extern int f10 ( int,int,int,int,int,int,int,int,int,int );
- extern int f11 ( int,int,int,int,int,int,int,int,int,int,int );
- extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int );
-
- int g9 ( void ) {
- return f9(11,22,33,44,55,66,77,88,99);
- }
- int g10 ( void ) {
- return f10(11,22,33,44,55,66,77,88,99,110);
- }
- int g11 ( void ) {
- return f11(11,22,33,44,55,66,77,88,99,110,121);
- }
- int g12 ( void ) {
- return f12(11,22,33,44,55,66,77,88,99,110,121,132);
- }
-*/
-
-/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
-
-/* These regs are trashed by the hidden call. */
-#define __CALLER_SAVED_REGS \
- "lr", "ctr", "xer", \
- "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
- "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
- "r11", "r12", "r13"
-
-/* These CALL_FN_ macros assume that on ppc32-linux,
- sizeof(unsigned long) == 4. */
-
-#define CALL_FN_W_v(lval, orig) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[1]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_W(lval, orig, arg1) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[2]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[4]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- _argvec[3] = (unsigned long)arg3; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 5,12(11)\n\t" \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[5]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- _argvec[3] = (unsigned long)arg3; \
- _argvec[4] = (unsigned long)arg4; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 5,12(11)\n\t" \
- "lwz 6,16(11)\n\t" /* arg4->r6 */ \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[6]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- _argvec[3] = (unsigned long)arg3; \
- _argvec[4] = (unsigned long)arg4; \
- _argvec[5] = (unsigned long)arg5; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 5,12(11)\n\t" \
- "lwz 6,16(11)\n\t" /* arg4->r6 */ \
- "lwz 7,20(11)\n\t" \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[7]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- _argvec[3] = (unsigned long)arg3; \
- _argvec[4] = (unsigned long)arg4; \
- _argvec[5] = (unsigned long)arg5; \
- _argvec[6] = (unsigned long)arg6; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 5,12(11)\n\t" \
- "lwz 6,16(11)\n\t" /* arg4->r6 */ \
- "lwz 7,20(11)\n\t" \
- "lwz 8,24(11)\n\t" \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[8]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- _argvec[3] = (unsigned long)arg3; \
- _argvec[4] = (unsigned long)arg4; \
- _argvec[5] = (unsigned long)arg5; \
- _argvec[6] = (unsigned long)arg6; \
- _argvec[7] = (unsigned long)arg7; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 5,12(11)\n\t" \
- "lwz 6,16(11)\n\t" /* arg4->r6 */ \
- "lwz 7,20(11)\n\t" \
- "lwz 8,24(11)\n\t" \
- "lwz 9,28(11)\n\t" \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[9]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- _argvec[3] = (unsigned long)arg3; \
- _argvec[4] = (unsigned long)arg4; \
- _argvec[5] = (unsigned long)arg5; \
- _argvec[6] = (unsigned long)arg6; \
- _argvec[7] = (unsigned long)arg7; \
- _argvec[8] = (unsigned long)arg8; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 5,12(11)\n\t" \
- "lwz 6,16(11)\n\t" /* arg4->r6 */ \
- "lwz 7,20(11)\n\t" \
- "lwz 8,24(11)\n\t" \
- "lwz 9,28(11)\n\t" \
- "lwz 10,32(11)\n\t" /* arg8->r10 */ \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[10]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- _argvec[3] = (unsigned long)arg3; \
- _argvec[4] = (unsigned long)arg4; \
- _argvec[5] = (unsigned long)arg5; \
- _argvec[6] = (unsigned long)arg6; \
- _argvec[7] = (unsigned long)arg7; \
- _argvec[8] = (unsigned long)arg8; \
- _argvec[9] = (unsigned long)arg9; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "addi 1,1,-16\n\t" \
- /* arg9 */ \
- "lwz 3,36(11)\n\t" \
- "stw 3,8(1)\n\t" \
- /* args1-8 */ \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 5,12(11)\n\t" \
- "lwz 6,16(11)\n\t" /* arg4->r6 */ \
- "lwz 7,20(11)\n\t" \
- "lwz 8,24(11)\n\t" \
- "lwz 9,28(11)\n\t" \
- "lwz 10,32(11)\n\t" /* arg8->r10 */ \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "addi 1,1,16\n\t" \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[11]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- _argvec[3] = (unsigned long)arg3; \
- _argvec[4] = (unsigned long)arg4; \
- _argvec[5] = (unsigned long)arg5; \
- _argvec[6] = (unsigned long)arg6; \
- _argvec[7] = (unsigned long)arg7; \
- _argvec[8] = (unsigned long)arg8; \
- _argvec[9] = (unsigned long)arg9; \
- _argvec[10] = (unsigned long)arg10; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "addi 1,1,-16\n\t" \
- /* arg10 */ \
- "lwz 3,40(11)\n\t" \
- "stw 3,12(1)\n\t" \
- /* arg9 */ \
- "lwz 3,36(11)\n\t" \
- "stw 3,8(1)\n\t" \
- /* args1-8 */ \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 5,12(11)\n\t" \
- "lwz 6,16(11)\n\t" /* arg4->r6 */ \
- "lwz 7,20(11)\n\t" \
- "lwz 8,24(11)\n\t" \
- "lwz 9,28(11)\n\t" \
- "lwz 10,32(11)\n\t" /* arg8->r10 */ \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "addi 1,1,16\n\t" \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10,arg11) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[12]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- _argvec[3] = (unsigned long)arg3; \
- _argvec[4] = (unsigned long)arg4; \
- _argvec[5] = (unsigned long)arg5; \
- _argvec[6] = (unsigned long)arg6; \
- _argvec[7] = (unsigned long)arg7; \
- _argvec[8] = (unsigned long)arg8; \
- _argvec[9] = (unsigned long)arg9; \
- _argvec[10] = (unsigned long)arg10; \
- _argvec[11] = (unsigned long)arg11; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "addi 1,1,-32\n\t" \
- /* arg11 */ \
- "lwz 3,44(11)\n\t" \
- "stw 3,16(1)\n\t" \
- /* arg10 */ \
- "lwz 3,40(11)\n\t" \
- "stw 3,12(1)\n\t" \
- /* arg9 */ \
- "lwz 3,36(11)\n\t" \
- "stw 3,8(1)\n\t" \
- /* args1-8 */ \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 5,12(11)\n\t" \
- "lwz 6,16(11)\n\t" /* arg4->r6 */ \
- "lwz 7,20(11)\n\t" \
- "lwz 8,24(11)\n\t" \
- "lwz 9,28(11)\n\t" \
- "lwz 10,32(11)\n\t" /* arg8->r10 */ \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "addi 1,1,32\n\t" \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10,arg11,arg12) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[13]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)arg1; \
- _argvec[2] = (unsigned long)arg2; \
- _argvec[3] = (unsigned long)arg3; \
- _argvec[4] = (unsigned long)arg4; \
- _argvec[5] = (unsigned long)arg5; \
- _argvec[6] = (unsigned long)arg6; \
- _argvec[7] = (unsigned long)arg7; \
- _argvec[8] = (unsigned long)arg8; \
- _argvec[9] = (unsigned long)arg9; \
- _argvec[10] = (unsigned long)arg10; \
- _argvec[11] = (unsigned long)arg11; \
- _argvec[12] = (unsigned long)arg12; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "addi 1,1,-32\n\t" \
- /* arg12 */ \
- "lwz 3,48(11)\n\t" \
- "stw 3,20(1)\n\t" \
- /* arg11 */ \
- "lwz 3,44(11)\n\t" \
- "stw 3,16(1)\n\t" \
- /* arg10 */ \
- "lwz 3,40(11)\n\t" \
- "stw 3,12(1)\n\t" \
- /* arg9 */ \
- "lwz 3,36(11)\n\t" \
- "stw 3,8(1)\n\t" \
- /* args1-8 */ \
- "lwz 3,4(11)\n\t" /* arg1->r3 */ \
- "lwz 4,8(11)\n\t" \
- "lwz 5,12(11)\n\t" \
- "lwz 6,16(11)\n\t" /* arg4->r6 */ \
- "lwz 7,20(11)\n\t" \
- "lwz 8,24(11)\n\t" \
- "lwz 9,28(11)\n\t" \
- "lwz 10,32(11)\n\t" /* arg8->r10 */ \
- "lwz 11,0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "addi 1,1,32\n\t" \
- "mr %0,3" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#endif /* PLAT_ppc32_linux */
-
-/* ------------------------ ppc64-linux ------------------------ */
-
-#if defined(PLAT_ppc64_linux)
-
-/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
-
-/* These regs are trashed by the hidden call. */
-#define __CALLER_SAVED_REGS \
- "lr", "ctr", "xer", \
- "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
- "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
- "r11", "r12", "r13"
-
-/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned
- long) == 8. */
-
-#define CALL_FN_W_v(lval, orig) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+0]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)" /* restore tocptr */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_W(lval, orig, arg1) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+1]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)" /* restore tocptr */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+2]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)" /* restore tocptr */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+3]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)" /* restore tocptr */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+4]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)" /* restore tocptr */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+5]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)" /* restore tocptr */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+6]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)" /* restore tocptr */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+7]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)" /* restore tocptr */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+8]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 10, 64(11)\n\t" /* arg8->r10 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)" /* restore tocptr */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+9]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "addi 1,1,-128\n\t" /* expand stack frame */ \
- /* arg9 */ \
- "ld 3,72(11)\n\t" \
- "std 3,112(1)\n\t" \
- /* args1-8 */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 10, 64(11)\n\t" /* arg8->r10 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- "addi 1,1,128" /* restore frame */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+10]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- _argvec[2+10] = (unsigned long)arg10; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "addi 1,1,-128\n\t" /* expand stack frame */ \
- /* arg10 */ \
- "ld 3,80(11)\n\t" \
- "std 3,120(1)\n\t" \
- /* arg9 */ \
- "ld 3,72(11)\n\t" \
- "std 3,112(1)\n\t" \
- /* args1-8 */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 10, 64(11)\n\t" /* arg8->r10 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- "addi 1,1,128" /* restore frame */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10,arg11) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+11]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- _argvec[2+10] = (unsigned long)arg10; \
- _argvec[2+11] = (unsigned long)arg11; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "addi 1,1,-144\n\t" /* expand stack frame */ \
- /* arg11 */ \
- "ld 3,88(11)\n\t" \
- "std 3,128(1)\n\t" \
- /* arg10 */ \
- "ld 3,80(11)\n\t" \
- "std 3,120(1)\n\t" \
- /* arg9 */ \
- "ld 3,72(11)\n\t" \
- "std 3,112(1)\n\t" \
- /* args1-8 */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 10, 64(11)\n\t" /* arg8->r10 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- "addi 1,1,144" /* restore frame */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10,arg11,arg12) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+12]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- _argvec[2+10] = (unsigned long)arg10; \
- _argvec[2+11] = (unsigned long)arg11; \
- _argvec[2+12] = (unsigned long)arg12; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "addi 1,1,-144\n\t" /* expand stack frame */ \
- /* arg12 */ \
- "ld 3,96(11)\n\t" \
- "std 3,136(1)\n\t" \
- /* arg11 */ \
- "ld 3,88(11)\n\t" \
- "std 3,128(1)\n\t" \
- /* arg10 */ \
- "ld 3,80(11)\n\t" \
- "std 3,120(1)\n\t" \
- /* arg9 */ \
- "ld 3,72(11)\n\t" \
- "std 3,112(1)\n\t" \
- /* args1-8 */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 10, 64(11)\n\t" /* arg8->r10 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- "addi 1,1,144" /* restore frame */ \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#endif /* PLAT_ppc64_linux */
-
-/* ------------------------- arm-linux ------------------------- */
-
-#if defined(PLAT_arm_linux)
-
-/* These regs are trashed by the hidden call. */
-#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14"
-
-/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned
- long) == 4. */
-
-#define CALL_FN_W_v(lval, orig) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[1]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- __asm__ volatile( \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "mov %0, r0\n" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_W(lval, orig, arg1) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[2]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- __asm__ volatile( \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "mov %0, r0\n" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- __asm__ volatile( \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "mov %0, r0\n" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[4]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- __asm__ volatile( \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r2, [%1, #12] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "mov %0, r0\n" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[5]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- __asm__ volatile( \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r2, [%1, #12] \n\t" \
- "ldr r3, [%1, #16] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "mov %0, r0" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[6]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- __asm__ volatile( \
- "ldr r0, [%1, #20] \n\t" \
- "push {r0} \n\t" \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r2, [%1, #12] \n\t" \
- "ldr r3, [%1, #16] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "add sp, sp, #4 \n\t" \
- "mov %0, r0" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[7]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- __asm__ volatile( \
- "ldr r0, [%1, #20] \n\t" \
- "ldr r1, [%1, #24] \n\t" \
- "push {r0, r1} \n\t" \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r2, [%1, #12] \n\t" \
- "ldr r3, [%1, #16] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "add sp, sp, #8 \n\t" \
- "mov %0, r0" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[8]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- __asm__ volatile( \
- "ldr r0, [%1, #20] \n\t" \
- "ldr r1, [%1, #24] \n\t" \
- "ldr r2, [%1, #28] \n\t" \
- "push {r0, r1, r2} \n\t" \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r2, [%1, #12] \n\t" \
- "ldr r3, [%1, #16] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "add sp, sp, #12 \n\t" \
- "mov %0, r0" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[9]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- __asm__ volatile( \
- "ldr r0, [%1, #20] \n\t" \
- "ldr r1, [%1, #24] \n\t" \
- "ldr r2, [%1, #28] \n\t" \
- "ldr r3, [%1, #32] \n\t" \
- "push {r0, r1, r2, r3} \n\t" \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r2, [%1, #12] \n\t" \
- "ldr r3, [%1, #16] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "add sp, sp, #16 \n\t" \
- "mov %0, r0" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[10]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- __asm__ volatile( \
- "ldr r0, [%1, #20] \n\t" \
- "ldr r1, [%1, #24] \n\t" \
- "ldr r2, [%1, #28] \n\t" \
- "ldr r3, [%1, #32] \n\t" \
- "ldr r4, [%1, #36] \n\t" \
- "push {r0, r1, r2, r3, r4} \n\t" \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r2, [%1, #12] \n\t" \
- "ldr r3, [%1, #16] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "add sp, sp, #20 \n\t" \
- "mov %0, r0" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[11]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- _argvec[10] = (unsigned long)(arg10); \
- __asm__ volatile( \
- "ldr r0, [%1, #40] \n\t" \
- "push {r0} \n\t" \
- "ldr r0, [%1, #20] \n\t" \
- "ldr r1, [%1, #24] \n\t" \
- "ldr r2, [%1, #28] \n\t" \
- "ldr r3, [%1, #32] \n\t" \
- "ldr r4, [%1, #36] \n\t" \
- "push {r0, r1, r2, r3, r4} \n\t" \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r2, [%1, #12] \n\t" \
- "ldr r3, [%1, #16] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "add sp, sp, #24 \n\t" \
- "mov %0, r0" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
- arg6,arg7,arg8,arg9,arg10, \
- arg11) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[12]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- _argvec[10] = (unsigned long)(arg10); \
- _argvec[11] = (unsigned long)(arg11); \
- __asm__ volatile( \
- "ldr r0, [%1, #40] \n\t" \
- "ldr r1, [%1, #44] \n\t" \
- "push {r0, r1} \n\t" \
- "ldr r0, [%1, #20] \n\t" \
- "ldr r1, [%1, #24] \n\t" \
- "ldr r2, [%1, #28] \n\t" \
- "ldr r3, [%1, #32] \n\t" \
- "ldr r4, [%1, #36] \n\t" \
- "push {r0, r1, r2, r3, r4} \n\t" \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r2, [%1, #12] \n\t" \
- "ldr r3, [%1, #16] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "add sp, sp, #28 \n\t" \
- "mov %0, r0" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory",__CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
- arg6,arg7,arg8,arg9,arg10, \
- arg11,arg12) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[13]; \
- volatile unsigned long _res; \
- _argvec[0] = (unsigned long)_orig.nraddr; \
- _argvec[1] = (unsigned long)(arg1); \
- _argvec[2] = (unsigned long)(arg2); \
- _argvec[3] = (unsigned long)(arg3); \
- _argvec[4] = (unsigned long)(arg4); \
- _argvec[5] = (unsigned long)(arg5); \
- _argvec[6] = (unsigned long)(arg6); \
- _argvec[7] = (unsigned long)(arg7); \
- _argvec[8] = (unsigned long)(arg8); \
- _argvec[9] = (unsigned long)(arg9); \
- _argvec[10] = (unsigned long)(arg10); \
- _argvec[11] = (unsigned long)(arg11); \
- _argvec[12] = (unsigned long)(arg12); \
- __asm__ volatile( \
- "ldr r0, [%1, #40] \n\t" \
- "ldr r1, [%1, #44] \n\t" \
- "ldr r2, [%1, #48] \n\t" \
- "push {r0, r1, r2} \n\t" \
- "ldr r0, [%1, #20] \n\t" \
- "ldr r1, [%1, #24] \n\t" \
- "ldr r2, [%1, #28] \n\t" \
- "ldr r3, [%1, #32] \n\t" \
- "ldr r4, [%1, #36] \n\t" \
- "push {r0, r1, r2, r3, r4} \n\t" \
- "ldr r0, [%1, #4] \n\t" \
- "ldr r1, [%1, #8] \n\t" \
- "ldr r2, [%1, #12] \n\t" \
- "ldr r3, [%1, #16] \n\t" \
- "ldr r4, [%1] \n\t" /* target->r4 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
- "add sp, sp, #32 \n\t" \
- "mov %0, r0" \
- : /*out*/ "=r" (_res) \
- : /*in*/ "0" (&_argvec[0]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#endif /* PLAT_arm_linux */
-
-/* ------------------------ ppc32-aix5 ------------------------- */
-
-#if defined(PLAT_ppc32_aix5)
-
-/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
-
-/* These regs are trashed by the hidden call. */
-#define __CALLER_SAVED_REGS \
- "lr", "ctr", "xer", \
- "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
- "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
- "r11", "r12", "r13"
-
-/* Expand the stack frame, copying enough info that unwinding
- still works. Trashes r3. */
-
-#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \
- "addi 1,1,-" #_n_fr "\n\t" \
- "lwz 3," #_n_fr "(1)\n\t" \
- "stw 3,0(1)\n\t"
-
-#define VG_CONTRACT_FRAME_BY(_n_fr) \
- "addi 1,1," #_n_fr "\n\t"
-
-/* These CALL_FN_ macros assume that on ppc32-aix5, sizeof(unsigned
- long) == 4. */
-
-#define CALL_FN_W_v(lval, orig) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+0]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_W(lval, orig, arg1) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+1]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+2]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+3]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+4]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
- "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+5]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
- "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
- "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+6]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
- "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
- "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
- "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+7]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
- "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
- "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
- "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
- "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+8]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
- "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
- "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
- "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
- "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
- "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+9]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- VG_EXPAND_FRAME_BY_trashes_r3(64) \
- /* arg9 */ \
- "lwz 3,36(11)\n\t" \
- "stw 3,56(1)\n\t" \
- /* args1-8 */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
- "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
- "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
- "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
- "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
- "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(64) \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+10]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- _argvec[2+10] = (unsigned long)arg10; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- VG_EXPAND_FRAME_BY_trashes_r3(64) \
- /* arg10 */ \
- "lwz 3,40(11)\n\t" \
- "stw 3,60(1)\n\t" \
- /* arg9 */ \
- "lwz 3,36(11)\n\t" \
- "stw 3,56(1)\n\t" \
- /* args1-8 */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
- "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
- "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
- "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
- "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
- "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(64) \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10,arg11) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+11]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- _argvec[2+10] = (unsigned long)arg10; \
- _argvec[2+11] = (unsigned long)arg11; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- VG_EXPAND_FRAME_BY_trashes_r3(72) \
- /* arg11 */ \
- "lwz 3,44(11)\n\t" \
- "stw 3,64(1)\n\t" \
- /* arg10 */ \
- "lwz 3,40(11)\n\t" \
- "stw 3,60(1)\n\t" \
- /* arg9 */ \
- "lwz 3,36(11)\n\t" \
- "stw 3,56(1)\n\t" \
- /* args1-8 */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
- "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
- "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
- "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
- "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
- "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(72) \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10,arg11,arg12) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+12]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- _argvec[2+10] = (unsigned long)arg10; \
- _argvec[2+11] = (unsigned long)arg11; \
- _argvec[2+12] = (unsigned long)arg12; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "stw 2,-8(11)\n\t" /* save tocptr */ \
- "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
- VG_EXPAND_FRAME_BY_trashes_r3(72) \
- /* arg12 */ \
- "lwz 3,48(11)\n\t" \
- "stw 3,68(1)\n\t" \
- /* arg11 */ \
- "lwz 3,44(11)\n\t" \
- "stw 3,64(1)\n\t" \
- /* arg10 */ \
- "lwz 3,40(11)\n\t" \
- "stw 3,60(1)\n\t" \
- /* arg9 */ \
- "lwz 3,36(11)\n\t" \
- "stw 3,56(1)\n\t" \
- /* args1-8 */ \
- "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
- "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
- "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
- "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
- "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
- "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
- "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
- "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
- "lwz 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "lwz 2,-8(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(72) \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#endif /* PLAT_ppc32_aix5 */
-
-/* ------------------------ ppc64-aix5 ------------------------- */
-
-#if defined(PLAT_ppc64_aix5)
-
-/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
-
-/* These regs are trashed by the hidden call. */
-#define __CALLER_SAVED_REGS \
- "lr", "ctr", "xer", \
- "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
- "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
- "r11", "r12", "r13"
-
-/* Expand the stack frame, copying enough info that unwinding
- still works. Trashes r3. */
-
-#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \
- "addi 1,1,-" #_n_fr "\n\t" \
- "ld 3," #_n_fr "(1)\n\t" \
- "std 3,0(1)\n\t"
-
-#define VG_CONTRACT_FRAME_BY(_n_fr) \
- "addi 1,1," #_n_fr "\n\t"
-
-/* These CALL_FN_ macros assume that on ppc64-aix5, sizeof(unsigned
- long) == 8. */
-
-#define CALL_FN_W_v(lval, orig) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+0]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_W(lval, orig, arg1) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+1]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+2]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+3]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+4]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+5]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+6]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+7]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+8]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 10, 64(11)\n\t" /* arg8->r10 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+9]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- VG_EXPAND_FRAME_BY_trashes_r3(128) \
- /* arg9 */ \
- "ld 3,72(11)\n\t" \
- "std 3,112(1)\n\t" \
- /* args1-8 */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 10, 64(11)\n\t" /* arg8->r10 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(128) \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+10]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- _argvec[2+10] = (unsigned long)arg10; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- VG_EXPAND_FRAME_BY_trashes_r3(128) \
- /* arg10 */ \
- "ld 3,80(11)\n\t" \
- "std 3,120(1)\n\t" \
- /* arg9 */ \
- "ld 3,72(11)\n\t" \
- "std 3,112(1)\n\t" \
- /* args1-8 */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 10, 64(11)\n\t" /* arg8->r10 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(128) \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10,arg11) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+11]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- _argvec[2+10] = (unsigned long)arg10; \
- _argvec[2+11] = (unsigned long)arg11; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- VG_EXPAND_FRAME_BY_trashes_r3(144) \
- /* arg11 */ \
- "ld 3,88(11)\n\t" \
- "std 3,128(1)\n\t" \
- /* arg10 */ \
- "ld 3,80(11)\n\t" \
- "std 3,120(1)\n\t" \
- /* arg9 */ \
- "ld 3,72(11)\n\t" \
- "std 3,112(1)\n\t" \
- /* args1-8 */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 10, 64(11)\n\t" /* arg8->r10 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(144) \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
- arg7,arg8,arg9,arg10,arg11,arg12) \
- do { \
- volatile OrigFn _orig = (orig); \
- volatile unsigned long _argvec[3+12]; \
- volatile unsigned long _res; \
- /* _argvec[0] holds current r2 across the call */ \
- _argvec[1] = (unsigned long)_orig.r2; \
- _argvec[2] = (unsigned long)_orig.nraddr; \
- _argvec[2+1] = (unsigned long)arg1; \
- _argvec[2+2] = (unsigned long)arg2; \
- _argvec[2+3] = (unsigned long)arg3; \
- _argvec[2+4] = (unsigned long)arg4; \
- _argvec[2+5] = (unsigned long)arg5; \
- _argvec[2+6] = (unsigned long)arg6; \
- _argvec[2+7] = (unsigned long)arg7; \
- _argvec[2+8] = (unsigned long)arg8; \
- _argvec[2+9] = (unsigned long)arg9; \
- _argvec[2+10] = (unsigned long)arg10; \
- _argvec[2+11] = (unsigned long)arg11; \
- _argvec[2+12] = (unsigned long)arg12; \
- __asm__ volatile( \
- "mr 11,%1\n\t" \
- VG_EXPAND_FRAME_BY_trashes_r3(512) \
- "std 2,-16(11)\n\t" /* save tocptr */ \
- "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
- VG_EXPAND_FRAME_BY_trashes_r3(144) \
- /* arg12 */ \
- "ld 3,96(11)\n\t" \
- "std 3,136(1)\n\t" \
- /* arg11 */ \
- "ld 3,88(11)\n\t" \
- "std 3,128(1)\n\t" \
- /* arg10 */ \
- "ld 3,80(11)\n\t" \
- "std 3,120(1)\n\t" \
- /* arg9 */ \
- "ld 3,72(11)\n\t" \
- "std 3,112(1)\n\t" \
- /* args1-8 */ \
- "ld 3, 8(11)\n\t" /* arg1->r3 */ \
- "ld 4, 16(11)\n\t" /* arg2->r4 */ \
- "ld 5, 24(11)\n\t" /* arg3->r5 */ \
- "ld 6, 32(11)\n\t" /* arg4->r6 */ \
- "ld 7, 40(11)\n\t" /* arg5->r7 */ \
- "ld 8, 48(11)\n\t" /* arg6->r8 */ \
- "ld 9, 56(11)\n\t" /* arg7->r9 */ \
- "ld 10, 64(11)\n\t" /* arg8->r10 */ \
- "ld 11, 0(11)\n\t" /* target->r11 */ \
- VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
- "mr 11,%1\n\t" \
- "mr %0,3\n\t" \
- "ld 2,-16(11)\n\t" /* restore tocptr */ \
- VG_CONTRACT_FRAME_BY(144) \
- VG_CONTRACT_FRAME_BY(512) \
- : /*out*/ "=r" (_res) \
- : /*in*/ "r" (&_argvec[2]) \
- : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
- ); \
- lval = (__typeof__(lval)) _res; \
- } while (0)
-
-#endif /* PLAT_ppc64_aix5 */
-
-
-/* ------------------------------------------------------------------ */
-/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */
-/* */
-/* ------------------------------------------------------------------ */
-
-/* Some request codes. There are many more of these, but most are not
- exposed to end-user view. These are the public ones, all of the
- form 0x1000 + small_number.
-
- Core ones are in the range 0x00000000--0x0000ffff. The non-public
- ones start at 0x2000.
-*/
-
-/* These macros are used by tools -- they must be public, but don't
- embed them into other programs. */
-#define VG_USERREQ_TOOL_BASE(a,b) \
- ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16))
-#define VG_IS_TOOL_USERREQ(a, b, v) \
- (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000))
-
-/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
- This enum comprises an ABI exported by Valgrind to programs
- which use client requests. DO NOT CHANGE THE ORDER OF THESE
- ENTRIES, NOR DELETE ANY -- add new ones at the end. */
-typedef
- enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001,
- VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002,
-
- /* These allow any function to be called from the simulated
- CPU but run on the real CPU. Nb: the first arg passed to
- the function is always the ThreadId of the running
- thread! So CLIENT_CALL0 actually requires a 1 arg
- function, etc. */
- VG_USERREQ__CLIENT_CALL0 = 0x1101,
- VG_USERREQ__CLIENT_CALL1 = 0x1102,
- VG_USERREQ__CLIENT_CALL2 = 0x1103,
- VG_USERREQ__CLIENT_CALL3 = 0x1104,
-
- /* Can be useful in regression testing suites -- eg. can
- send Valgrind's output to /dev/null and still count
- errors. */
- VG_USERREQ__COUNT_ERRORS = 0x1201,
-
- /* These are useful and can be interpreted by any tool that
- tracks malloc() et al, by using vg_replace_malloc.c. */
- VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301,
- VG_USERREQ__FREELIKE_BLOCK = 0x1302,
- /* Memory pool support. */
- VG_USERREQ__CREATE_MEMPOOL = 0x1303,
- VG_USERREQ__DESTROY_MEMPOOL = 0x1304,
- VG_USERREQ__MEMPOOL_ALLOC = 0x1305,
- VG_USERREQ__MEMPOOL_FREE = 0x1306,
- VG_USERREQ__MEMPOOL_TRIM = 0x1307,
- VG_USERREQ__MOVE_MEMPOOL = 0x1308,
- VG_USERREQ__MEMPOOL_CHANGE = 0x1309,
- VG_USERREQ__MEMPOOL_EXISTS = 0x130a,
-
- /* Allow printfs to valgrind log. */
- /* The first two pass the va_list argument by value, which
- assumes it is the same size as or smaller than a UWord,
- which generally isn't the case. Hence are deprecated.
- The second two pass the vargs by reference and so are
- immune to this problem. */
- /* both :: char* fmt, va_list vargs (DEPRECATED) */
- VG_USERREQ__PRINTF = 0x1401,
- VG_USERREQ__PRINTF_BACKTRACE = 0x1402,
- /* both :: char* fmt, va_list* vargs */
- VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403,
- VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404,
-
- /* Stack support. */
- VG_USERREQ__STACK_REGISTER = 0x1501,
- VG_USERREQ__STACK_DEREGISTER = 0x1502,
- VG_USERREQ__STACK_CHANGE = 0x1503,
-
- /* Wine support */
- VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601
- } Vg_ClientRequest;
-
-#if !defined(__GNUC__)
-# define __extension__ /* */
-#endif
-
-/* Returns the number of Valgrinds this code is running under. That
- is, 0 if running natively, 1 if running under Valgrind, 2 if
- running under Valgrind which is running under another Valgrind,
- etc. */
-#define RUNNING_ON_VALGRIND __extension__ \
- ({unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* if not */, \
- VG_USERREQ__RUNNING_ON_VALGRIND, \
- 0, 0, 0, 0, 0); \
- _qzz_res; \
- })
-
-
-/* Discard translation of code in the range [_qzz_addr .. _qzz_addr +
- _qzz_len - 1]. Useful if you are debugging a JITter or some such,
- since it provides a way to make sure valgrind will retranslate the
- invalidated area. Returns no value. */
-#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__DISCARD_TRANSLATIONS, \
- _qzz_addr, _qzz_len, 0, 0, 0); \
- }
-
-
-/* These requests are for getting Valgrind itself to print something.
- Possibly with a backtrace. This is a really ugly hack. The return value
- is the number of characters printed, excluding the "**<pid>** " part at the
- start and the backtrace (if present). */
-
-#if defined(NVALGRIND)
-
-# define VALGRIND_PRINTF(...)
-# define VALGRIND_PRINTF_BACKTRACE(...)
-
-#else /* NVALGRIND */
-
-/* Modern GCC will optimize the static routine out if unused,
- and unused attribute will shut down warnings about it. */
-static int VALGRIND_PRINTF(const char *format, ...)
- __attribute__((format(__printf__, 1, 2), __unused__));
-static int
-VALGRIND_PRINTF(const char *format, ...)
-{
- unsigned long _qzz_res;
- va_list vargs;
- va_start(vargs, format);
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0,
- VG_USERREQ__PRINTF_VALIST_BY_REF,
- (unsigned long)format,
- (unsigned long)&vargs,
- 0, 0, 0);
- va_end(vargs);
- return (int)_qzz_res;
-}
-
-static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
- __attribute__((format(__printf__, 1, 2), __unused__));
-static int
-VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
-{
- unsigned long _qzz_res;
- va_list vargs;
- va_start(vargs, format);
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0,
- VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF,
- (unsigned long)format,
- (unsigned long)&vargs,
- 0, 0, 0);
- va_end(vargs);
- return (int)_qzz_res;
-}
-
-#endif /* NVALGRIND */
-
-
-/* These requests allow control to move from the simulated CPU to the
- real CPU, calling an arbitary function.
-
- Note that the current ThreadId is inserted as the first argument.
- So this call:
-
- VALGRIND_NON_SIMD_CALL2(f, arg1, arg2)
-
- requires f to have this signature:
-
- Word f(Word tid, Word arg1, Word arg2)
-
- where "Word" is a word-sized type.
-
- Note that these client requests are not entirely reliable. For example,
- if you call a function with them that subsequently calls printf(),
- there's a high chance Valgrind will crash. Generally, your prospects of
- these working are made higher if the called function does not refer to
- any global variables, and does not refer to any libc or other functions
- (printf et al). Any kind of entanglement with libc or dynamic linking is
- likely to have a bad outcome, for tricky reasons which we've grappled
- with a lot in the past.
-*/
-#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \
- __extension__ \
- ({unsigned long _qyy_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
- VG_USERREQ__CLIENT_CALL0, \
- _qyy_fn, \
- 0, 0, 0, 0); \
- _qyy_res; \
- })
-
-#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \
- __extension__ \
- ({unsigned long _qyy_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
- VG_USERREQ__CLIENT_CALL1, \
- _qyy_fn, \
- _qyy_arg1, 0, 0, 0); \
- _qyy_res; \
- })
-
-#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \
- __extension__ \
- ({unsigned long _qyy_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
- VG_USERREQ__CLIENT_CALL2, \
- _qyy_fn, \
- _qyy_arg1, _qyy_arg2, 0, 0); \
- _qyy_res; \
- })
-
-#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \
- __extension__ \
- ({unsigned long _qyy_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
- VG_USERREQ__CLIENT_CALL3, \
- _qyy_fn, \
- _qyy_arg1, _qyy_arg2, \
- _qyy_arg3, 0); \
- _qyy_res; \
- })
-
-
-/* Counts the number of errors that have been recorded by a tool. Nb:
- the tool must record the errors with VG_(maybe_record_error)() or
- VG_(unique_error)() for them to be counted. */
-#define VALGRIND_COUNT_ERRORS \
- __extension__ \
- ({unsigned int _qyy_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
- VG_USERREQ__COUNT_ERRORS, \
- 0, 0, 0, 0, 0); \
- _qyy_res; \
- })
-
-/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing
- when heap blocks are allocated in order to give accurate results. This
- happens automatically for the standard allocator functions such as
- malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete,
- delete[], etc.
-
- But if your program uses a custom allocator, this doesn't automatically
- happen, and Valgrind will not do as well. For example, if you allocate
- superblocks with mmap() and then allocates chunks of the superblocks, all
- Valgrind's observations will be at the mmap() level and it won't know that
- the chunks should be considered separate entities. In Memcheck's case,
- that means you probably won't get heap block overrun detection (because
- there won't be redzones marked as unaddressable) and you definitely won't
- get any leak detection.
-
- The following client requests allow a custom allocator to be annotated so
- that it can be handled accurately by Valgrind.
-
- VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated
- by a malloc()-like function. For Memcheck (an illustrative case), this
- does two things:
-
- - It records that the block has been allocated. This means any addresses
- within the block mentioned in error messages will be
- identified as belonging to the block. It also means that if the block
- isn't freed it will be detected by the leak checker.
-
- - It marks the block as being addressable and undefined (if 'is_zeroed' is
- not set), or addressable and defined (if 'is_zeroed' is set). This
- controls how accesses to the block by the program are handled.
-
- 'addr' is the start of the usable block (ie. after any
- redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator
- can apply redzones -- these are blocks of padding at the start and end of
- each block. Adding redzones is recommended as it makes it much more likely
- Valgrind will spot block overruns. `is_zeroed' indicates if the memory is
- zeroed (or filled with another predictable value), as is the case for
- calloc().
-
- VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a
- heap block -- that will be used by the client program -- is allocated.
- It's best to put it at the outermost level of the allocator if possible;
- for example, if you have a function my_alloc() which calls
- internal_alloc(), and the client request is put inside internal_alloc(),
- stack traces relating to the heap block will contain entries for both
- my_alloc() and internal_alloc(), which is probably not what you want.
-
- For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out
- custom blocks from within a heap block, B, that has been allocated with
- malloc/calloc/new/etc, then block B will be *ignored* during leak-checking
- -- the custom blocks will take precedence.
-
- VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For
- Memcheck, it does two things:
-
- - It records that the block has been deallocated. This assumes that the
- block was annotated as having been allocated via
- VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued.
-
- - It marks the block as being unaddressable.
-
- VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a
- heap block is deallocated.
-
- In many cases, these two client requests will not be enough to get your
- allocator working well with Memcheck. More specifically, if your allocator
- writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call
- will be necessary to mark the memory as addressable just before the zeroing
- occurs, otherwise you'll get a lot of invalid write errors. For example,
- you'll need to do this if your allocator recycles freed blocks, but it
- zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK).
- Alternatively, if your allocator reuses freed blocks for allocator-internal
- data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary.
-
- Really, what's happening is a blurring of the lines between the client
- program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the
- memory should be considered unaddressable to the client program, but the
- allocator knows more than the rest of the client program and so may be able
- to safely access it. Extra client requests are necessary for Valgrind to
- understand the distinction between the allocator and the rest of the
- program.
-
- Note: there is currently no VALGRIND_REALLOCLIKE_BLOCK client request; it
- has to be emulated with MALLOCLIKE/FREELIKE and memory copying.
-
- Ignored if addr == 0.
-*/
-#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__MALLOCLIKE_BLOCK, \
- addr, sizeB, rzB, is_zeroed, 0); \
- }
-
-/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details.
- Ignored if addr == 0.
-*/
-#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__FREELIKE_BLOCK, \
- addr, rzB, 0, 0, 0); \
- }
-
-/* Create a memory pool. */
-#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__CREATE_MEMPOOL, \
- pool, rzB, is_zeroed, 0, 0); \
- }
-
-/* Destroy a memory pool. */
-#define VALGRIND_DESTROY_MEMPOOL(pool) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__DESTROY_MEMPOOL, \
- pool, 0, 0, 0, 0); \
- }
-
-/* Associate a piece of memory with a memory pool. */
-#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__MEMPOOL_ALLOC, \
- pool, addr, size, 0, 0); \
- }
-
-/* Disassociate a piece of memory from a memory pool. */
-#define VALGRIND_MEMPOOL_FREE(pool, addr) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__MEMPOOL_FREE, \
- pool, addr, 0, 0, 0); \
- }
-
-/* Disassociate any pieces outside a particular range. */
-#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__MEMPOOL_TRIM, \
- pool, addr, size, 0, 0); \
- }
-
-/* Resize and/or move a piece associated with a memory pool. */
-#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__MOVE_MEMPOOL, \
- poolA, poolB, 0, 0, 0); \
- }
-
-/* Resize and/or move a piece associated with a memory pool. */
-#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__MEMPOOL_CHANGE, \
- pool, addrA, addrB, size, 0); \
- }
-
-/* Return 1 if a mempool exists, else 0. */
-#define VALGRIND_MEMPOOL_EXISTS(pool) \
- __extension__ \
- ({unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__MEMPOOL_EXISTS, \
- pool, 0, 0, 0, 0); \
- _qzz_res; \
- })
-
-/* Mark a piece of memory as being a stack. Returns a stack id. */
-#define VALGRIND_STACK_REGISTER(start, end) \
- __extension__ \
- ({unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__STACK_REGISTER, \
- start, end, 0, 0, 0); \
- _qzz_res; \
- })
-
-/* Unmark the piece of memory associated with a stack id as being a
- stack. */
-#define VALGRIND_STACK_DEREGISTER(id) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__STACK_DEREGISTER, \
- id, 0, 0, 0, 0); \
- }
-
-/* Change the start and end address of the stack id. */
-#define VALGRIND_STACK_CHANGE(id, start, end) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__STACK_CHANGE, \
- id, start, end, 0, 0); \
- }
-
-/* Load PDB debug info for Wine PE image_map. */
-#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \
- {unsigned int _qzz_res; \
- VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
- VG_USERREQ__LOAD_PDB_DEBUGINFO, \
- fd, ptr, total_size, delta, 0); \
- }
-
-
-#undef PLAT_x86_linux
-#undef PLAT_amd64_linux
-#undef PLAT_ppc32_linux
-#undef PLAT_ppc64_linux
-#undef PLAT_arm_linux
-#undef PLAT_ppc32_aix5
-#undef PLAT_ppc64_aix5
-
-#endif /* __VALGRIND_H */