Compare commits
437 commits
feature/st
...
2.0
Author | SHA1 | Date | |
---|---|---|---|
ade1527bef | |||
|
c1aab00c40 | ||
|
9887545ab0 | ||
|
c105117b40 | ||
|
9168f7e4d4 | ||
|
58d8b0c028 | ||
|
91405adda5 | ||
|
b6639e557f | ||
|
c8123430a9 | ||
|
9b69f38527 | ||
|
25cc816025 | ||
|
edbeb0631d | ||
|
04d52a6dd1 | ||
|
cfed420cd3 | ||
|
76e2770304 | ||
|
90450def8b | ||
|
2f3e0b01ff | ||
|
bf11f8e2e6 | ||
|
f0dd25bd53 | ||
|
e533bae646 | ||
|
b44f052897 | ||
|
06c4fc41bf | ||
|
35c5d13df8 | ||
|
4f3c1cd994 | ||
|
5114d894e5 | ||
|
79a3bc7c50 | ||
|
9c8feff07f | ||
|
b13dc8aa6e | ||
|
eca8d9992e | ||
|
c9743f3904 | ||
|
383717a8c7 | ||
|
673bb2f6d9 | ||
|
4babe735e8 | ||
|
0072b618b6 | ||
|
00ec8b7f2b | ||
|
d2fcf2f2d4 | ||
|
da0c89b6db | ||
|
3228fb8cb5 | ||
|
ff1ef2c148 | ||
|
ac7af9697c | ||
|
36198aefba | ||
|
e20e48a5ce | ||
|
79fd587615 | ||
|
1b9e007dfc | ||
|
81f77ffe5a | ||
|
0d70b8f5de | ||
|
577cfa0ccb | ||
|
3dac164cec | ||
|
f8a3741211 | ||
|
845c47907f | ||
|
2ad6b4c4f0 | ||
|
3364df80ee | ||
|
db91289ab6 | ||
|
d516237aab | ||
|
41685d402d | ||
|
27d4d3191e | ||
|
fd34c7244a | ||
|
2b332bffba | ||
|
91cfc9d282 | ||
|
fdc698f924 | ||
|
0485554d44 | ||
|
4b066dd0fc | ||
|
43666a1183 | ||
|
3f410ef5d4 | ||
|
8e18a995d8 | ||
|
410f66bc1a | ||
|
848b8efdca | ||
|
2a08bc62d5 | ||
|
917fa6e385 | ||
|
4ab222a308 | ||
|
9761bdeec3 | ||
|
69e31ce07a | ||
|
22a6b99b34 | ||
|
8abc03ea56 | ||
|
b3dbf75da5 | ||
|
afd2c486c6 | ||
|
e701bfd596 | ||
|
d1c06cd3e9 | ||
|
2be2a08750 | ||
|
e9afe4b6cf | ||
|
3b9bb5745a | ||
|
179ee52801 | ||
|
081ccb2bde | ||
|
cf92c9dc96 | ||
|
6565274ce8 | ||
|
1fceec760f | ||
|
2f96c59929 | ||
|
a2396fe15b | ||
|
2b37680087 | ||
|
59d182b1c1 | ||
|
1eeff1598a | ||
|
f8131b53d6 | ||
|
55de164023 | ||
|
8d0b90f6e1 | ||
|
5cab368af2 | ||
|
021c54308a | ||
|
0d78599a57 | ||
|
234e64f584 | ||
|
7e4c74d625 | ||
|
267ab34c36 | ||
|
cefefb239a | ||
|
341a29387b | ||
|
9411039fce | ||
|
ffc9e8571b | ||
|
75438ef3d3 | ||
|
0ba956159c | ||
|
5a644ee046 | ||
|
ef910789c0 | ||
|
67fa8929f7 | ||
|
839001afec | ||
|
40e0e1c2a0 | ||
|
b7c0132fa9 | ||
|
400768394d | ||
|
c609003c95 | ||
|
44ee48724f | ||
|
82611c93b5 | ||
|
daee40756c | ||
|
03031e02aa | ||
|
c3ef959539 | ||
|
29a2a934d0 | ||
|
ca5ad7497e | ||
|
49ffb454f7 | ||
|
ef55fb9cdb | ||
|
1f1ab15cdd | ||
|
b0971e1553 | ||
|
c128cdef13 | ||
|
3808cb37aa | ||
|
a2f5205bc5 | ||
|
973a2cb85c | ||
|
6275e8724c | ||
|
e5ef80a200 | ||
|
5e6252d8a3 | ||
|
63577cfecc | ||
|
88dc323f7f | ||
|
38651578f9 | ||
|
122d85d9e1 | ||
|
520dba47a5 | ||
|
dbc18a86c4 | ||
|
2484b7328c | ||
|
af8acd0286 | ||
|
35b8bb1cf2 | ||
|
84a7d83812 | ||
|
0407c48884 | ||
|
b52af2acbd | ||
|
150f7953a2 | ||
|
acb2ff1767 | ||
|
cd430968ef | ||
|
4ce9068963 | ||
|
d4850d3564 | ||
|
28726b27af | ||
|
89e4517772 | ||
|
f795785dce | ||
|
2f3bcb7116 | ||
|
b70c239b2c | ||
|
12947c74d0 | ||
|
3376d1b771 | ||
|
c18c466a9b | ||
|
fbe3fb66b1 | ||
|
a2994cfdac | ||
|
34269aa045 | ||
|
0d9fbdbff7 | ||
|
659dc19b1b | ||
|
5d35d34ab3 | ||
|
3e57819bdf | ||
|
6234166ffd | ||
|
5002272a33 | ||
|
b6d76f75c1 | ||
|
5f39b73211 | ||
|
031a37514e | ||
|
d12296af59 | ||
|
eb2ff77e5c | ||
|
292b082c78 | ||
|
d9a7deab9a | ||
|
d5296df683 | ||
|
5eb3e483b3 | ||
|
f445c261c6 | ||
|
3a62cb6559 | ||
|
85f1b218a5 | ||
|
50643bb25f | ||
|
868ee4a27b | ||
|
01239c96f6 | ||
|
ea286b2c56 | ||
|
17276ae8eb | ||
|
c7fcba3ee5 | ||
|
607a285426 | ||
|
82143dfacb | ||
|
0f01283e78 | ||
|
7a39929776 | ||
|
bd22e7b35c | ||
|
21dcdd5807 | ||
|
84a27bdf05 | ||
|
b80270afe9 | ||
|
3629452fe0 | ||
|
5d22dc54af | ||
|
302763b783 | ||
|
8c5b25949e | ||
|
3963bddb1f | ||
|
b3482c7792 | ||
|
6eb509beb2 | ||
|
21d9c486c0 | ||
|
270fd1dd7b | ||
|
1dca0fb453 | ||
|
13900d44ba | ||
|
da026219be | ||
|
72f10015d5 | ||
|
1eeae6d26e | ||
|
9a9572a0d5 | ||
|
04ac654440 | ||
|
d00ac51809 | ||
|
db35d03217 | ||
|
6e468fd5b5 | ||
|
5e00b60f79 | ||
|
cc253e069a | ||
|
7754043252 | ||
|
a697301c44 | ||
|
8ebaac0536 | ||
|
fb0cb84c3c | ||
|
25e3644630 | ||
|
9eab3619c1 | ||
|
ce4095af35 | ||
|
3e03f8ed97 | ||
|
c103f669a0 | ||
|
34e779c84a | ||
|
f74126e0a8 | ||
|
09cdbfaa60 | ||
|
fb65694934 | ||
|
98f27dadc9 | ||
|
db55c63b9d | ||
|
03b5d107cd | ||
|
360646e680 | ||
|
a8cd804402 | ||
|
f21d070803 | ||
|
594d5361dd | ||
|
5361a52b58 | ||
|
31974a6e9c | ||
|
2c5f135600 | ||
|
7f8684d808 | ||
|
b7a20774f2 | ||
|
4611566512 | ||
|
4180b30be3 | ||
|
f4b776ce35 | ||
|
2e7ec1357d | ||
|
e4eada3250 | ||
|
5137d4d8e4 | ||
|
8e39e58c88 | ||
|
36a330292b | ||
|
f03fcf7a5b | ||
|
a1afa303f2 | ||
|
a7a3621cfe | ||
|
190986fb31 | ||
|
c5ed7e202c | ||
|
a361e1fe58 | ||
|
6834f0eccd | ||
|
c5dce1d232 | ||
|
dc61d54530 | ||
|
7171c2b195 | ||
|
47dcc6921b | ||
|
ea2efdaaea | ||
|
911eb17bf4 | ||
|
4d68ae6841 | ||
|
15ad4b5890 | ||
|
b2946352f1 | ||
|
a3c527e1b2 | ||
|
2ff85c876d | ||
|
db45ae3e53 | ||
|
cbd9c1cda8 | ||
|
24db602d1a | ||
|
23cfbe167f | ||
|
2aa3509845 | ||
|
da87115761 | ||
|
4b2a80112e | ||
|
c7220c5616 | ||
|
bad59e99be | ||
|
a736672323 | ||
|
f95cce7407 | ||
|
5609fd5f9e | ||
|
053f9202f6 | ||
|
09da7a0b6c | ||
|
8bb68ab31d | ||
|
7b65457e52 | ||
|
f3da92936c | ||
|
4ac3d74fd3 | ||
|
b68e14dd66 | ||
|
67366bf688 | ||
|
8cbce697eb | ||
|
12de194a28 | ||
|
65052fbe5c | ||
|
cee5d0a3e0 | ||
|
7c7e8b22e4 | ||
|
b6bf81e3e9 | ||
|
db0e1c074f | ||
|
f26397c74c | ||
|
d8945980a7 | ||
|
8e3a35b06f | ||
|
7cae9bf71a | ||
|
2890ed142e | ||
|
af5a89b3ea | ||
|
712767c5b6 | ||
|
ea7fa973cd | ||
|
15021b11c9 | ||
|
1f21ea19a8 | ||
|
7cc84867ce | ||
|
2774221ab7 | ||
|
04d8977b21 | ||
|
16d7cc207f | ||
|
324d7014b4 | ||
|
33e16138bd | ||
|
6333261f28 | ||
|
7d69ec1d81 | ||
|
dbefe7534e | ||
|
d370985048 | ||
|
8c35987760 | ||
|
ba1106df4b | ||
|
f2c8005707 | ||
|
2465ee4ffe | ||
|
9441f974de | ||
|
fd1e5f7f50 | ||
|
9f58a5b651 | ||
|
b4ce60de59 | ||
|
f3bf32b922 | ||
|
f5221fd5c9 | ||
|
aace74f7a5 | ||
|
20c6a84f7c | ||
|
b80f90dad9 | ||
|
7597398c08 | ||
|
2a7998b76f | ||
|
b2a9ad47d8 | ||
|
9271f0bf08 | ||
|
57b4fde126 | ||
|
d120389415 | ||
|
17b4201197 | ||
|
a6be7f5061 | ||
|
0eb9cad11c | ||
|
3512bf033a | ||
|
493a9d7d8e | ||
|
ee7f1d32f7 | ||
|
7cfdfbb3f5 | ||
|
44b8068c58 | ||
|
2e6fc6f0f1 | ||
|
f9b7dd8522 | ||
|
00c364dacb | ||
|
c801164c88 | ||
|
fcc00deadb | ||
|
20e3f1a3ba | ||
|
8775c3ff85 | ||
|
d4d1a59728 | ||
|
359d9df369 | ||
|
6dfd37d43d | ||
|
2a75861741 | ||
|
80b9f00739 | ||
|
24b84d068d | ||
|
0636c9dad3 | ||
|
39fc24e3fa | ||
|
c52639c431 | ||
|
e1e5c3a4ae | ||
|
258128f30d | ||
|
da2f2311c9 | ||
|
70693ea1f2 | ||
|
2858ede695 | ||
|
dc407f2cac | ||
|
8d2a473f54 | ||
|
779d9a9859 | ||
|
76182660f9 | ||
|
c5501ab5f6 | ||
|
8f9c2acc5c | ||
|
9820384ed5 | ||
|
7d6209d99c | ||
|
93c3d0769b | ||
|
9417bc76eb | ||
|
7cdfaa0f7f | ||
|
b139d8d3f2 | ||
|
7d69e79535 | ||
|
6e6de35fd0 | ||
|
adde7bc361 | ||
|
8ee11aaa6f | ||
|
f5f24aa15e | ||
|
998560634e | ||
|
40fa19002a | ||
|
bc27293f36 | ||
|
8f8dbd520b | ||
|
89fb801cfd | ||
|
2df5c11d09 | ||
|
a946a79223 | ||
|
4c26995e41 | ||
|
f670f08639 | ||
|
1a4eb14a05 | ||
|
642fdfca88 | ||
|
fe57cbf54d | ||
|
4bb3ba9d04 | ||
|
4f93a530e1 | ||
|
188a687150 | ||
|
a19d2377cc | ||
|
7d9b6c8889 | ||
|
b61d5a78ae | ||
|
e182dd3a7b | ||
|
9433e2192a | ||
|
15933a061e | ||
|
e74deece23 | ||
|
c58d48f962 | ||
|
d400789d96 | ||
|
072c72621c | ||
|
9cb2f80e1f | ||
|
a2bec00a9e | ||
|
f5996731ca | ||
|
34ec73ecbc | ||
|
e12ca5840e | ||
|
1c1063e3b6 | ||
|
cd16d67663 | ||
|
cf734a480c | ||
|
60821aac0c | ||
|
f596c7392d | ||
|
10d6a64eac | ||
|
6cc57d545f | ||
|
9f8176447b | ||
|
54397987bd | ||
|
533eb6b9c3 | ||
|
aaf35d22d8 | ||
|
f597239986 | ||
|
7aaed3e5d7 | ||
|
a99547bea5 | ||
|
94c71b5431 | ||
|
ef9962c958 | ||
|
da47f06bdc | ||
|
568400c1b7 | ||
|
0909ab8af9 | ||
|
c1cd7444cb | ||
|
dcf4bf0f14 | ||
|
7b70d436e8 | ||
|
c69d483fc7 | ||
|
3104802101 | ||
|
adc96ae2d2 | ||
|
6f66cc4f7a | ||
|
b737fbb130 | ||
|
a5217a9362 | ||
|
6358da7df8 | ||
|
f3c6296a7a | ||
|
3fd773f889 |
98 changed files with 16153 additions and 1993 deletions
70
.github/workflows/ci.yml
vendored
Normal file
70
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
name: ci
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
autotools:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
sudo apt update -qq
|
||||
sudo apt install -qq check lcov
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --enable-check --enable-debug --enable-gcov
|
||||
make V=1
|
||||
- name: Install
|
||||
run: sudo make install
|
||||
- name: Run tests
|
||||
run: make check
|
||||
- name: Collect coverage
|
||||
run: lcov --capture -d '.' --exclude '/usr*' -o coverage.info
|
||||
- name: Upload coverage
|
||||
if: github.repository == 'c9s/r3'
|
||||
uses: coverallsapp/github-action@1.1.3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path-to-lcov: coverage.info
|
||||
|
||||
cmake:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
sudo apt update -qq
|
||||
sudo apt install -qq check ninja-build
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build and test
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake -GNinja ..
|
||||
ninja -v
|
||||
ctest --verbose
|
||||
|
||||
sanitizers:
|
||||
name: ${{ matrix.sanitizer }}-sanitizer [${{ matrix.compiler }}]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler: [gcc, clang]
|
||||
sanitizer: [thread, undefined, leak, address]
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
sudo apt update -qq
|
||||
sudo apt install -qq check
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
CFLAGS="-fsanitize=${{ matrix.sanitizer }} -fno-sanitize-recover=all -fno-omit-frame-pointer" cmake ..
|
||||
VERBOSE=1 make all
|
||||
- name: Test
|
||||
run: |
|
||||
cd build
|
||||
ctest --verbose
|
21
.github/workflows/coverity.yml
vendored
Normal file
21
.github/workflows/coverity.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
name: coverity
|
||||
on:
|
||||
push:
|
||||
branches: [2.0]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
if: github.repository == 'c9s/r3'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Prepare
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --enable-check
|
||||
- uses: vapier/coverity-scan-action@v1
|
||||
with:
|
||||
project: r3
|
||||
email: yoanlin93+github@gmail.com
|
||||
token: ${{ secrets.COVERITY_TOKEN }}
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -51,3 +51,8 @@ autoscan.log
|
|||
r3.pc
|
||||
stamp-h1
|
||||
tests/bench_str.csv
|
||||
tests/check_host
|
||||
|
||||
config.h.in
|
||||
examples/simple
|
||||
examples/simple_cpp
|
||||
|
|
5
.travis-ci/after_success.sh
Executable file
5
.travis-ci/after_success.sh
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ x$COVERALLS == xyes ]; then
|
||||
coveralls --exclude php --exclude 3rdparty
|
||||
fi
|
24
.travis-ci/install.sh
Executable file
24
.travis-ci/install.sh
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/bin/sh
|
||||
|
||||
apt-get update -qq
|
||||
apt-get install -qq \
|
||||
autoconf \
|
||||
automake \
|
||||
build-essential \
|
||||
check \
|
||||
clang \
|
||||
cmake \
|
||||
graphviz-dev \
|
||||
libjemalloc-dev \
|
||||
libpcre2-dev \
|
||||
libtool \
|
||||
ninja-build \
|
||||
pkg-config
|
||||
|
||||
if [ x$COVERALLS == xyes ]; then
|
||||
pip install cpp-coveralls
|
||||
fi
|
||||
|
||||
if [ x$VALGRIND == xyes ]; then
|
||||
apt-get install valgrind
|
||||
fi
|
18
.travis-ci/script-autotools.sh
Executable file
18
.travis-ci/script-autotools.sh
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -ev
|
||||
|
||||
./autogen.sh
|
||||
./configure --enable-check $CONFIGURE_OPTION
|
||||
make V=1
|
||||
make install
|
||||
if [ x$VALGRIND == xyes ]; then
|
||||
make check
|
||||
else
|
||||
make check V=1
|
||||
fi
|
||||
|
||||
# XXX: tracing memory leak, disabled for some mystery reason for automake...
|
||||
#if [ x$VALGRIND == xyes && x$DEBUG == xyes ]; then
|
||||
# valgrind ./tests/check_* -v --trace-children=yes --show-leak-kinds=full --leak-check=full
|
||||
#fi
|
8
.travis-ci/script-cmake.sh
Executable file
8
.travis-ci/script-cmake.sh
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -ev
|
||||
|
||||
mkdir build && cd build
|
||||
cmake -GNinja ..
|
||||
ninja -v
|
||||
ctest
|
120
.travis.yml
120
.travis.yml
|
@ -1,37 +1,111 @@
|
|||
language: c
|
||||
sudo: required
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
services:
|
||||
- docker
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
env: CONFIGURE_OPTION='--enable-debug --with-malloc=jemalloc' COVERALLS=yes VALGRIND=no
|
||||
env:
|
||||
- TYPE=autotools
|
||||
- CONFIGURE_OPTION='--enable-debug --enable-gcov --with-malloc=jemalloc'
|
||||
- COVERALLS=yes
|
||||
- VALGRIND=no
|
||||
- DEBUG=yes
|
||||
- CC=gcc
|
||||
- CXX=g++
|
||||
- compiler: gcc
|
||||
env: CONFIGURE_OPTION='--enable-debug' COVERALLS=yes VALGRIND=yes LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/
|
||||
env:
|
||||
- TYPE=autotools
|
||||
- CONFIGURE_OPTION='--enable-debug --enable-gcov'
|
||||
- COVERALLS=yes
|
||||
- VALGRIND=yes
|
||||
- DEBUG=yes
|
||||
- CC=gcc
|
||||
- CXX=g++
|
||||
- compiler: clang
|
||||
env: ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/local/clang-3.4/bin/llvm-symbolizer CFLAGS='-fsanitize=address -g -O1'
|
||||
env:
|
||||
- TYPE=autotools
|
||||
- CONFIGURE_OPTION='--enable-debug --enable-gcov'
|
||||
- COVERALLS=yes
|
||||
- VALGRIND=yes
|
||||
- DEBUG=yes
|
||||
- CC=clang
|
||||
- CXX=clang++
|
||||
- compiler: gcc
|
||||
env:
|
||||
- TYPE=cmake
|
||||
- CONFIGURE_OPTION='--enable-debug --enable-gcov'
|
||||
- COVERALLS=yes
|
||||
- VALGRIND=yes
|
||||
- DEBUG=yes
|
||||
- CC=gcc
|
||||
- CXX=g++
|
||||
#power Jobs Added
|
||||
- compiler: gcc
|
||||
arch: pc64le
|
||||
env:
|
||||
- TYPE=autotools
|
||||
- CONFIGURE_OPTION='--enable-debug --enable-gcov --with-malloc=jemalloc'
|
||||
- COVERALLS=yes
|
||||
- VALGRIND=no
|
||||
- DEBUG=yes
|
||||
- CC=gcc
|
||||
- CXX=g++
|
||||
- compiler: gcc
|
||||
arch: ppc64le
|
||||
env:
|
||||
- TYPE=autotools
|
||||
- CONFIGURE_OPTION='--enable-debug --enable-gcov'
|
||||
- COVERALLS=yes
|
||||
- VALGRIND=yes
|
||||
- DEBUG=yes
|
||||
- CC=gcc
|
||||
- CXX=g++
|
||||
- compiler: clang
|
||||
arch: ppc64le
|
||||
env:
|
||||
- TYPE=autotools
|
||||
- CONFIGURE_OPTION='--enable-debug --enable-gcov'
|
||||
- COVERALLS=yes
|
||||
- VALGRIND=yes
|
||||
- DEBUG=yes
|
||||
- CC=clang
|
||||
- CXX=clang++
|
||||
- compiler: gcc
|
||||
arch: ppc64le
|
||||
env:
|
||||
- TYPE=cmake
|
||||
- CONFIGURE_OPTION='--enable-debug --enable-gcov'
|
||||
- COVERALLS=yes
|
||||
- VALGRIND=yes
|
||||
- DEBUG=yes
|
||||
- CC=gcc
|
||||
- CXX=g++
|
||||
|
||||
before_install:
|
||||
- docker run -d
|
||||
--name build
|
||||
-v $(pwd):/travis
|
||||
-e "CONFIGURE_OPTION=$CONFIGURE_OPTION"
|
||||
-e "COVERALLS=$COVERALLS"
|
||||
-e "VALGRIND=$VALGRIND"
|
||||
-e "DEBUG=$DEBUG"
|
||||
-e "CC=$CC"
|
||||
-e "CXX=$CXX"
|
||||
ubuntu:16.04
|
||||
tail -f /dev/null
|
||||
- docker ps
|
||||
|
||||
install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq automake pkg-config build-essential libtool automake autoconf m4 gnulib
|
||||
- sudo apt-get install -qq check libpcre3 libpcre3-dev libjemalloc-dev libjemalloc1
|
||||
- sudo apt-get install -qq graphviz-dev graphviz
|
||||
- if [ "x$COVERALLS" == xyes ]; then sudo pip install cpp-coveralls; fi
|
||||
- if [ "x$VALGRIND" == xyes ]; then sudo apt-get install valgrind; fi
|
||||
- docker exec -t build bash -c "cd /travis && .travis-ci/install.sh"
|
||||
|
||||
before_script:
|
||||
- sudo ldconfig
|
||||
script:
|
||||
- ./autogen.sh
|
||||
- ./configure $CONFIGURE_OPTION
|
||||
- make V=1
|
||||
- sudo make install
|
||||
- if [ "x$VALGRIND" == xyes ]; then make check > /dev/null 2>&1; else make check V=1; fi
|
||||
- if [ "x$VALGRIND" == xyes ]; then valgrind ./tests/.libs/check_* -v --trace-children=yes --show-leak-kinds=full --leak-check=full; fi
|
||||
- docker exec -t build bash -c "cd /travis && .travis-ci/script-$TYPE.sh"
|
||||
|
||||
after_success:
|
||||
- if [ x$COVERALLS == xyes ]; then coveralls ; fi
|
||||
|
||||
cache:
|
||||
apt: true
|
||||
- docker exec -t build bash -c "cd /travis && .travis-ci/after_success.sh"
|
||||
|
|
39
CHANGES.md
39
CHANGES.md
|
@ -1,3 +1,36 @@
|
|||
# CHANGELOG
|
||||
|
||||
by Yo-An Lin <yoanlin93@gmail.com>
|
||||
|
||||
|
||||
### 2.0 - Wed Nov 11 11:08:22 2015
|
||||
|
||||
- Renamed node/edge struct to R3Node and R3Edge
|
||||
|
||||
|
||||
### 1.3.3 - Sat Jun 28 00:53:48 2014
|
||||
|
||||
- Fix graphviz generator.
|
||||
|
||||
|
||||
### 1.3.2 - Sat Jun 28 00:54:22 2014
|
||||
|
||||
- `HAVE_STRNDUP` and `HAVE_STRDUP` definition fix
|
||||
|
||||
### 1.3.0 - Tue Jun 3 18:47:14 2014
|
||||
|
||||
- Added Incorrect slug syntax warnings
|
||||
- Added error message support for pcre/pcre-jit compile
|
||||
- Added JSON encode support for the tree structure
|
||||
- Improved Graphivz Related Functions
|
||||
- More failing test cases
|
||||
|
||||
### 1.2.1 - Tue May 27 21:16:13 2014
|
||||
|
||||
- Bug fixes.
|
||||
- Function declaration improvement.
|
||||
- pkg-config flags update (r3.pc)
|
||||
|
||||
### 1.2 - Fri May 23 23:30:11 2014
|
||||
|
||||
- Added simple pattern optimization.
|
||||
|
@ -8,15 +41,15 @@
|
|||
|
||||
API changes:
|
||||
|
||||
1. Removed the `route` argument from `r3_tree_insert_pathl_`:
|
||||
1. Removed the `route` argument from `r3_tree_insert_pathl_ex`:
|
||||
|
||||
node * r3_tree_insert_pathl_(node *tree, char *path, int path_len, void * data);
|
||||
node * r3_tree_insert_pathl_ex(node *tree, char *path, int path_len, void * data);
|
||||
|
||||
This reduce the interface complexity, e.g.,
|
||||
|
||||
r3_tree_insert_path(n, "/user2/{id:\\d+}", &var2);
|
||||
|
||||
2. The original `r3_tree_insert_pathl_` has been moved to `r3_tree_insert_pathl_` as a private API.
|
||||
2. The original `r3_tree_insert_pathl_ex` has been moved to `r3_tree_insert_pathl_ex` as a private API.
|
||||
|
||||
3. Moved `r3_tree_matchl` to `r3_tree_matchl` since it require the length of the path string.
|
||||
|
||||
|
|
49
CMakeLists.txt
Normal file
49
CMakeLists.txt
Normal file
|
@ -0,0 +1,49 @@
|
|||
cmake_minimum_required(VERSION 3.0)
|
||||
project(r3 VERSION 2.0.0)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
|
||||
find_package(Check)
|
||||
find_package(PCRE2 REQUIRED)
|
||||
|
||||
include(CheckSymbolExists)
|
||||
include(CheckIncludeFile)
|
||||
check_symbol_exists(strdup string.h HAVE_STRDUP)
|
||||
check_symbol_exists(strndup string.h HAVE_STRNDUP)
|
||||
check_include_file(stdbool.h HAVE_STDBOOL_H)
|
||||
configure_file(config.h.cmake config.h)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
install(
|
||||
FILES
|
||||
include/memory.h
|
||||
include/r3.h
|
||||
include/r3_list.h
|
||||
include/r3_slug.h
|
||||
include/r3_gvc.h
|
||||
include/r3_json.h
|
||||
include/str_array.h
|
||||
include/r3.hpp
|
||||
DESTINATION include/r3)
|
||||
|
||||
# Configure substitutions for r3.pc. The variables set here must match the
|
||||
# @<values>@ in r3.pc.in.
|
||||
set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(exec_prefix ${prefix})
|
||||
set(includedir ${prefix}/include)
|
||||
set(libdir ${prefix}/lib)
|
||||
set(PACKAGE_VERSION ${PROJECT_VERSION})
|
||||
configure_file(r3.pc.in r3.pc @ONLY)
|
||||
install(
|
||||
FILES
|
||||
${PROJECT_BINARY_DIR}/r3.pc
|
||||
DESTINATION lib/pkgconfig)
|
||||
|
||||
if(CHECK_FOUND)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
else()
|
||||
message(STATUS "Skipping unit tests, Check library not found!")
|
||||
endif()
|
33
Makefile.am
33
Makefile.am
|
@ -1,15 +1,40 @@
|
|||
SUBDIRS=src tests
|
||||
SUBDIRS=src . examples
|
||||
|
||||
if ENABLE_CHECK
|
||||
SUBDIRS += tests
|
||||
endif
|
||||
|
||||
lib_LTLIBRARIES = libr3.la
|
||||
libr3_la_SOURCES =
|
||||
libr3_la_LIBADD = src/libr3core.la
|
||||
libr3_la_LDFLAGS =
|
||||
|
||||
AM_CFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include -Wall -std=c99
|
||||
AM_LDFLAGS=$(DEPS_LIBS) $(GVC_DEPS_LIBS)
|
||||
|
||||
|
||||
ACLOCAL_AMFLAGS=-I m4
|
||||
|
||||
if ENABLE_DEBUG
|
||||
AM_CFLAGS += -ggdb -fprofile-arcs -ftest-coverage
|
||||
else
|
||||
AM_CFLAGS += -O2
|
||||
endif
|
||||
|
||||
if USE_JEMALLOC
|
||||
AM_LDFLAGS += -ljemalloc
|
||||
endif
|
||||
|
||||
r3_includedir = $(includedir)/r3
|
||||
r3_include_HEADERS = \
|
||||
include/memory.h \
|
||||
include/r3.h \
|
||||
include/r3_define.h \
|
||||
include/r3_list.h \
|
||||
include/r3_str.h \
|
||||
include/r3_slug.h \
|
||||
include/r3_gvc.h \
|
||||
include/r3_json.h \
|
||||
include/str_array.h \
|
||||
include/zmalloc.h \
|
||||
include/r3.hpp \
|
||||
$(NULL)
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
|
|
248
README.md
248
README.md
|
@ -1,27 +1,34 @@
|
|||
R3
|
||||
================
|
||||
|
||||
[![Build Status](https://travis-ci.org/c9s/r3.svg?branch=master)](https://travis-ci.org/c9s/r3)
|
||||
[![Build Status](https://travis-ci.org/c9s/r3.svg?branch=2.0)](https://travis-ci.org/c9s/r3)
|
||||
|
||||
[![Coverage Status](https://coveralls.io/repos/c9s/r3/badge.png)](https://coveralls.io/r/c9s/r3)
|
||||
[![Coverage Status](https://coveralls.io/repos/c9s/r3/badge.svg)](https://coveralls.io/r/c9s/r3)
|
||||
|
||||
R3 is an URL router library with high performance, thus, it's implemented in C.
|
||||
It compiles your route paths into a prefix trie.
|
||||
It compiles your R3Route paths into a prefix trie.
|
||||
|
||||
By using the prefix tree constructed in the start-up time, you can dispatch
|
||||
the path to the controller with high efficiency.
|
||||
|
||||
By using the constructed prefix trie in the start-up time, you can dispatch
|
||||
routes with efficiency.
|
||||
|
||||
|
||||
Requirement
|
||||
-----------------------
|
||||
|
||||
### Build Requirement
|
||||
|
||||
* autoconf
|
||||
* automake
|
||||
* check
|
||||
* pcre
|
||||
* graphviz version 2.38.0 (20140413.2041)
|
||||
* pkg-config
|
||||
|
||||
### Runtime Requirement
|
||||
|
||||
* pcre2
|
||||
* (optional) graphviz version 2.38.0 (20140413.2041)
|
||||
* (optional) libjson-c-dev
|
||||
|
||||
Pattern Syntax
|
||||
-----------------------
|
||||
|
||||
|
@ -29,18 +36,18 @@ Pattern Syntax
|
|||
/blog/post/{id:\d+} use `\d+` regular expression instead of default.
|
||||
|
||||
|
||||
C API
|
||||
API
|
||||
------------------------
|
||||
|
||||
```c
|
||||
#include <r3.h>
|
||||
#include <r3/r3.h>
|
||||
|
||||
// create a router tree with 10 children capacity (this capacity can grow dynamically)
|
||||
n = r3_tree_create(10);
|
||||
R3Node *n = r3_tree_create(10);
|
||||
|
||||
int route_data = 3;
|
||||
|
||||
// insert the route path into the router tree
|
||||
// insert the R3Route path into the router tree
|
||||
r3_tree_insert_path(n, "/bar", &route_data); // ignore the length of path
|
||||
|
||||
r3_tree_insert_pathl(n, "/zoo", strlen("/zoo"), &route_data );
|
||||
|
@ -50,25 +57,46 @@ r3_tree_insert_pathl(n ,"/post/{id}", strlen("/post/{id}") , &route_data );
|
|||
|
||||
r3_tree_insert_pathl(n, "/user/{id:\\d+}", strlen("/user/{id:\\d+}"), &route_data );
|
||||
|
||||
|
||||
// if you want to catch error, you may call the extended path function for insertion
|
||||
int data = 10;
|
||||
char *errstr = NULL;
|
||||
R3Node *ret = r3_tree_insert_pathl_ex(n, "/foo/{name:\\d{5}", strlen("/foo/{name:\\d{5}"), NULL, &data, &errstr);
|
||||
if (ret == NULL) {
|
||||
// failed insertion
|
||||
printf("error: %s\n", errstr);
|
||||
free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
|
||||
}
|
||||
|
||||
|
||||
// let's compile the tree!
|
||||
r3_tree_compile(n);
|
||||
char *errstr = NULL;
|
||||
int err = r3_tree_compile(n, &errstr);
|
||||
if (err != 0) {
|
||||
// fail
|
||||
printf("error: %s\n", errstr);
|
||||
free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
|
||||
}
|
||||
|
||||
|
||||
// dump the compiled tree
|
||||
r3_tree_dump(n, 0);
|
||||
|
||||
// match a route
|
||||
node *matched_node = r3_tree_match(n, "/foo/bar", strlen("/foo/bar"), NULL);
|
||||
R3Node *matched_node = r3_tree_matchl(n, "/foo/bar", strlen("/foo/bar"), NULL);
|
||||
if (matched_node) {
|
||||
matched_node->endpoint; // make sure there is a route end at here.
|
||||
int ret = *( (*int) matched_node->route_ptr );
|
||||
int ret = *( (int*) matched_node->data );
|
||||
}
|
||||
|
||||
// release the tree
|
||||
r3_tree_free(n);
|
||||
```
|
||||
|
||||
If you want to capture the variables from regular expression, you will need to create a match entry,
|
||||
|
||||
**Capture Dynamic Variables**
|
||||
|
||||
If you want to capture the variables from regular expression, you will need to
|
||||
create a `match_entry` object and pass the object to `r3_tree_matchl` function,
|
||||
the catched variables will be pushed into the match entry structure:
|
||||
|
||||
```c
|
||||
|
@ -86,37 +114,57 @@ entry->request_method = METHOD_POST;
|
|||
entry->request_method = METHOD_GET | METHOD_POST;
|
||||
```
|
||||
|
||||
When using `match_entry`, you may match the route with `r3_tree_match_entry` function:
|
||||
When using `match_entry`, you may match the R3Route with `r3_tree_match_entry` function:
|
||||
|
||||
```c
|
||||
node *matched_node = r3_tree_match_entry(n, entry);
|
||||
R3Node * matched_node = r3_tree_match_entry(n, entry);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
**Release Memory**
|
||||
|
||||
To release the memory, you may call `r3_tree_free(R3Node *tree)` to release the whole tree structure,
|
||||
`node*`, `edge*`, `route*` objects that were inserted into the tree will be freed.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Routing with conditions
|
||||
|
||||
```c
|
||||
// create the match entry for capturing dynamic variables.
|
||||
match_entry * entry = match_entry_create("/foo/bar");
|
||||
entry->request_method = METHOD_GET;
|
||||
|
||||
// create a router tree with 10 children capacity (this capacity can grow dynamically)
|
||||
n = r3_tree_create(10);
|
||||
|
||||
int route_data = 3;
|
||||
|
||||
// define the route with conditions
|
||||
route *r1 = route_create("/blog/post");
|
||||
r1->request_method = METHOD_GET | METHOD_POST; // ALLOW GET OR POST METHOD
|
||||
// insert the R3Route path into the router tree
|
||||
r3_tree_insert_routel(n, METHOD_GET | METHOD_POST, "/blog/post", sizeof("/blog/post") - 1, &route_data );
|
||||
|
||||
// insert the route path into the router tree
|
||||
r3_tree_insert_route(n, r1, &route_data );
|
||||
r3_tree_compile(n);
|
||||
char *errstr = NULL;
|
||||
int err = r3_tree_compile(n, &errstr);
|
||||
if (err != 0) {
|
||||
// fail
|
||||
printf("error: %s\n", errstr);
|
||||
free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
|
||||
}
|
||||
|
||||
route *matched_route = r3_tree_match_route(n, entry);
|
||||
|
||||
// in your http server handler
|
||||
|
||||
// create the match entry for capturing dynamic variables.
|
||||
match_entry * entry = match_entry_create("/blog/post");
|
||||
entry->request_method = METHOD_GET;
|
||||
|
||||
|
||||
R3Route *matched_R3Route = r3_tree_match_route(n, entry);
|
||||
matched_route->data; // get the data from matched route
|
||||
|
||||
// free the objects at the end
|
||||
r3_route_free(r1);
|
||||
match_entry_free(entry);
|
||||
r3_tree_free(n);
|
||||
```
|
||||
|
||||
|
@ -125,27 +173,27 @@ Slug
|
|||
A slug is a placeholder, which captures the string from the URL as a variable.
|
||||
Slugs will be compiled into regular expression patterns.
|
||||
|
||||
Slugs without specified pattern (like `/user/{userId}`) will be compiled with the `[^/]+` pattern.
|
||||
Slugs without patterns (like `/user/{userId}`) will be compiled into the `[^/]+` pattern.
|
||||
|
||||
To specify the pattern of a slug, you may write a colon to separate the slug name and the pattern:
|
||||
|
||||
"/user/{userId:\\d+}"
|
||||
|
||||
The above route will use `\d+` as its pattern.
|
||||
The above R3Route will use `\d+` as its pattern.
|
||||
|
||||
|
||||
Optimization
|
||||
-----------------------
|
||||
Simple regular expressions are optimized through a regexp pattern to opcode
|
||||
compiler, which translates simple patterns into small & fast scanners.
|
||||
translator, which translates simple patterns into small & fast scanners.
|
||||
|
||||
By using this method, r3 reduces the matching overhead of pcre library.
|
||||
By using this method, r3 reduces the matching overhead of pcre2 library.
|
||||
|
||||
Optimized patterns are: `[a-z]+`, `[0-9]+`, `\d+`, `\w+`, `[^/]+` or `[^-]+`
|
||||
Optimized patterns are: `[a-z]+`, `[0-9]+`, `\d+`, `\w+`, `[^/]+`, `[^-]+` or `.*`.
|
||||
|
||||
slugs without specified regular expression will be compiled with a `[^/]+` pattern. therefore, it's optimized too.
|
||||
Slugs without specified regular expression will be compiled into the `[^/]+` pattern. therefore, it's optimized too.
|
||||
|
||||
Complex regular expressions will still use libpcre to match URL (partially).
|
||||
Complex regular expressions will still use libpcre2 to match URL (partially).
|
||||
|
||||
|
||||
Performance
|
||||
|
@ -158,15 +206,15 @@ And here is the result of the router journey:
|
|||
|
||||
omg 9932.9 (±4.8%) i/s - 49873 in 5.033452s
|
||||
|
||||
r3 uses the same route path data for benchmarking, and here is the benchmark:
|
||||
r3 uses the same R3Route path data for benchmarking, and here is the benchmark:
|
||||
|
||||
3 runs, 5000000 iterations each run, finished in 1.308894 seconds
|
||||
11460057.83 i/sec
|
||||
|
||||
|
||||
### The benchmarking route paths
|
||||
### The Route Paths Of Benchmark
|
||||
|
||||
The route path generator is from <https://github.com/stevegraham/rails/pull/1>:
|
||||
The R3Route path generator is from <https://github.com/stevegraham/rails/pull/1>:
|
||||
|
||||
```ruby
|
||||
#!/usr/bin/env ruby
|
||||
|
@ -191,12 +239,48 @@ Function prefix mapping
|
|||
|
||||
|
||||
|
||||
Rendering routes with graphviz
|
||||
-------------------------------
|
||||
Rendering Routes With Graphviz
|
||||
---------------------------------------
|
||||
|
||||
The `test_gvc_render_file` API let you render the whole route trie into a image.
|
||||
The `r3_tree_render_file` API let you render the whole R3Route trie into a image.
|
||||
|
||||
![Imgur](http://i.imgur.com/J2LdzeK.png)
|
||||
To use graphviz, you need to enable graphviz while you run `configure`:
|
||||
|
||||
|
||||
./configure --enable-graphviz
|
||||
|
||||
|
||||
Here is the sample code of generating graph output:
|
||||
|
||||
|
||||
```c
|
||||
R3Node * n = r3_tree_create(1);
|
||||
|
||||
r3_tree_insert_path(n, "/foo/bar/baz", NULL);
|
||||
r3_tree_insert_path(n, "/foo/bar/qux", NULL);
|
||||
r3_tree_insert_path(n, "/foo/bar/quux", NULL);
|
||||
r3_tree_insert_path(n, "/foo/bar/corge", NULL);
|
||||
r3_tree_insert_path(n, "/foo/bar/grault", NULL);
|
||||
r3_tree_insert_path(n, "/garply/grault/foo", NULL);
|
||||
r3_tree_insert_path(n, "/garply/grault/bar", NULL);
|
||||
r3_tree_insert_path(n, "/user/{id}", NULL);
|
||||
r3_tree_insert_path(n, "/post/{title:\\w+}", NULL);
|
||||
|
||||
char *errstr = NULL;
|
||||
int err;
|
||||
err = r3_tree_compile(n, &errstr);
|
||||
if (err != 0) {
|
||||
// fail
|
||||
printf("error: %s\n", errstr);
|
||||
free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
|
||||
}
|
||||
|
||||
r3_tree_render_file(n, "png", "check_gvc.png");
|
||||
r3_tree_free(n);
|
||||
```
|
||||
|
||||
|
||||
![Imgur](http://imgur.com/HrUoEbI.png)
|
||||
|
||||
Or you can even export it with dot format:
|
||||
|
||||
|
@ -213,6 +297,39 @@ digraph g {
|
|||
....
|
||||
```
|
||||
|
||||
### Graphviz Related Functions
|
||||
|
||||
```c
|
||||
int r3_tree_render_file(const R3Node * tree, const char * format, const char * filename);
|
||||
|
||||
int r3_tree_render(const R3Node * tree, const char *layout, const char * format, FILE *fp);
|
||||
|
||||
int r3_tree_render_dot(const R3Node * tree, const char *layout, FILE *fp);
|
||||
|
||||
int r3_tree_render_file(const R3Node * tree, const char * format, const char * filename);
|
||||
```
|
||||
|
||||
|
||||
JSON Output
|
||||
----------------------------------------
|
||||
|
||||
You can render the whole tree structure into json format output.
|
||||
|
||||
Please run `configure` with the `--enable-json` option.
|
||||
|
||||
Here is the sample code to generate JSON string:
|
||||
|
||||
```c
|
||||
json_object * obj = r3_node_to_json_object(n);
|
||||
|
||||
const char *json = r3_node_to_json_pretty_string(n);
|
||||
printf("Pretty JSON: %s\n",json);
|
||||
|
||||
const char *json = r3_node_to_json_string(n);
|
||||
printf("JSON: %s\n",json);
|
||||
```
|
||||
|
||||
|
||||
Use case in PHP
|
||||
-----------------------
|
||||
**not implemented yet**
|
||||
|
@ -230,7 +347,7 @@ $ret = r3_dispatch($rs, '/blog/post/3' );
|
|||
list($complete, $route, $variables) = $ret;
|
||||
|
||||
// matched conditions aren't done yet
|
||||
list($error, $message) = r3_validate($route); // validate route conditions
|
||||
list($error, $message) = r3_validate($route); // validate R3Route conditions
|
||||
if ( $error ) {
|
||||
echo $message; // "Method not allowed", "...";
|
||||
}
|
||||
|
@ -239,13 +356,25 @@ if ( $error ) {
|
|||
Install
|
||||
----------------------
|
||||
|
||||
sudo apt-get install check libpcre3 libpcre3-dev libjemalloc-dev libjemalloc1 build-essential libtool automake autoconf pkg-config
|
||||
sudo apt-get install check libpcre2 libpcre2-dev libjemalloc-dev libjemalloc1 build-essential libtool automake autoconf pkg-config
|
||||
sudo apt-get install graphviz-dev graphviz # if you want graphviz
|
||||
./autogen.sh
|
||||
./configure && make
|
||||
make check # run tests
|
||||
sudo make install
|
||||
|
||||
And we support debian-based distro now!
|
||||
|
||||
sudo apt-get install build-essential autoconf automake libpcre2-dev pkg-config debhelper libtool check
|
||||
mv dist-debian debian
|
||||
dpkg-buildpackage -b -us -uc
|
||||
sudo gdebi ../libr3*.deb
|
||||
|
||||
|
||||
#### Run Unit Tests
|
||||
|
||||
./configure --enable-check
|
||||
make check
|
||||
|
||||
#### Enable Graphviz
|
||||
|
||||
./configure --enable-graphviz
|
||||
|
@ -254,6 +383,31 @@ Install
|
|||
|
||||
./configure --with-malloc=jemalloc
|
||||
|
||||
ubuntu PPA
|
||||
----------------------
|
||||
|
||||
The PPA for libr3 can be found in <https://launchpad.net/~r3-team/+archive/libr3-daily>.
|
||||
|
||||
Binding For Other Languages
|
||||
---------------------------
|
||||
|
||||
* Perl Router::R3 by @CindyLinz <https://metacpan.org/pod/Router::R3>
|
||||
* Python pyr3 by @lucemia <https://github.com/lucemia/pyr3>
|
||||
* Python pyr3 by @thedrow <https://github.com/thedrow/pyr3>
|
||||
* Haskell r3 by @MnO2 <https://github.com/MnO2/r3>
|
||||
* Vala r3-vala by @Ronmi <https://github.com/Ronmi/r3-vala>
|
||||
|
||||
Node.js
|
||||
|
||||
* node-r3 by @othree <https://github.com/othree/node-r3>
|
||||
* node-libr3 by @caasi <https://github.com/caasi/node-r3>
|
||||
|
||||
Ruby
|
||||
|
||||
* Ruby rr3 by @tonytonyjan <https://github.com/tonytonyjan/rr3>
|
||||
* mruby r3 <https://github.com/rail44/mruby-r3>
|
||||
* mruby rake r3 <https://github.com/rail44/mruby-rack-r3>
|
||||
|
||||
|
||||
License
|
||||
--------------------
|
||||
|
|
69
bench.html
69
bench.html
|
@ -11,6 +11,8 @@
|
|||
<script>
|
||||
$.get('bench_str.csv', function(data) {
|
||||
|
||||
|
||||
|
||||
var options = {
|
||||
chart: {
|
||||
zoomType: 'x',
|
||||
|
@ -70,43 +72,10 @@
|
|||
},
|
||||
|
||||
tooltip: {
|
||||
// headerFormat: '<span style="font-size: 10px">{point.key}: </span><br/>',
|
||||
formatter: function() {
|
||||
var s = '';
|
||||
|
||||
var comment = this.points[1].point.comment;
|
||||
// new Date(this.x) + ':';
|
||||
if (comment) {
|
||||
s += '<span style="font-size: 10px;">';
|
||||
s += comment;
|
||||
s += '</span><br/>';
|
||||
}
|
||||
|
||||
$.each(this.points, function(i, point) {
|
||||
s += '<span style="color:' + point.series.color + '">\u25CF</span> ' + point.series.name + ': <b>' + point.y + '</b><br/>';
|
||||
});
|
||||
return s;
|
||||
},
|
||||
shared: true,
|
||||
crosshairs: true
|
||||
},
|
||||
|
||||
/*
|
||||
tooltip: {
|
||||
formatter: function() {
|
||||
var s = '';
|
||||
$.each(this.points, function(i, point) {
|
||||
s += point.series.name +': '+ point.y + '<br/>';
|
||||
});
|
||||
if (this.points[1] && this.points[1].comment) {
|
||||
s += '<b>'+ this.points[1].comment +'</b>';
|
||||
}
|
||||
return s;
|
||||
},
|
||||
shared: true
|
||||
},
|
||||
*/
|
||||
|
||||
plotOptions: {
|
||||
/*
|
||||
area: {
|
||||
|
@ -180,24 +149,30 @@
|
|||
};
|
||||
|
||||
var lines = data.split(/\n/);
|
||||
$(lines).each(function(i,line) {
|
||||
|
||||
/*
|
||||
{ marker: {
|
||||
fillColor: '#FF0000',
|
||||
lineWidth: 3,
|
||||
lineColor: "#FF0000" // inherit from series
|
||||
// keep window size to 60 records
|
||||
lines = lines.splice(-60);
|
||||
|
||||
$(lines).each(function(i,line) {
|
||||
if (line == "") {
|
||||
return;
|
||||
}
|
||||
*/
|
||||
var columns = line.split(/,/);
|
||||
var commentText = columns[5];
|
||||
for (var i = 1; i < 5; i++ ) {
|
||||
var a = parseInt(columns[i]);
|
||||
var args = a ?
|
||||
(commentText ? { y: a, comment: commentText } : a)
|
||||
: 0;
|
||||
options.series[i-1].data.push(args);
|
||||
if (columns.length == 0) {
|
||||
return;
|
||||
}
|
||||
var a;
|
||||
a = parseInt(columns[1]);
|
||||
options.series[0].data.push(a || 0);
|
||||
|
||||
a = parseInt(columns[2]);
|
||||
options.series[1].data.push(a || 0);
|
||||
|
||||
a = parseInt(columns[3]);
|
||||
options.series[2].data.push(a || 0);
|
||||
|
||||
a = parseInt(columns[4]);
|
||||
options.series[3].data.push(a || 0);
|
||||
});
|
||||
|
||||
$('#chart').highcharts(options);
|
||||
|
|
160
bench_str.csv
160
bench_str.csv
|
@ -443,23 +443,6 @@
|
|||
1400668574,13632260.72
|
||||
1400681414,10832905.89
|
||||
1400685490,13185955.87
|
||||
1400748100,12609470.07
|
||||
1400748288,13317009.48
|
||||
1400748727,12973679.22
|
||||
1400748826,12902583.84
|
||||
1400748965,13584323.91
|
||||
1400749175,13518288.33
|
||||
1400749320,13445606.30
|
||||
1400749326,13242705.99
|
||||
1400749599,13245418.70
|
||||
1400749614,12494314.81
|
||||
1400749643,12690632.63
|
||||
1400750350,10391028.46
|
||||
1400750424,9445761.22
|
||||
1400750472,9248611.74
|
||||
1400750479,10757562.36
|
||||
1400750512,10126746.58
|
||||
1400750536,10568568.26
|
||||
1400762875,10472029.42
|
||||
1400764426,10066458.45,1590373.41
|
||||
1400765068,10657617.64,2131810.12
|
||||
|
@ -510,7 +493,6 @@
|
|||
1400833024,11715363.55,4726544.41,59074.70
|
||||
1400833045,11813359.08,4828190.72,53092.46
|
||||
1400833051,11082009.03,4721512.49,62601.55
|
||||
1400837359,2709622.75,4773140.52,71089.90
|
||||
1400837501,11958680.82,4777890.52,45590.26
|
||||
1400837621,11423775.75,4679207.46,59074.70,2459430.07
|
||||
1400837636,11534281.98,4192617.73,66576.25,2968590.57
|
||||
|
@ -528,11 +510,137 @@
|
|||
1400837774,11190990.08,4331119.44,45590.26,2587281.10
|
||||
1400837785,10306507.50,3909290.89,47662.55,2827471.10
|
||||
1400837797,10323334.38,4221122.48,55924.05,2294463.55
|
||||
1400858847,10663450.41,4674634.54,66576.25,2653715.59
|
||||
1400858857,10336003.37,4496145.54,59074.70,2607583.19
|
||||
1400858869,9640828.18,4607189.58,66576.25,2268341.79
|
||||
1400858880,10959344.32,4114165.27,41943.04,2405089.57
|
||||
1400858891,9456420.72,4254882.18,33554.43,2770969.36
|
||||
1400858902,10904696.64,3989404.21,72315.59,2410755.81
|
||||
1400858913,9379941.50,4385508.67,49932.19,2595906.55
|
||||
1400858923,10653302.72,4655704.49,55188.21,2677379.49
|
||||
1400922358,11238818.94,4585324.58,59074.70,2649691.23
|
||||
1400922668,10605016.95,4435942.19,52428.80,2805666.62
|
||||
1400922931,10906416.86,4595485.60,55924.05,2744562.44
|
||||
1400922941,11009432.85,4532428.61,47662.55,2763157.51
|
||||
1400922951,11053192.20,4323815.83,45590.26,2783158.56
|
||||
1400922974,10151535.91,3794968.57,47662.55,2474383.18
|
||||
1400922986,10218555.01,3887695.21,53092.46,2200366.58
|
||||
1400922997,10051469.51,4460938.45,59074.70,2490310.13
|
||||
1400923283,11180779.62,4365911.08,71089.90,2629220.52
|
||||
1400923294,11193863.27,4516227.63,82241.25,2545079.35
|
||||
1400923304,11423045.65,4137604.47,47662.55,2655584.06
|
||||
1400923315,10840939.91,4663256.98,55924.05,2611667.47
|
||||
1400923325,11091416.01,4103660.70,59074.70,2817256.97
|
||||
1400923335,11435046.07,4356917.52,45100.04,2883103.51
|
||||
1400928121,11717285.79,4816672.70,58254.22,2897866.52
|
||||
1400928131,11343621.37,4759351.32,45100.04,2849262.65
|
||||
1400928141,11268332.55,4717279.18,55188.21,2871378.81
|
||||
1400928151,11308055.40,4652350.21,47662.55,2866742.89
|
||||
1400928225,10802915.60,4687267.34,58254.22,2632936.94
|
||||
1400928235,10661874.62,4739682.52,62601.55,2740810.88
|
||||
1400928262,11033784.15,4714416.68,55188.21,2737984.12
|
||||
1400928272,10649279.69,4567955.12,47662.55,2716598.01
|
||||
1400928319,10230059.66,4527332.79,49932.19,2345303.39
|
||||
1400928329,11085172.21,4647514.08,62601.55,2735715.15
|
||||
1400929424,11030888.11,4442984.66,55924.05,2892178.39
|
||||
1400929434,11544517.30,4792322.97,77672.30,2718374.75
|
||||
1400929444,11729287.31,4167112.20,47662.55,2880342.59
|
||||
1401713584,11611550.10,3615349.83,59074.70,2395398.52
|
||||
1401713613,11497337.95,3867069.72,76260.07,2513425.94
|
||||
1401713624,11012180.78,4218063.79,62601.55,2274326.51
|
||||
1401713638,10375230.94,3665431.16,59074.70,1995737.34
|
||||
1401713649,11430757.90,3840336.12,62601.55,2385432.38
|
||||
1401713661,8970219.06,4509359.41,62601.55,2278745.62
|
||||
1401713673,11040727.70,3743492.87,55188.21,2408080.74
|
||||
1401785936,11348169.84,4097861.38,77672.30,2199971.87
|
||||
1401785947,11312609.98,3904129.33,77672.30,2560323.79
|
||||
1401785958,10537229.10,4396861.86,58254.22,2424404.69
|
||||
1401785969,11206290.89,4517530.60,45100.04,2551294.60
|
||||
1401785980,11248534.36,4396504.83,45100.04,2262115.57
|
||||
1401785991,11198475.79,4330140.21,47662.55,2628625.68
|
||||
1401786002,11160024.50,4168595.71,59074.70,2536134.48
|
||||
1401786013,10430212.17,4125745.93,45590.26,2506666.43
|
||||
1401786024,10213079.29,4298993.08,45100.04,2533248.03
|
||||
1401786035,10579681.12,4386844.66,37117.73,2541278.85
|
||||
1401882943,11243814.21,3713601.99,71089.90,2059566.22
|
||||
1401882981,11134212.84,4231286.57,53092.46,2180900.77
|
||||
1401882993,10950888.02,4182279.39,53092.46,2164511.15
|
||||
1401883005,11123999.47,3725357.71,76260.07,2211605.54
|
||||
1401883017,10773686.78,3988825.00,45590.26,2256792.94
|
||||
1401883041,10991057.38,4192016.84,55924.05,2164285.17
|
||||
1401883053,11253995.23,4111639.22,45100.04,2096238.88
|
||||
1401883388,11153576.69,4347093.92,76260.07,2205268.76
|
||||
1401883411,11501180.01,4506130.97,58254.22,2243712.66
|
||||
1401883422,11156015.26,4540579.82,47662.55,2279418.81
|
||||
1401883445,11195098.22,4455873.90,38479.85,2267750.98
|
||||
1401883468,11467197.28,4422749.94,72315.59,2289153.70
|
||||
1401883480,11087024.10,4312175.00,55188.21,2296830.48
|
||||
1401925287,11654811.40,4536210.26,72315.59,2379382.00
|
||||
1401925299,11573243.70,3894464.26,59074.70,2276455.83
|
||||
1401925310,11944273.21,4666625.62,62601.55,2304322.33
|
||||
1401925322,11775622.43,3945455.94,43690.67,2149656.21
|
||||
1401925333,11539429.12,4630751.73,43240.25,2270121.49
|
||||
1401925344,11312437.08,4589657.39,62601.55,2329731.93
|
||||
1401926595,11633680.46,4701904.00,55924.05,2410241.02
|
||||
1401926639,11630837.25,3933338.77,55924.05,2346259.71
|
||||
1401926649,11822018.52,4686898.26,76260.07,2419184.30
|
||||
1401926661,11800543.07,4196806.70,71089.90,2295282.55
|
||||
1401926673,10529846.71,3931058.57,49932.19,2165044.18
|
||||
1401926686,10117457.56,3362173.28,55924.05,1994411.93
|
||||
1401926711,11029110.99,4353057.39,49932.19,2239772.42
|
||||
1401926725,10862072.89,2750694.08,32263.88,2104475.43
|
||||
1402402553,11294194.69,4189904.32,49932.19,2123185.29
|
||||
1402402565,11164650.78,4006360.04,55924.05,2049352.49
|
||||
1402402578,9943455.54,3781047.14,45590.26,2166878.84
|
||||
1447151242,13847614.65,5481111.96,66576.25,2528296.71
|
||||
1447151252,13478094.70,5762300.49,99864.38,2467814.73
|
||||
1447151261,13318275.24,5669995.06,91180.52,2528263.08
|
||||
1447151271,13328641.49,5792107.41,62601.55,2534213.23
|
||||
1447151281,13603554.01,5690401.12,52428.80,2533696.10
|
||||
1447151291,13893573.36,5447950.39,66576.25,2532335.35
|
||||
1447151303,13091255.94,3616732.34,62601.55,2227987.21
|
||||
1447151314,11690373.06,4497110.65,91180.52,2136426.50
|
||||
1447151325,13580596.98,4134246.08,91180.52,2373758.39
|
||||
1447151335,13974417.41,5615391.00,59074.70,2507497.84
|
||||
1447152253,13474619.18,4825849.40,49932.19,2424468.13
|
||||
1447152263,12626952.28,5517283.96,49932.19,2459678.52
|
||||
1447152273,13712806.08,5401932.71,91180.52,2531766.93
|
||||
1447152286,13674890.28,5105976.43,91180.52,1637699.37
|
||||
1447152296,12598276.86,5621295.29,91180.52,2265631.05
|
||||
1447152307,13427458.88,5257266.07,24966.10,2309148.88
|
||||
1447152317,13658739.92,5880717.85,62601.55,2403685.89
|
||||
1447152327,13697028.28,5704775.21,91180.52,2397147.59
|
||||
1447152337,13854967.00,5598234.96,43690.67,2516441.18
|
||||
1447152348,13446160.91,4623962.27,91180.52,2349433.72
|
||||
1447154320,13805978.08,5843317.38,66576.25,2529583.04
|
||||
1447154330,13794992.46,5866609.62,89240.51,2479643.35
|
||||
1447154340,12999574.36,5870781.62,83886.08,2565547.20
|
||||
1447154349,13813759.40,5896996.47,59074.70,2577608.73
|
||||
1447154359,13646711.36,5716116.84,47662.55,2574703.24
|
||||
1447154368,13664364.46,5873110.25,41527.76,2502096.76
|
||||
1447154378,13345158.07,5929994.94,66576.25,2526050.75
|
||||
1447154388,13619724.42,5891895.35,66576.25,2542931.44
|
||||
1447154397,13274559.00,5736176.87,66576.25,2538733.05
|
||||
1447154407,13445232.76,5381001.80,89240.51,2576233.33
|
||||
1447155008,13277391.26,5722360.39,71089.90,2475000.02
|
||||
1447155019,13581101.21,5484972.04,45100.04,2192286.64
|
||||
1447155029,13116025.99,5830570.40,52428.80,2522486.73
|
||||
1447155066,13645222.60,5724509.20,62601.55,2514659.99
|
||||
1447155349,11915902.67,5912124.62,58254.22,2421361.02
|
||||
1447155679,13951049.58,5897239.13,26379.27,2527577.97
|
||||
1447155699,13781189.15,5851252.25,62601.55,2539751.33
|
||||
1447156053,13415522.24,5930072.07,82241.25,2533834.67
|
||||
1447156073,13492327.24,5848589.68,52428.80,2567896.99
|
||||
1447156411,13229275.90,5858750.37,66576.25,2523350.73
|
||||
1447156432,13556025.90,5873947.56,62601.55,2487130.01
|
||||
1447156745,13744909.39,5913103.69,66576.25,2551782.92
|
||||
1447158285,11638128.71,5241775.30,71089.90,2321077.83
|
||||
1447158396,13539837.29,5874704.47,47662.55,2533571.93
|
||||
1447158415,14054879.53,5952300.47,41527.76,2571669.83
|
||||
1447210457,13616841.50,5604087.24,83886.08,2458628.97
|
||||
1447210807,14529897.99,5833087.33,77672.30,1845729.06
|
||||
1447210834,14016924.69,5806227.80,66576.25,1715107.19
|
||||
1447211104,14738120.40,5873312.56,58254.22,2092537.05
|
||||
1447211128,14875503.82,5649431.95,27776.85,2033045.40
|
||||
1447211244,15335902.86,6019829.26,77672.30,1842297.15
|
||||
1447211259,14365504.46,5812325.12,91180.52,1965977.09
|
||||
1447211278,15175749.51,5931324.37,99864.38,1905029.23
|
||||
1447211529,15442994.79,5909448.56,91180.52,1953744.42
|
||||
1447211564,15175229.72,6100062.87,58254.22,1918667.68
|
||||
1447211712,15957717.02,6145969.23,77672.30,1960098.15
|
||||
1447211732,15692151.82,5725138.47,62601.55,1711560.29
|
||||
1447211755,15758474.73,6033801.22,82241.25,1995758.04
|
||||
1447766311,15118132.03,6006294.55,31068.92,1946048.29
|
||||
1447829172,15903322.09,5728120.55,58254.22,1988443.21
|
||||
|
|
Can't render this file because it has a wrong number of fields in line 447.
|
54
cmake/Modules/FindCheck.cmake
Normal file
54
cmake/Modules/FindCheck.cmake
Normal file
|
@ -0,0 +1,54 @@
|
|||
# - Try to find the CHECK libraries
|
||||
# Once done this will define
|
||||
#
|
||||
# CHECK_FOUND - system has check
|
||||
# CHECK_INCLUDE_DIRS - the check include directory
|
||||
# CHECK_LIBRARIES - check library
|
||||
#
|
||||
# Copyright (c) 2007 Daniel Gollub <gollub@b1-systems.de>
|
||||
# Copyright (c) 2007-2009 Bjoern Ricks <bjoern.ricks@gmail.com>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
|
||||
INCLUDE( FindPkgConfig )
|
||||
|
||||
IF ( Check_FIND_REQUIRED )
|
||||
SET( _pkgconfig_REQUIRED "REQUIRED" )
|
||||
ELSE( Check_FIND_REQUIRED )
|
||||
SET( _pkgconfig_REQUIRED "" )
|
||||
ENDIF ( Check_FIND_REQUIRED )
|
||||
|
||||
IF ( CHECK_MIN_VERSION )
|
||||
PKG_SEARCH_MODULE( CHECK ${_pkgconfig_REQUIRED} check>=${CHECK_MIN_VERSION} )
|
||||
ELSE ( CHECK_MIN_VERSION )
|
||||
PKG_SEARCH_MODULE( CHECK ${_pkgconfig_REQUIRED} check )
|
||||
ENDIF ( CHECK_MIN_VERSION )
|
||||
|
||||
# Look for CHECK include dir and libraries
|
||||
IF( NOT CHECK_FOUND AND NOT PKG_CONFIG_FOUND )
|
||||
|
||||
FIND_PATH( CHECK_INCLUDE_DIRS check.h )
|
||||
|
||||
FIND_LIBRARY( CHECK_LIBRARIES NAMES check )
|
||||
|
||||
IF ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES )
|
||||
SET( CHECK_FOUND 1 )
|
||||
IF ( NOT Check_FIND_QUIETLY )
|
||||
MESSAGE ( STATUS "Found CHECK: ${CHECK_LIBRARIES}" )
|
||||
ENDIF ( NOT Check_FIND_QUIETLY )
|
||||
ELSE ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES )
|
||||
IF ( Check_FIND_REQUIRED )
|
||||
MESSAGE( FATAL_ERROR "Could NOT find CHECK" )
|
||||
ELSE ( Check_FIND_REQUIRED )
|
||||
IF ( NOT Check_FIND_QUIETLY )
|
||||
MESSAGE( STATUS "Could NOT find CHECK" )
|
||||
ENDIF ( NOT Check_FIND_QUIETLY )
|
||||
ENDIF ( Check_FIND_REQUIRED )
|
||||
ENDIF ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES )
|
||||
ENDIF( NOT CHECK_FOUND AND NOT PKG_CONFIG_FOUND )
|
||||
|
||||
# Hide advanced variables from CMake GUIs
|
||||
MARK_AS_ADVANCED( CHECK_INCLUDE_DIRS CHECK_LIBRARIES )
|
44
cmake/Modules/FindJemalloc.cmake
Normal file
44
cmake/Modules/FindJemalloc.cmake
Normal file
|
@ -0,0 +1,44 @@
|
|||
# - Try to find jemalloc headers and libraries.
|
||||
#
|
||||
# Usage of this module as follows:
|
||||
#
|
||||
# find_package(JeMalloc)
|
||||
#
|
||||
# Variables used by this module, they can change the default behaviour and need
|
||||
# to be set before calling find_package:
|
||||
#
|
||||
# JEMALLOC_ROOT_DIR Set this variable to the root installation of
|
||||
# jemalloc if the module has problems finding
|
||||
# the proper installation path.
|
||||
#
|
||||
# Variables defined by this module:
|
||||
#
|
||||
# JEMALLOC_FOUND System has jemalloc libs/headers
|
||||
# JEMALLOC_LIBRARIES The jemalloc library/libraries
|
||||
# JEMALLOC_INCLUDE_DIR The location of jemalloc headers
|
||||
|
||||
find_path(JEMALLOC_ROOT_DIR
|
||||
NAMES include/jemalloc/jemalloc.h
|
||||
)
|
||||
|
||||
find_library(JEMALLOC_LIBRARIES
|
||||
NAMES jemalloc
|
||||
HINTS ${JEMALLOC_ROOT_DIR}/lib
|
||||
)
|
||||
|
||||
find_path(JEMALLOC_INCLUDE_DIR
|
||||
NAMES jemalloc/jemalloc.h
|
||||
HINTS ${JEMALLOC_ROOT_DIR}/include
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(JeMalloc DEFAULT_MSG
|
||||
JEMALLOC_LIBRARIES
|
||||
JEMALLOC_INCLUDE_DIR
|
||||
)
|
||||
|
||||
mark_as_advanced(
|
||||
JEMALLOC_ROOT_DIR
|
||||
JEMALLOC_LIBRARIES
|
||||
JEMALLOC_INCLUDE_DIR
|
||||
)
|
37
cmake/Modules/FindJudy.cmake
Normal file
37
cmake/Modules/FindJudy.cmake
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Copyright (C) 2007-2009 LuaDist.
|
||||
# Created by Peter Kapec <kapecp@gmail.com>
|
||||
# Redistribution and use of this file is allowed according to the terms of the MIT license.
|
||||
# For details see the COPYRIGHT file distributed with LuaDist.
|
||||
# Note:
|
||||
# Searching headers and libraries is very simple and is NOT as powerful as scripts
|
||||
# distributed with CMake, because LuaDist defines directories to search for.
|
||||
# Everyone is encouraged to contact the author with improvements. Maybe this file
|
||||
# becomes part of CMake distribution sometimes.
|
||||
|
||||
# - Find judy
|
||||
# Find the native Judy headers and libraries.
|
||||
#
|
||||
# Judy_INCLUDE_DIRS - where to find judy.h, etc.
|
||||
# Judy_LIBRARIES - List of libraries when using judy.
|
||||
# Judy_FOUND - True if judy found.
|
||||
|
||||
# Look for the header file.
|
||||
FIND_PATH(Judy_INCLUDE_DIR NAMES Judy.h)
|
||||
|
||||
# Look for the library.
|
||||
FIND_LIBRARY(Judy_LIBRARY NAMES judy)
|
||||
|
||||
# Handle the QUIETLY and REQUIRED arguments and set Judy_FOUND to TRUE if all listed variables are TRUE.
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Judy DEFAULT_MSG Judy_LIBRARY Judy_INCLUDE_DIR)
|
||||
|
||||
# Copy the results to the output variables.
|
||||
IF(Judy_FOUND)
|
||||
SET(Judy_LIBRARIES ${Judy_LIBRARY})
|
||||
SET(Judy_INCLUDE_DIRS ${Judy_INCLUDE_DIR})
|
||||
ELSE(Judy_FOUND)
|
||||
SET(Judy_LIBRARIES)
|
||||
SET(Judy_INCLUDE_DIRS)
|
||||
ENDIF(Judy_FOUND)
|
||||
|
||||
MARK_AS_ADVANCED(Judy_INCLUDE_DIRS Judy_LIBRARIES)
|
37
cmake/Modules/FindPCRE2.cmake
Normal file
37
cmake/Modules/FindPCRE2.cmake
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Copyright (C) 2007-2009 LuaDist.
|
||||
# Created by Peter Kapec <kapecp@gmail.com>
|
||||
# Redistribution and use of this file is allowed according to the terms of the MIT license.
|
||||
# For details see the COPYRIGHT file distributed with LuaDist.
|
||||
# Note:
|
||||
# Searching headers and libraries is very simple and is NOT as powerful as scripts
|
||||
# distributed with CMake, because LuaDist defines directories to search for.
|
||||
# Everyone is encouraged to contact the author with improvements. Maybe this file
|
||||
# becomes part of CMake distribution sometimes.
|
||||
|
||||
# - Find pcre2
|
||||
# Find the native PCRE2 headers and libraries.
|
||||
#
|
||||
# PCRE2_INCLUDE_DIRS - where to find pcre2.h, etc.
|
||||
# PCRE2_LIBRARIES - List of libraries when using pcre2.
|
||||
# PCRE2_FOUND - True if pcre2 found.
|
||||
|
||||
# Look for the header file.
|
||||
FIND_PATH(PCRE2_INCLUDE_DIR NAMES pcre2.h)
|
||||
|
||||
# Look for the library.
|
||||
FIND_LIBRARY(PCRE2_LIBRARY NAMES pcre2-8)
|
||||
|
||||
# Handle the QUIETLY and REQUIRED arguments and set PCRE2_FOUND to TRUE if all listed variables are TRUE.
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE2 DEFAULT_MSG PCRE2_LIBRARY PCRE2_INCLUDE_DIR)
|
||||
|
||||
# Copy the results to the output variables.
|
||||
IF(PCRE2_FOUND)
|
||||
SET(PCRE2_LIBRARIES ${PCRE2_LIBRARY})
|
||||
SET(PCRE2_INCLUDE_DIRS ${PCRE2_INCLUDE_DIR})
|
||||
ELSE(PCRE2_FOUND)
|
||||
SET(PCRE2_LIBRARIES)
|
||||
SET(PCRE2_INCLUDE_DIRS)
|
||||
ENDIF(PCRE2_FOUND)
|
||||
|
||||
MARK_AS_ADVANCED(PCRE2_INCLUDE_DIRS PCRE2_LIBRARIES)
|
3
config.h.cmake
Normal file
3
config.h.cmake
Normal file
|
@ -0,0 +1,3 @@
|
|||
#cmakedefine HAVE_STRDUP @HAVE_STRDUP@
|
||||
#cmakedefine HAVE_STRNDUP @HAVE_STRNDUP@
|
||||
#cmakedefine HAVE_STDBOOL_H @HAVE_STDBOOL_H@
|
118
config.h.in
118
config.h.in
|
@ -1,118 +0,0 @@
|
|||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* "whether graphviz is enable" */
|
||||
#undef ENABLE_GRAPHVIZ
|
||||
|
||||
/* "whether statistics is enable" */
|
||||
#undef ENABLE_STATS
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#undef HAVE_DLFCN_H
|
||||
|
||||
/* Define to 1 if you have the `gettimeofday' function. */
|
||||
#undef HAVE_GETTIMEOFDAY
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <jemalloc/jemalloc.h> header file. */
|
||||
#undef HAVE_JEMALLOC_JEMALLOC_H
|
||||
|
||||
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
||||
to 0 otherwise. */
|
||||
#undef HAVE_MALLOC
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the `memset' function. */
|
||||
#undef HAVE_MEMSET
|
||||
|
||||
/* Define to 1 if your system has a GNU libc compatible `realloc' function,
|
||||
and to 0 otherwise. */
|
||||
#undef HAVE_REALLOC
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the `strchr' function. */
|
||||
#undef HAVE_STRCHR
|
||||
|
||||
/* Define to 1 if you have the `strdup' function. */
|
||||
#undef HAVE_STRDUP
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the `strndup' function. */
|
||||
#undef HAVE_STRNDUP
|
||||
|
||||
/* Define to 1 if you have the `strstr' function. */
|
||||
#undef HAVE_STRSTR
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#undef HAVE_SYS_TIME_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#undef LT_OBJDIR
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Define to 1 if you have the PATH_MAX macro. */
|
||||
#undef USE_JEMALLOC
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
#undef inline
|
||||
#endif
|
||||
|
||||
/* Define to rpl_malloc if the replacement function should be used. */
|
||||
#undef malloc
|
||||
|
||||
/* Define to rpl_realloc if the replacement function should be used. */
|
||||
#undef realloc
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
100
configure.ac
100
configure.ac
|
@ -1,39 +1,44 @@
|
|||
AC_INIT([r3], 1.0.0)
|
||||
AC_INIT([r3], 2.0.0)
|
||||
AC_PREREQ([2.64])
|
||||
AC_USE_SYSTEM_EXTENSIONS
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
AM_SILENT_RULES([yes])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
LT_INIT
|
||||
AC_PROG_CC
|
||||
AC_PROG_CC_STDC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_INSTALL
|
||||
AC_HEADER_STDBOOL
|
||||
|
||||
AC_CHECK_HEADERS([stdlib.h string.h sys/time.h stdint.h])
|
||||
# older debian
|
||||
AC_PROG_LIBTOOL
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
PKG_CHECK_MODULES(CHECK,[check >= 0.9.4],:,[
|
||||
ifdef([AM_PATH_CHECK],
|
||||
[AM_PATH_CHECK(,[have_check="yes"])],
|
||||
AC_MSG_WARN([Check not found; cannot run unit tests!])
|
||||
[have_check="no"])]
|
||||
])
|
||||
AM_CONDITIONAL(HAVE_CHECK, test x"$have_check" = "xyes")
|
||||
AC_CHECK_HEADERS([stdlib.h string.h sys/time.h])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_INLINE
|
||||
AC_TYPE_SIZE_T
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_MALLOC
|
||||
AC_FUNC_REALLOC
|
||||
AC_CHECK_FUNCS([gettimeofday memset strchr strdup strndup strstr])
|
||||
|
||||
|
||||
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
|
||||
|
||||
AC_ARG_ENABLE([gcov],
|
||||
[AS_HELP_STRING([--enable-gcov],
|
||||
[use Gcov to test the test suite])],
|
||||
[],
|
||||
[enable_gcov=no])
|
||||
AM_CONDITIONAL([COND_GCOV],[test '!' "$enable_gcov" = no])
|
||||
|
||||
AC_ARG_WITH([malloc], AS_HELP_STRING([--without-malloc], [Use the default malloc]))
|
||||
|
||||
|
||||
AC_ARG_WITH([malloc],
|
||||
AS_HELP_STRING([--without-malloc], [Use the default malloc]))
|
||||
|
||||
AS_IF([test "x$with_malloc" == "xjemalloc"],
|
||||
[AC_CHECK_HEADERS([jemalloc/jemalloc.h], [
|
||||
|
@ -63,38 +68,71 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <jemalloc/jemalloc.h>]],
|
|||
)
|
||||
fi
|
||||
AM_CONDITIONAL(USE_JEMALLOC, test "x$have_jemalloc" = "xyes")
|
||||
AM_CONDITIONAL(ENABLE_GRAPHVIZ, test "x$enable_graphviz" = "xyes")
|
||||
|
||||
AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable debug]), [],[enable_debug=unset])
|
||||
# AM_CONDITIONAL(USE_JEMALLOC, test "x$found_jemalloc" = "xyes")
|
||||
# AC_DEFINE(USE_JEMALLOC, test "x$found_jemalloc" = "xyes" , "use jemalloc")
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(DEPS, [libpcre2-8])
|
||||
AC_SUBST(DEPS_CFLAGS)
|
||||
AC_SUBST(DEPS_LIBS)
|
||||
|
||||
|
||||
AC_ARG_ENABLE(debug,AS_HELP_STRING([--enable-debug],[enable debug]))
|
||||
if test "x$enable_debug" = "xyes"; then
|
||||
AC_DEFINE(DEBUG, 1, "debug")
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_DEBUG, test "x$enable_debug" = "xyes")
|
||||
|
||||
|
||||
AC_ARG_ENABLE(graphviz,
|
||||
AS_HELP_STRING([--enable-graphviz],
|
||||
[enable graphviz support]),
|
||||
, enable_graphviz=unset)
|
||||
if test "x$enable_graphviz" != "xunset" ; then
|
||||
|
||||
AC_ARG_ENABLE(graphviz, AS_HELP_STRING([--enable-graphviz],[enable graphviz support]))
|
||||
if test "x$enable_graphviz" = "xyes" ; then
|
||||
PKG_CHECK_MODULES(GVC_DEPS, [libgvc])
|
||||
AC_SUBST(GVC_DEPS_CFLAGS)
|
||||
AC_SUBST(GVC_DEPS_LIBS)
|
||||
AC_DEFINE(ENABLE_GRAPHVIZ, 1, "whether graphviz is enable")
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_GRAPHVIZ, test "x$enable_graphviz" = "xyes")
|
||||
AC_DEFINE(ENABLE_GRAPHVIZ, test "x$enable_graphviz" = "xyes", "whether graphviz is enable")
|
||||
|
||||
|
||||
AC_ARG_ENABLE(stats, AS_HELP_STRING([--enable-stats], [enable statistics]), [] ,[enable_stats=unset])
|
||||
AC_DEFINE(ENABLE_STATS, test "x$enable_stats" = "xyes", "whether statistics is enable")
|
||||
|
||||
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(DEPS, [libpcre])
|
||||
AC_SUBST(DEPS_CFLAGS)
|
||||
AC_SUBST(DEPS_LIBS)
|
||||
AC_ARG_ENABLE(json, AS_HELP_STRING([--enable-json],[enable json encoder]))
|
||||
if test "x$enable_json" = "xyes"; then
|
||||
PKG_CHECK_MODULES(JSONC, [json-c])
|
||||
AC_SUBST(JSONC_CFLAGS)
|
||||
AC_SUBST(JSONC_LIBS)
|
||||
AC_DEFINE(ENABLE_JSON, 1, [enable json])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_JSON, test "x$enable_json" = "xyes")
|
||||
|
||||
|
||||
# This does not work because configure does not look into /opt/local/include...
|
||||
# AC_CHECK_HEADERS([check.h],[ enable_check=yes ],[ enable_check=unset ])
|
||||
|
||||
|
||||
AC_ARG_ENABLE(check,
|
||||
AS_HELP_STRING([--enable-check],
|
||||
[enable unit testing]),
|
||||
, enable_check=unset)
|
||||
|
||||
if test "x$enable_check" != "xunset" ; then
|
||||
PKG_CHECK_MODULES(CHECK,[check >= 0.9.4],:,[
|
||||
ifdef([AM_PATH_CHECK],
|
||||
[AM_PATH_CHECK(,[have_check="yes"])],
|
||||
AC_MSG_WARN([Check not found; cannot run unit tests!])
|
||||
[have_check="no"]
|
||||
)]
|
||||
])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_CHECK, test "x$enable_check" = "xyes")
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
r3.pc
|
||||
Makefile
|
||||
src/Makefile
|
||||
tests/Makefile
|
||||
examples/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
|
5
dist-debian/changelog
Normal file
5
dist-debian/changelog
Normal file
|
@ -0,0 +1,5 @@
|
|||
libr3 (1.3.1-1) unstable; urgency=low
|
||||
|
||||
* Initial release using libr3 1.3.1.
|
||||
|
||||
-- Ronmi Ren <ronmi.ren@gmail.com> Thu, 12 Jun 2014 10:54:16 +0800
|
1
dist-debian/compat
Normal file
1
dist-debian/compat
Normal file
|
@ -0,0 +1 @@
|
|||
9
|
28
dist-debian/control
Normal file
28
dist-debian/control
Normal file
|
@ -0,0 +1,28 @@
|
|||
Source: libr3
|
||||
Priority: optional
|
||||
Maintainer: Ronmi Ren <ronmi.ren@gmail.com>
|
||||
Build-Depends: debhelper (>= 8.0.0), automake, autotools-dev, autoconf,
|
||||
libtool, libpcre2-dev, pkg-config, check
|
||||
Standards-Version: 3.9.4
|
||||
Section: libs
|
||||
Homepage: https://github.com/c9s/r3
|
||||
|
||||
Package: libr3-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Depends: libr3 (= ${binary:Version})
|
||||
Description: Development files for libr3
|
||||
libr3 (https://github.com/c9s/r3) is an URL router library with high
|
||||
performance, thus, it's implemented in C. It compiles your R3Route paths into
|
||||
a prefix trie.
|
||||
.
|
||||
This package contains header files for libr3.
|
||||
|
||||
Package: libr3
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: High performance URL routing library written in C.
|
||||
libr3 (https://github.com/c9s/r3) is an URL router library with high
|
||||
performance, thus, it's implemented in C. It compiles your R3Route paths into
|
||||
a prefix trie.
|
30
dist-debian/copyright
Normal file
30
dist-debian/copyright
Normal file
|
@ -0,0 +1,30 @@
|
|||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: libr3
|
||||
Source: https://github.com/c9s/r3
|
||||
|
||||
Files: *
|
||||
Copyright: 2014 yoanlin93@gmail.com
|
||||
License: MIT
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2014 Ronmi Ren <ronmi.ren@gmail.com>
|
||||
License: MIT
|
||||
|
||||
License: MIT
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1
dist-debian/docs
Normal file
1
dist-debian/docs
Normal file
|
@ -0,0 +1 @@
|
|||
README.md
|
2
dist-debian/libr3-dev.dirs
Normal file
2
dist-debian/libr3-dev.dirs
Normal file
|
@ -0,0 +1,2 @@
|
|||
usr/lib
|
||||
usr/include
|
4
dist-debian/libr3-dev.install
Normal file
4
dist-debian/libr3-dev.install
Normal file
|
@ -0,0 +1,4 @@
|
|||
usr/include/*
|
||||
usr/lib/lib*.a
|
||||
usr/lib/lib*.so
|
||||
usr/lib/pkgconfig/*
|
1
dist-debian/libr3.dirs
Normal file
1
dist-debian/libr3.dirs
Normal file
|
@ -0,0 +1 @@
|
|||
usr/lib
|
1
dist-debian/libr3.install
Normal file
1
dist-debian/libr3.install
Normal file
|
@ -0,0 +1 @@
|
|||
usr/lib/lib*.so.*
|
12
dist-debian/rules
Executable file
12
dist-debian/rules
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/make -f
|
||||
# -*- makefile -*-
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
%:
|
||||
dh $@ --with autotools-dev
|
||||
|
||||
override_dh_auto_configure:
|
||||
./autogen.sh
|
||||
./configure --enable-check --prefix=/usr
|
1
dist-debian/source/format
Normal file
1
dist-debian/source/format
Normal file
|
@ -0,0 +1 @@
|
|||
3.0 (quilt)
|
7
examples/Makefile.am
Normal file
7
examples/Makefile.am
Normal file
|
@ -0,0 +1,7 @@
|
|||
AM_CFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) -I$(top_builddir)/include -Wall -std=c99
|
||||
AM_CXXFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) -I$(top_builddir)/include -Wall
|
||||
AM_LDFLAGS=$(DEPS_LIBS) $(GVC_DEPS_LIBS) $(top_builddir)/libr3.la
|
||||
|
||||
noinst_PROGRAMS = simple simple_cpp
|
||||
simple_SOURCES = simple.c
|
||||
simple_cpp_SOURCES = simple_cpp.cpp
|
95
examples/routing.c
Normal file
95
examples/routing.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* check_slug.c
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "../include/r3.h"
|
||||
|
||||
|
||||
|
||||
|
||||
void test1(void) {
|
||||
R3Node *n = r3_tree_create(10);
|
||||
|
||||
int route_data1 = 3;
|
||||
int route_data2 = 44;
|
||||
int route_data3 = 555;
|
||||
|
||||
// insert the R3Route path into the router tree
|
||||
r3_tree_insert_routel(n, METHOD_GET | METHOD_POST, "/blog", sizeof("/blog") - 1, &route_data1 );
|
||||
r3_tree_insert_routel(n, METHOD_GET | METHOD_POST, "/blog/{idl:\\d+}/asf/{id}", strlen("/blog/{idl:\\d+}/asf/{id}"), &route_data2 );
|
||||
r3_tree_insert_routel(n, METHOD_GET | METHOD_POST, "/blog3/{idl:\\d{3}}/asd/{id:[0-9]+}/qwe", sizeof("/blog3/{idl:\\d{3}}/asd/{id:[0-9]+}/qwe") - 1, &route_data3 );
|
||||
|
||||
char *errstr = NULL;
|
||||
int err = r3_tree_compile(n, &errstr);
|
||||
if (err != 0) {
|
||||
// fail
|
||||
printf("error: %s\n", errstr);
|
||||
free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
|
||||
}
|
||||
// r3_tree_dump(n,0);
|
||||
|
||||
|
||||
// in your http server handler
|
||||
|
||||
// create the match entry for capturing dynamic variables.
|
||||
match_entry * entry;
|
||||
R3Route *matched_route;
|
||||
int i;
|
||||
for (int k = 0; k < 3000000; k++) {
|
||||
// printf("round N%d\n",k);
|
||||
entry = match_entry_create("/blog/432/asf/678");
|
||||
entry->request_method = METHOD_GET;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
// if (matched_route) {
|
||||
// printf("Routed data is: %d\n", *(int*)matched_route->data); // get the data from matched route
|
||||
// if (entry->vars.tokens.size == entry->vars.slugs.size) {
|
||||
// for (i = 0; i < entry->vars.tokens.size; i++) {
|
||||
// // entry->vars.slugs.entries[i];
|
||||
// // entry->vars.tokens.entries[i];
|
||||
// printf("Slug name is: %*.*s\n",entry->vars.slugs.entries[i].len,
|
||||
// entry->vars.slugs.entries[i].len, entry->vars.slugs.entries[i].base);
|
||||
// printf("Slug value is: %*.*s\n",entry->vars.tokens.entries[i].len,
|
||||
// entry->vars.tokens.entries[i].len, entry->vars.tokens.entries[i].base);
|
||||
// }
|
||||
// } else {
|
||||
// // printf("Slugs and tokens sizes are not equal\n");
|
||||
// // for (i = 0; i < entry->vars.slugs.size; i++) {
|
||||
// // printf("Slug name is: %*.*s\n",entry->vars.slugs.entries[i].len,
|
||||
// // entry->vars.slugs.entries[i].len, entry->vars.slugs.entries[i].base);
|
||||
// // }
|
||||
// // for (i = 0; i < entry->vars.tokens.size; i++) {
|
||||
// // printf("Slug value is: %*.*s\n",entry->vars.tokens.entries[i].len,
|
||||
// // entry->vars.tokens.entries[i].len, entry->vars.tokens.entries[i].base);
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
// free the objects at the end
|
||||
match_entry_free(entry);
|
||||
}
|
||||
// entry = match_entry_create("/blog/aaa/asd/123/qwe");
|
||||
// if (entry != NULL) {
|
||||
// entry->request_method = METHOD_GET;
|
||||
// matched_route = r3_tree_match_route(n, entry);
|
||||
// if (matched_route != NULL) {
|
||||
// // printf("Routed data is: %d\n", *(int*)matched_route->data); // get the data from matched route
|
||||
// for (int i = 0; i < entry->vars->len; i++) {
|
||||
// // entry->vars->slugs[i];
|
||||
// // entry->vars->tokens[i];
|
||||
// printf("Slug name is: %s\n",entry->vars->slugs[i]);
|
||||
// printf("Slug value is: %s\n",entry->vars->tokens[i]);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // free the objects at the end
|
||||
// match_entry_free(entry);
|
||||
|
||||
r3_tree_free(n);
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
test1();
|
||||
}
|
59
examples/simple.c
Normal file
59
examples/simple.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* bench.c
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "r3.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
R3Node * n = r3_tree_create(3);
|
||||
|
||||
r3_tree_insert_path(n, "/foo/bar/baz", NULL);
|
||||
r3_tree_insert_path(n, "/foo/bar/qux", NULL);
|
||||
r3_tree_insert_path(n, "/foo/bar/quux", NULL);
|
||||
r3_tree_insert_path(n, "/bar/foo/baz", NULL);
|
||||
r3_tree_insert_path(n, "/bar/foo/quux", NULL);
|
||||
r3_tree_insert_path(n, "/bar/garply/grault", NULL);
|
||||
r3_tree_insert_path(n, "/baz/foo/bar", NULL);
|
||||
r3_tree_insert_path(n, "/baz/foo/qux", NULL);
|
||||
r3_tree_insert_path(n, "/baz/foo/quux", NULL);
|
||||
r3_tree_insert_path(n, "/qux/foo/quux", NULL);
|
||||
r3_tree_insert_path(n, "/qux/foo/corge", NULL);
|
||||
r3_tree_insert_path(n, "/qux/foo/grault", NULL);
|
||||
r3_tree_insert_path(n, "/corge/quux/foo", NULL);
|
||||
r3_tree_insert_path(n, "/corge/quux/bar", NULL);
|
||||
r3_tree_insert_path(n, "/corge/quux/baz", NULL);
|
||||
r3_tree_insert_path(n, "/corge/quux/qux", NULL);
|
||||
r3_tree_insert_path(n, "/corge/quux/grault", NULL);
|
||||
r3_tree_insert_path(n, "/grault/foo/bar", NULL);
|
||||
r3_tree_insert_path(n, "/grault/foo/baz", NULL);
|
||||
r3_tree_insert_path(n, "/garply/baz/quux", NULL);
|
||||
r3_tree_insert_path(n, "/garply/baz/corge", NULL);
|
||||
r3_tree_insert_path(n, "/garply/baz/grault", NULL);
|
||||
r3_tree_insert_path(n, "/garply/qux/foo", NULL);
|
||||
|
||||
char *errstr = NULL;
|
||||
int err = r3_tree_compile(n, &errstr);
|
||||
if(err) {
|
||||
printf("%s\n",errstr);
|
||||
free(errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
R3Node *m;
|
||||
|
||||
m = r3_tree_match(n , "/qux/bar/corge", NULL);
|
||||
|
||||
match_entry * e = match_entry_createl("/garply/baz/grault", strlen("/garply/baz/grault") );
|
||||
m = r3_tree_match_entry(n , e);
|
||||
if (m) {
|
||||
printf("Matched! %s\n", e->path.base);
|
||||
}
|
||||
match_entry_free(e);
|
||||
r3_tree_free(n);
|
||||
return 0;
|
||||
}
|
97
examples/simple_cpp.cpp
Normal file
97
examples/simple_cpp.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <r3.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void example_1() {
|
||||
// create a router tree with 10 children capacity (this capacity can grow dynamically)
|
||||
r3::Tree tree(10);
|
||||
|
||||
// insert the R3Route path into the router tree
|
||||
int route_data_1 = 1;
|
||||
tree.insert_path("/bar", &route_data_1); // ignore the length of path
|
||||
|
||||
int route_data_2 = 2;
|
||||
tree.insert_pathl("/zoo", strlen("/zoo"), &route_data_2);
|
||||
int route_data_3 = 3;
|
||||
tree.insert_pathl("/foo/bar", strlen("/foo/bar"), &route_data_3);
|
||||
|
||||
int route_data_4 = 4;
|
||||
tree.insert_pathl("/post/{id}", strlen("/post/{id}") , &route_data_4);
|
||||
|
||||
int route_data_5 = 5;
|
||||
tree.insert_pathl("/user/{id:\\d+}", strlen("/user/{id:\\d+}"),
|
||||
&route_data_5);
|
||||
|
||||
// if you want to catch error, you may call the extended path function for insertion
|
||||
int data = 10;
|
||||
char* errstr;
|
||||
r3::Node ret = tree.insert_pathl("/foo/{name:\\d{5}",
|
||||
strlen("/foo/{name:\\d{5}"), &data, &errstr);
|
||||
if (ret == NULL) {
|
||||
// failed insertion
|
||||
cout << "error: " << errstr << endl;
|
||||
free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
|
||||
}
|
||||
|
||||
// let's compile the tree!
|
||||
int err = tree.compile(&errstr);
|
||||
if (err != 0) {
|
||||
cout << "error: " << errstr << endl;
|
||||
free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
|
||||
}
|
||||
|
||||
// dump the compiled tree
|
||||
tree.dump(0);
|
||||
|
||||
// match a route
|
||||
r3::Node matched_node = tree.matchl("/foo/bar", strlen("/foo/bar"));
|
||||
if (matched_node) {
|
||||
int ret = *static_cast<int*>(matched_node.data());
|
||||
cout << "match path ret: " << ret << endl;
|
||||
}
|
||||
|
||||
r3::MatchEntry entry("/foo/bar");
|
||||
matched_node = tree.match_entry(entry);
|
||||
if (matched_node) {
|
||||
int ret = *static_cast<int*>(matched_node.data());
|
||||
cout << "match entry ret: " << ret << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void example_2() {
|
||||
// create a router tree with 10 children capacity (this capacity can grow dynamically)
|
||||
r3::Tree tree(10);
|
||||
|
||||
// insert the R3Route path into the router tree
|
||||
int route_data = 1;
|
||||
tree.insert_routel(METHOD_GET | METHOD_POST, "/blog/post",
|
||||
sizeof("/blog/post") - 1, &route_data);
|
||||
|
||||
char* errstr;
|
||||
int err = tree.compile(&errstr);
|
||||
if (err != 0) {
|
||||
cout << "errstr: " << errstr << endl;
|
||||
free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
|
||||
}
|
||||
|
||||
// in your http server handler
|
||||
|
||||
// create the match entry for capturing dynamic variables.
|
||||
r3::MatchEntry entry("/blog/post");
|
||||
entry.set_request_method(METHOD_GET);
|
||||
|
||||
r3::Route matched_route = tree.match_route(entry);
|
||||
if (matched_route) {
|
||||
int ret = *static_cast<int*>(matched_route.data());
|
||||
cout << "match route ret: " << ret << endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
example_1();
|
||||
example_2();
|
||||
return 0;
|
||||
}
|
76
gen_route_tests.rb
Normal file
76
gen_route_tests.rb
Normal file
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
puts <<END
|
||||
|
||||
/** DO NOT MODIFY THIS FILE, THIS TEST FILE IS AUTO-GENERATED. **/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "r3.h"
|
||||
#include "r3_slug.h"
|
||||
#include "zmalloc.h"
|
||||
|
||||
START_TEST (test_routes)
|
||||
{
|
||||
node * n = r3_tree_create(10);
|
||||
node * m = NULL;
|
||||
|
||||
END
|
||||
|
||||
|
||||
arr = ["foo", "bar", "baz", "qux", "quux", "corge", "grault", "garply"]
|
||||
paths = arr.permutation(3).map { |a| "/#{a.join '/'}" }
|
||||
paths.each_index do |idx|
|
||||
path = paths.fetch(idx)
|
||||
puts " char *data#{idx} = \"#{path}\";"
|
||||
puts " r3_tree_insert_path(n, \"#{path}\", (void*) data#{idx});"
|
||||
end
|
||||
|
||||
|
||||
puts <<END
|
||||
char *err = NULL;
|
||||
r3_tree_compile(n, &err);
|
||||
ck_assert(err == NULL);
|
||||
END
|
||||
|
||||
paths.each_index do |idx|
|
||||
path = paths.fetch(idx)
|
||||
puts " m = r3_tree_match(n, \"#{path}\", NULL);"
|
||||
puts " ck_assert(m != NULL);"
|
||||
puts " ck_assert(m->data == data#{idx});"
|
||||
puts " ck_assert(m->endpoint > 0);"
|
||||
end
|
||||
|
||||
|
||||
|
||||
puts <<END
|
||||
|
||||
r3_tree_free(n);
|
||||
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
|
||||
Suite* r3_suite (void) {
|
||||
Suite *suite = suite_create("r3 routes tests");
|
||||
TCase *tcase = tcase_create("testcase");
|
||||
tcase_add_test(tcase, test_routes);
|
||||
suite_add_tcase(suite, tcase);
|
||||
return suite;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
int number_failed;
|
||||
Suite *suite = r3_suite();
|
||||
SRunner *runner = srunner_create(suite);
|
||||
srunner_run_all(runner, CK_NORMAL);
|
||||
number_failed = srunner_ntests_failed(runner);
|
||||
srunner_free(runner);
|
||||
return number_failed;
|
||||
}
|
||||
|
||||
END
|
141
include/memory.h
Normal file
141
include/memory.h
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (c) 2014,2015 DeNA Co., Ltd., Kazuho Oku, Justin Zhu
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef r3__memory_h
|
||||
#define r3__memory_h
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define R3_GNUC_VERSION ((__GNUC__ << 16) | (__GNUC_MINOR__ << 8) | __GNUC_PATCHLEVEL__)
|
||||
#else
|
||||
#define R3_GNUC_VERSION 0
|
||||
#endif
|
||||
|
||||
#if __STDC_VERSION__ >= 201112L
|
||||
#define R3_NORETURN _Noreturn
|
||||
#elif defined(__clang__) || defined(__GNUC__) && R3_GNUC_VERSION >= 0x20500
|
||||
// noreturn was not defined before gcc 2.5
|
||||
#define R3_NORETURN __attribute__((noreturn))
|
||||
#else
|
||||
#define R3_NORETURN
|
||||
#endif
|
||||
|
||||
#if !defined(__clang__) && defined(__GNUC__) && R3_GNUC_VERSION >= 0x40900
|
||||
// returns_nonnull was seemingly not defined before gcc 4.9 (exists in 4.9.1 but not in 4.8.2)
|
||||
#define R3_RETURNS_NONNULL __attribute__((returns_nonnull))
|
||||
#else
|
||||
#define R3_RETURNS_NONNULL
|
||||
#endif
|
||||
|
||||
/**
|
||||
* buffer structure compatible with iovec
|
||||
*/
|
||||
typedef struct st_r3_iovec_t {
|
||||
const char *base;
|
||||
unsigned int len;
|
||||
} r3_iovec_t;
|
||||
|
||||
#define R3_VECTOR(type) \
|
||||
struct { \
|
||||
type *entries; \
|
||||
unsigned int size; \
|
||||
unsigned int capacity; \
|
||||
}
|
||||
|
||||
typedef R3_VECTOR(void) r3_vector_t;
|
||||
|
||||
/**
|
||||
* prints an error message and aborts
|
||||
*/
|
||||
R3_NORETURN void r3_fatal(const char *msg);
|
||||
|
||||
/**
|
||||
* constructor for r3_iovec_t
|
||||
*/
|
||||
static r3_iovec_t r3_iovec_init(const void *base, unsigned int len);
|
||||
|
||||
/**
|
||||
* wrapper of malloc; allocates given size of memory or dies if impossible
|
||||
*/
|
||||
R3_RETURNS_NONNULL static void *r3_mem_alloc(unsigned int sz);
|
||||
|
||||
/**
|
||||
* wrapper of realloc; reallocs the given chunk or dies if impossible
|
||||
*/
|
||||
static void *r3_mem_realloc(void *oldp, unsigned int sz);
|
||||
|
||||
/**
|
||||
* grows the vector so that it could store at least new_capacity elements of given size (or dies if impossible).
|
||||
* @param vector the vector
|
||||
* @param element_size size of the elements stored in the vector
|
||||
* @param new_capacity the capacity of the buffer after the function returns
|
||||
*/
|
||||
#define r3_vector_reserve(vector, new_capacity) \
|
||||
r3_vector__reserve((r3_vector_t *)(void *)(vector), sizeof((vector)->entries[0]), (new_capacity))
|
||||
static void r3_vector__reserve(r3_vector_t *vector, unsigned int element_size, unsigned int new_capacity);
|
||||
void r3_vector__expand(r3_vector_t *vector, unsigned int element_size, unsigned int new_capacity);
|
||||
|
||||
/* inline defs */
|
||||
|
||||
inline r3_iovec_t r3_iovec_init(const void *base, unsigned int len)
|
||||
{
|
||||
/* intentionally declared to take a "const void*" since it may contain any type of data and since _some_ buffers are constant */
|
||||
r3_iovec_t buf;
|
||||
buf.base = (char *)base;
|
||||
buf.len = len;
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline void *r3_mem_alloc(unsigned int sz)
|
||||
{
|
||||
void *p = malloc(sz);
|
||||
if (p == NULL)
|
||||
r3_fatal("no memory");
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void *r3_mem_realloc(void *oldp, unsigned int sz)
|
||||
{
|
||||
void *newp = realloc(oldp, sz);
|
||||
if (newp == NULL) {
|
||||
r3_fatal("no memory");
|
||||
return oldp;
|
||||
}
|
||||
return newp;
|
||||
}
|
||||
|
||||
inline void r3_vector__reserve(r3_vector_t *vector, unsigned int element_size, unsigned int new_capacity)
|
||||
{
|
||||
if (vector->capacity < new_capacity) {
|
||||
r3_vector__expand(vector, element_size, new_capacity);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
254
include/r3.h
254
include/r3.h
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* r3.h
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
@ -10,183 +10,180 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <pcre.h>
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#include <pcre2.h>
|
||||
|
||||
#include "r3_define.h"
|
||||
#include "str_array.h"
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#else
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#if __STDC_VERSION__ <= 201710L
|
||||
#ifdef HAVE_STDBOOL_H
|
||||
# include <stdbool.h>
|
||||
#elif !defined(bool) && !defined(__cplusplus)
|
||||
typedef unsigned char bool;
|
||||
# define bool bool /* For redefinition guards */
|
||||
# define false 0
|
||||
# define true 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define node_edge_pattern(node,i) node->edges[i]->pattern
|
||||
#define node_edge_pattern_len(node,i) node->edges[i]->pattern_len
|
||||
#include "str_array.h"
|
||||
#include "r3_slug.h"
|
||||
#include "memory.h"
|
||||
|
||||
|
||||
struct _root;
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct _edge;
|
||||
struct _node;
|
||||
struct _route;
|
||||
typedef struct _edge edge;
|
||||
typedef struct _node node;
|
||||
typedef struct _route route;
|
||||
typedef struct _root root;
|
||||
|
||||
struct _root {
|
||||
|
||||
};
|
||||
typedef struct _edge R3Edge;
|
||||
typedef struct _node R3Node;
|
||||
typedef struct _R3Route R3Route;
|
||||
|
||||
struct _node {
|
||||
edge ** edges;
|
||||
route ** routes;
|
||||
edge * parent_edge;
|
||||
uint32_t edge_len;
|
||||
uint32_t edge_cap;
|
||||
uint32_t route_len;
|
||||
uint32_t route_cap;
|
||||
|
||||
/** compile-time variables here.... **/
|
||||
|
||||
/* the combined regexp pattern string from pattern_tokens */
|
||||
int compare_type;
|
||||
R3_VECTOR(R3Edge) edges;
|
||||
R3_VECTOR(R3Route) routes;
|
||||
char * combined_pattern;
|
||||
pcre * pcre_pattern;
|
||||
pcre_extra * pcre_extra;
|
||||
pcre2_code * pcre_pattern;
|
||||
pcre2_match_data * match_data;
|
||||
|
||||
/**
|
||||
* the pointer of route data
|
||||
*/
|
||||
// edges are mostly less than 255
|
||||
unsigned int compare_type; // compare_type: pcre, opcode, string
|
||||
unsigned int endpoint; // endpoint, should be zero for non-endpoint nodes
|
||||
|
||||
// the pointer of R3Route data
|
||||
void * data;
|
||||
|
||||
uint8_t endpoint;
|
||||
};
|
||||
|
||||
#define r3_node_edge_pattern(node,i) node->edges.entries[i].pattern.base
|
||||
#define r3_node_edge_pattern_len(node,i) node->edges.entries[i].pattern.len
|
||||
|
||||
struct _edge {
|
||||
/* the child node */
|
||||
node * child;
|
||||
/* the parent node */
|
||||
node * parent;
|
||||
|
||||
char * pattern;
|
||||
int pattern_len;
|
||||
int opcode;
|
||||
float score;
|
||||
bool has_slug:1;
|
||||
r3_iovec_t pattern; // 8 bytes
|
||||
R3Node * child; // 8 bytes
|
||||
unsigned int opcode; // 4byte
|
||||
unsigned int has_slug; // 4byte
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
str_array * vars;
|
||||
char * path; // current path to dispatch
|
||||
int path_len; // the length of the current path
|
||||
int request_method; // current request method
|
||||
|
||||
void * data; // route ptr
|
||||
|
||||
char * host; // the request host
|
||||
int host_len;
|
||||
|
||||
char * remote_addr;
|
||||
int remote_addr_len;
|
||||
} match_entry;
|
||||
|
||||
struct _route {
|
||||
char * path;
|
||||
int path_len;
|
||||
|
||||
struct _R3Route {
|
||||
r3_iovec_t path;
|
||||
R3_VECTOR(r3_iovec_t) slugs;
|
||||
int request_method; // can be (GET || POST)
|
||||
|
||||
char * host; // required host name
|
||||
int host_len;
|
||||
r3_iovec_t host; // required host name
|
||||
|
||||
void * data;
|
||||
|
||||
char * remote_addr_pattern;
|
||||
int remote_addr_pattern_len;
|
||||
r3_iovec_t remote_addr_pattern;
|
||||
|
||||
unsigned int remote_addr_v4;
|
||||
int remote_addr_v4_bits;
|
||||
|
||||
unsigned int remote_addr_v6[4];
|
||||
int remote_addr_v6_bits[4];
|
||||
|
||||
int http_scheme; // can be (SCHEME_HTTP or SCHEME_HTTPS)
|
||||
|
||||
};
|
||||
|
||||
typedef struct _R3Entry match_entry;
|
||||
struct _R3Entry {
|
||||
str_array vars;
|
||||
r3_iovec_t path; // current path to dispatch
|
||||
int request_method; // current request method
|
||||
|
||||
void * data; // R3Route ptr
|
||||
|
||||
r3_iovec_t host; // the request host
|
||||
r3_iovec_t remote_addr;
|
||||
|
||||
int http_scheme;
|
||||
};
|
||||
|
||||
|
||||
node * r3_tree_create(int cap);
|
||||
R3Node * r3_tree_create(int cap);
|
||||
|
||||
node * r3_node_create();
|
||||
// R3Node * r3_node_create();
|
||||
|
||||
void r3_tree_free(node * tree);
|
||||
void r3_tree_free(R3Node * tree);
|
||||
|
||||
void r3_edge_free(edge * edge);
|
||||
|
||||
edge * r3_node_connectl(node * n, char * pat, int len, int strdup, node *child);
|
||||
R3Edge * r3_node_connectl(R3Node * n, const char * pat, int len, int strdup, R3Node *child);
|
||||
|
||||
#define r3_node_connect(n, pat, child) r3_node_connectl(n, pat, strlen(pat), 0, child)
|
||||
|
||||
edge * r3_node_find_edge(node * n, char * pat);
|
||||
R3Edge * r3_node_find_edge(const R3Node * n, const char * pat, unsigned int pat_len);
|
||||
|
||||
void r3_node_append_edge(node *n, edge *child);
|
||||
R3Edge * r3_node_append_edge(R3Node *n);
|
||||
|
||||
R3Edge * r3_node_find_common_prefix(R3Node *n, const char *path, int path_len, int *prefix_len, char **errstr);
|
||||
|
||||
R3Node * r3_tree_insert_pathl(R3Node *tree, const char *path, int path_len, void * data);
|
||||
|
||||
#define r3_tree_insert_pathl(tree, path, path_len, data) r3_tree_insert_pathl_ex(tree, path, path_len, 0, 0, data, NULL)
|
||||
|
||||
|
||||
node * r3_tree_insert_pathl(node *tree, char *path, int path_len, void * data);
|
||||
|
||||
#define r3_tree_insert_path(n,p,d) r3_tree_insert_pathl_(n,p,strlen(p), NULL, d)
|
||||
#define r3_tree_insert_route(n,r,d) r3_tree_insert_pathl_(n, r->path, r->path_len, r, d)
|
||||
R3Route * r3_tree_insert_routel(R3Node * tree, int method, const char *path, int path_len, void *data);
|
||||
|
||||
R3Route * r3_tree_insert_routel_ex(R3Node * tree, int method, const char *path, int path_len, void *data, char **errstr);
|
||||
|
||||
#define r3_tree_insert_routel(n, method, path, path_len, data) r3_tree_insert_routel_ex(n, method, path, path_len, data, NULL)
|
||||
|
||||
#define r3_tree_insert_path(n,p,d) r3_tree_insert_pathl_ex(n,p,strlen(p), 0, 0, d, NULL)
|
||||
|
||||
#define r3_tree_insert_route(n,method,path,data) r3_tree_insert_routel(n, method, path, strlen(path), data)
|
||||
|
||||
|
||||
/**
|
||||
* The private API to insert a path
|
||||
*/
|
||||
node * r3_tree_insert_pathl_(node *tree, char *path, int path_len, route * route, void * data);
|
||||
R3Node * r3_tree_insert_pathl_ex(R3Node *tree, const char *path, unsigned int path_len, int method, unsigned int router, void * data, char **errstr);
|
||||
|
||||
void r3_tree_dump(node * n, int level);
|
||||
|
||||
int r3_tree_render_file(node * tree, char * format, char * filename);
|
||||
|
||||
int r3_tree_render_dot(node * tree);
|
||||
|
||||
edge * r3_node_find_edge_str(const node * n, char * str, int str_len);
|
||||
void r3_tree_dump(const R3Node * n, int level);
|
||||
|
||||
|
||||
void r3_tree_compile(node *n);
|
||||
R3Edge * r3_node_find_edge_str(const R3Node * n, const char * str, int str_len);
|
||||
|
||||
void r3_tree_compile_patterns(node * n);
|
||||
|
||||
node * r3_tree_matchl(const node * n, char * path, int path_len, match_entry * entry);
|
||||
int r3_tree_compile(R3Node *n, char** errstr);
|
||||
|
||||
int r3_tree_compile_patterns(R3Node * n, char** errstr);
|
||||
|
||||
R3Node * r3_tree_matchl(const R3Node * n, const char * path, unsigned int path_len, match_entry * entry);
|
||||
|
||||
#define r3_tree_match(n,p,e) r3_tree_matchl(n,p, strlen(p), e)
|
||||
|
||||
// node * r3_tree_match_entry(node * n, match_entry * entry);
|
||||
#define r3_tree_match_entry(n, entry) r3_tree_matchl(n, entry->path, entry->path_len, entry)
|
||||
// R3Node * r3_tree_match_entry(R3Node * n, match_entry * entry);
|
||||
#define r3_tree_match_entry(n, entry) r3_tree_matchl(n, entry->path.base, entry->path.len, entry)
|
||||
|
||||
bool r3_node_has_slug_edges(node *n);
|
||||
bool r3_node_has_slug_edges(const R3Node *n);
|
||||
|
||||
edge * r3_edge_create(char * pattern, int pattern_len, node * child);
|
||||
// R3Edge * r3_edge_createl(const char * pattern, int pattern_len, R3Node * child);
|
||||
|
||||
node * r3_edge_branch(edge *e, int dl);
|
||||
void r3_edge_initl(R3Edge *e, const char * pattern, int pattern_len, R3Node * child);
|
||||
|
||||
void r3_edge_free(edge * edge);
|
||||
R3Node * r3_edge_branch(R3Edge *e, int dl);
|
||||
|
||||
void r3_edge_free(R3Edge * edge);
|
||||
|
||||
|
||||
|
||||
match_entry * match_entry_createl(char * path, int path_len);
|
||||
|
||||
#define match_entry_create(path) match_entry_createl(path,strlen(path))
|
||||
|
||||
void match_entry_free(match_entry * entry);
|
||||
|
||||
|
||||
route * r3_route_create(char * path);
|
||||
R3Route * r3_route_create(const char * path);
|
||||
|
||||
route * r3_route_createl(char * path, int path_len);
|
||||
// R3Route * r3_route_createl(const char * path, int path_len);
|
||||
|
||||
int r3_route_cmp(route *r1, match_entry *r2);
|
||||
|
||||
void r3_node_append_route(node * n, route * route);
|
||||
R3Route * r3_node_append_route(R3Node *tree, const char * path, int path_len, int method, void *data);
|
||||
|
||||
void r3_route_free(route * route);
|
||||
void r3_route_free(R3Route * route);
|
||||
|
||||
route * r3_tree_match_route(const node *n, match_entry * entry);
|
||||
int r3_route_cmp(const R3Route *r1, const match_entry *r2);
|
||||
|
||||
R3Route * r3_tree_match_route(const R3Node *n, match_entry * entry);
|
||||
|
||||
#define r3_route_create(p) r3_route_createl(p, strlen(p))
|
||||
|
||||
void r3_tree_feedback(node *tree, node *end);
|
||||
|
||||
#define METHOD_GET 2
|
||||
#define METHOD_POST 2<<1
|
||||
|
@ -196,12 +193,33 @@ void r3_tree_feedback(node *tree, node *end);
|
|||
#define METHOD_HEAD 2<<5
|
||||
#define METHOD_OPTIONS 2<<6
|
||||
|
||||
#define SCHEME_HTTP 2
|
||||
#define SCHEME_HTTPS 2<<1
|
||||
|
||||
|
||||
int r3_pattern_to_opcode(char * pattern, int pattern_len);
|
||||
int r3_pattern_to_opcode(const char * pattern, unsigned int len);
|
||||
|
||||
enum { NODE_COMPARE_STR, NODE_COMPARE_PCRE, NODE_COMPARE_OPCODE };
|
||||
|
||||
enum { OP_EXPECT_MORE_DIGITS = 1, OP_EXPECT_MORE_WORDS, OP_EXPECT_NOSLASH, OP_EXPECT_NODASH, OP_EXPECT_MORE_ALPHA };
|
||||
enum { OP_EXPECT_MORE_DIGITS = 1, OP_EXPECT_MORE_WORDS, OP_EXPECT_NOSLASH,
|
||||
OP_EXPECT_NODASH, OP_EXPECT_MORE_ALPHA, OP_GREEDY_ANY};
|
||||
|
||||
|
||||
|
||||
match_entry * match_entry_createl(const char * path, int path_len);
|
||||
|
||||
#define match_entry_create(path) match_entry_createl(path,strlen(path))
|
||||
|
||||
void match_entry_free(match_entry * entry);
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* !R3_NODE_H */
|
||||
|
|
140
include/r3.hpp
Normal file
140
include/r3.hpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* r3.hpp
|
||||
* Copyright (C) 2014 whitglint <whitglint.tw@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#ifndef R3_HPP
|
||||
#define R3_HPP
|
||||
|
||||
#include <cstring>
|
||||
#include "r3.h"
|
||||
|
||||
namespace r3 {
|
||||
template <typename T>
|
||||
class Base {
|
||||
public:
|
||||
Base(T* p)
|
||||
: p_(p) {
|
||||
}
|
||||
|
||||
void* data() const {
|
||||
return p_->data;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return p_;
|
||||
}
|
||||
|
||||
bool is_null() const {
|
||||
return p_ == NULL;
|
||||
}
|
||||
|
||||
operator void*() const {
|
||||
return p_;
|
||||
}
|
||||
|
||||
private:
|
||||
T* p_;
|
||||
};
|
||||
typedef Base<R3Node> Node;
|
||||
typedef Base<R3Route> Route;
|
||||
|
||||
class MatchEntry : public Base<match_entry> {
|
||||
public:
|
||||
explicit MatchEntry(const char* path)
|
||||
: Base(match_entry_create(path)) {
|
||||
}
|
||||
|
||||
MatchEntry(const char* path, int path_len)
|
||||
: Base(match_entry_createl(path, path_len)) {
|
||||
}
|
||||
|
||||
~MatchEntry() {
|
||||
if (get()) {
|
||||
match_entry_free(get());
|
||||
}
|
||||
}
|
||||
|
||||
int request_method() const {
|
||||
return get()->request_method;
|
||||
}
|
||||
|
||||
void set_request_method(int request_method) {
|
||||
get()->request_method = request_method;
|
||||
}
|
||||
|
||||
private:
|
||||
MatchEntry(const MatchEntry&);
|
||||
MatchEntry& operator =(const MatchEntry&);
|
||||
};
|
||||
|
||||
class Tree : public Base<R3Node> {
|
||||
public:
|
||||
explicit Tree(int cap)
|
||||
: Base(r3_tree_create(cap)) {
|
||||
}
|
||||
|
||||
~Tree() {
|
||||
if (get()) {
|
||||
r3_tree_free(get());
|
||||
}
|
||||
}
|
||||
|
||||
int compile(char** errstr = NULL) {
|
||||
return r3_tree_compile(get(), errstr);
|
||||
}
|
||||
|
||||
void dump(int level) const {
|
||||
r3_tree_dump(get(), level);
|
||||
}
|
||||
|
||||
Node insert_path(const char* path, void* data, char** errstr = NULL) {
|
||||
return r3_tree_insert_pathl_ex(get(), path, std::strlen(path), 0, 0,
|
||||
data, errstr);
|
||||
}
|
||||
|
||||
Node insert_pathl(const char* path, int path_len, void* data,
|
||||
char** errstr = NULL) {
|
||||
return r3_tree_insert_pathl_ex(get(), path, path_len, 0, 0, data,
|
||||
errstr);
|
||||
}
|
||||
|
||||
Route insert_route(int method, const char* path, void* data,
|
||||
char** errstr = NULL) {
|
||||
return r3_tree_insert_routel_ex(get(), method, path,
|
||||
std::strlen(path), data, errstr);
|
||||
}
|
||||
|
||||
Route insert_routel(int method, const char* path, int path_len,
|
||||
void* data, char** errstr = NULL) {
|
||||
return r3_tree_insert_routel_ex(get(), method, path, path_len, data,
|
||||
errstr);
|
||||
}
|
||||
|
||||
Node match(const char* path, MatchEntry* entry = NULL) const {
|
||||
return r3_tree_match(get(), path,
|
||||
entry != NULL ? entry->get() : NULL);
|
||||
}
|
||||
|
||||
Node matchl(const char* path, int path_len, MatchEntry* entry = NULL)
|
||||
const {
|
||||
return r3_tree_matchl(get(), path, path_len,
|
||||
entry != NULL ? entry->get() : NULL);
|
||||
}
|
||||
|
||||
Node match_entry(MatchEntry& entry) const {
|
||||
return r3_tree_match_entry(get(), entry.get());
|
||||
}
|
||||
|
||||
Route match_route(MatchEntry& entry) const {
|
||||
return r3_tree_match_route(get(), entry.get());
|
||||
}
|
||||
|
||||
private:
|
||||
Tree(const Tree&);
|
||||
Tree& operator =(const Tree&);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // R3_HPP
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* r3_define.h
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
||||
#ifndef DEFINE_H
|
||||
#define DEFINE_H
|
||||
|
||||
#ifndef bool
|
||||
typedef unsigned char bool;
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
# define FALSE 0
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
# define TRUE 1
|
||||
#endif
|
||||
|
||||
// #define DEBUG 1
|
||||
#ifdef DEBUG
|
||||
|
||||
#define info(fmt, ...) \
|
||||
do { fprintf(stderr, fmt, __VA_ARGS__); } while (0)
|
||||
|
||||
#define debug(fmt, ...) \
|
||||
do { fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
|
||||
__LINE__, __func__, __VA_ARGS__); } while (0)
|
||||
|
||||
#else
|
||||
#define info(...);
|
||||
#define debug(...);
|
||||
#endif
|
||||
|
||||
#endif /* !DEFINE_H */
|
|
@ -1,14 +1,23 @@
|
|||
/*
|
||||
* r3_gvc.h
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#ifndef R3_GVC_H
|
||||
#define R3_GVC_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gvc.h>
|
||||
#include "r3.h"
|
||||
|
||||
void r3_tree_build_ag_nodes(Agraph_t * g, Agnode_t * ag_parent_node, const node * n, int * node_cnt);
|
||||
|
||||
int r3_tree_render(const node * tree, const char *layout, const char * format, FILE *fp);
|
||||
|
||||
int r3_tree_render_dot(const node * tree, const char *layout, FILE *fp);
|
||||
|
||||
int r3_tree_render_file(const node * tree, const char * format, const char * filename);
|
||||
|
||||
void r3_tree_build_ag_nodes(Agraph_t * g, Agnode_t * ag_parent_node, node * n, int node_cnt);
|
||||
|
||||
#endif /* !R3_GVC_H */
|
||||
|
|
24
include/r3_json.h
Normal file
24
include/r3_json.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* r3_json.h
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
||||
#ifndef R3_JSON_H
|
||||
#define R3_JSON_H
|
||||
|
||||
#include <json-c/json.h>
|
||||
#include "r3.h"
|
||||
|
||||
json_object * r3_edge_to_json_object(const R3Edge * e);
|
||||
json_object * r3_node_to_json_object(const R3Node * n);
|
||||
json_object * r3_route_to_json_object(const R3Route * r);
|
||||
|
||||
const char * r3_node_to_json_string_ext(const R3Node * n, int options);
|
||||
const char * r3_node_to_json_pretty_string(const R3Node * n);
|
||||
const char * r3_node_to_json_string(const R3Node * n);
|
||||
|
||||
|
||||
|
||||
#endif /* !R3_JSON_H */
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* r3_list.h
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
|
30
include/r3_slug.h
Normal file
30
include/r3_slug.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* r3_str.h
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#ifndef R3_SLUG_H
|
||||
#define R3_SLUG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char * r3_slug_compile(const char * str, unsigned int len);
|
||||
|
||||
const char * r3_slug_find_pattern(const char *s1, unsigned int str_len, unsigned int *len);
|
||||
|
||||
const char * r3_slug_find_name(const char *s1, unsigned int str_len, unsigned int *len);
|
||||
|
||||
const char * r3_slug_find_placeholder(const char *s1, unsigned int str_len, unsigned int *len);
|
||||
|
||||
int r3_slug_count(const char * needle, int len, char **errstr);
|
||||
|
||||
char * r3_inside_slug(const char * needle, int needle_len, char *offset, char **errstr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !R3_SLUG_H */
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* r3_str.h
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#ifndef STR_H
|
||||
#define STR_H
|
||||
|
||||
#include "r3.h"
|
||||
#include "config.h"
|
||||
|
||||
int slug_count(char * p, int len);
|
||||
|
||||
char * slug_compile(char * str, int len);
|
||||
|
||||
bool contains_slug(char * str);
|
||||
|
||||
char * slug_find_pattern(char *s1, int *len);
|
||||
|
||||
char * slug_find_placeholder(char *s1, int *len);
|
||||
|
||||
char * inside_slug(char * needle, int needle_len, char *offset);
|
||||
|
||||
char * ltrim_slash(char* str);
|
||||
|
||||
void str_repeat(char *s, char *c, int len);
|
||||
|
||||
void print_indent(int level);
|
||||
|
||||
#ifndef HAVE_STRDUP
|
||||
char *strdup(const char *s);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRNDUP
|
||||
char *strndup(const char *s, int n);
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* !STR_H */
|
||||
|
|
@ -1,37 +1,41 @@
|
|||
/*
|
||||
* str_array.h
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
||||
#ifndef TOKEN_H
|
||||
#define TOKEN_H
|
||||
#ifndef STR_ARRAY_H
|
||||
#define STR_ARRAY_H
|
||||
|
||||
#include "r3_define.h"
|
||||
#include "memory.h"
|
||||
|
||||
#if __STDC_VERSION__ <= 201710L
|
||||
#ifdef HAVE_STDBOOL_H
|
||||
# include <stdbool.h>
|
||||
#elif !defined(bool) && !defined(__cplusplus)
|
||||
typedef unsigned char bool;
|
||||
# define bool bool /* For redefinition guards */
|
||||
# define false 0
|
||||
# define true 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct _str_array {
|
||||
char **tokens;
|
||||
int len;
|
||||
int cap;
|
||||
R3_VECTOR(r3_iovec_t) slugs;
|
||||
R3_VECTOR(r3_iovec_t) tokens;
|
||||
} str_array;
|
||||
|
||||
str_array * str_array_create(int cap);
|
||||
|
||||
bool str_array_is_full(str_array * l);
|
||||
|
||||
bool str_array_resize(str_array *l, int new_cap);
|
||||
|
||||
bool str_array_append(str_array * list, char * token);
|
||||
bool str_array_append(str_array * l, const char * token, unsigned int len);
|
||||
|
||||
void str_array_free(str_array *l);
|
||||
|
||||
void str_array_dump(str_array *l);
|
||||
void str_array_dump_slugs(const str_array *l);
|
||||
|
||||
str_array * split_route_pattern(char *pattern, int pattern_len);
|
||||
void str_array_dump(const str_array *l);
|
||||
|
||||
#define str_array_fetch(t,i) t->tokens[i]
|
||||
#define str_array_len(t) t->len
|
||||
#define str_array_cap(t) t->cap
|
||||
#define str_array_fetch(t,i) t->tokens.entries[i]
|
||||
#define str_array_len(t) t->tokens.size
|
||||
#define str_array_cap(t) t->tokens.capacity
|
||||
|
||||
#endif /* !TOKEN_H */
|
||||
#endif /* !STR_ARRAY_H */
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
#ifndef ZMALLOC_H
|
||||
#define ZMALLOC_H
|
||||
|
||||
/* zmalloc - total amount of allocated memory aware version of malloc()
|
||||
*
|
||||
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*/
|
||||
|
||||
/* Double expansion needed for stringification of macro values. */
|
||||
#define __xstr(s) __str(s)
|
||||
#define __str(s) #s
|
||||
|
||||
#if defined(USE_TCMALLOC)
|
||||
#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))
|
||||
#include <google/tcmalloc.h>
|
||||
#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define zmalloc_size(p) tc_malloc_size(p)
|
||||
#else
|
||||
#error "Newer version of tcmalloc required"
|
||||
#endif
|
||||
|
||||
#elif defined(USE_JEMALLOC) && (JEMALLOC_VERSION_MAJOR > 2)
|
||||
#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define zmalloc_size(p) je_malloc_usable_size(p)
|
||||
#else
|
||||
#error "Newer version of jemalloc required"
|
||||
#endif
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#include <malloc/malloc.h>
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define zmalloc_size(p) malloc_size(p)
|
||||
#endif
|
||||
|
||||
#ifndef ZMALLOC_LIB
|
||||
#define ZMALLOC_LIB "libc"
|
||||
#endif
|
||||
|
||||
void *zmalloc(size_t size);
|
||||
void *zcalloc(size_t size);
|
||||
void *zrealloc(void *ptr, size_t size);
|
||||
void zfree(void *ptr);
|
||||
char *zstrdup(const char *s);
|
||||
char *zstrndup(const char *s, size_t n);
|
||||
size_t zmalloc_used_memory(void);
|
||||
void zmalloc_enable_thread_safeness(void);
|
||||
void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
|
||||
float zmalloc_get_fragmentation_ratio(size_t rss);
|
||||
size_t zmalloc_get_rss(void);
|
||||
size_t zmalloc_get_private_dirty(void);
|
||||
void zlibc_free(void *ptr);
|
||||
|
||||
#ifndef HAVE_MALLOC_SIZE
|
||||
size_t zmalloc_size(void *ptr);
|
||||
#endif
|
||||
|
||||
#endif // ZMALLOC_H
|
9
package.json
Normal file
9
package.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "r3",
|
||||
"version": "2.0.0",
|
||||
"repo": "brendanashworth/r3",
|
||||
"description": "high-performance path dispatching library",
|
||||
"keywords": ["path", "dispatch", "performance", "r3", "c9s"],
|
||||
"license": "MIT",
|
||||
"src": ["include/memory.h", "include/r3.h", "include/r3.hpp", "include/r3_gvc.h", "include/r3_json.h", "include/r3_list.h", "include/r3_slug.h", "include/str_array.h", "src/edge.c", "src/gvc.c", "src/json.c", "src/list.c", "src/match_entry.c", "src/node.c", "src/slug.c", "src/slug.h", "src/str.c", "src/token.c"]
|
||||
}
|
40
php/r3/annotation/annot.h
Normal file
40
php/r3/annotation/annot.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
|
||||
/*
|
||||
+------------------------------------------------------------------------+
|
||||
| Phalcon Framework |
|
||||
+------------------------------------------------------------------------+
|
||||
| Copyright (c) 2011-2014 Phalcon Team (http://www.phalconphp.com) |
|
||||
+------------------------------------------------------------------------+
|
||||
| This source file is subject to the New BSD License that is bundled |
|
||||
| with this package in the file docs/LICENSE.txt. |
|
||||
| |
|
||||
| If you did not receive a copy of the license and are unable to |
|
||||
| obtain it through the world-wide-web, please send an email |
|
||||
| to license@phalconphp.com so we can send you a copy immediately. |
|
||||
+------------------------------------------------------------------------+
|
||||
| Authors: Andres Gutierrez <andres@phalconphp.com> |
|
||||
| Eduar Carvajal <eduar@phalconphp.com> |
|
||||
+------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
typedef struct _phannot_parser_token {
|
||||
char *token;
|
||||
int opcode;
|
||||
int token_len;
|
||||
int free_flag;
|
||||
} phannot_parser_token;
|
||||
|
||||
typedef struct _phannot_parser_status {
|
||||
zval *ret;
|
||||
phannot_scanner_state *scanner_state;
|
||||
phannot_scanner_token *token;
|
||||
int status;
|
||||
zend_uint syntax_error_len;
|
||||
char *syntax_error;
|
||||
} phannot_parser_status;
|
||||
|
||||
#define PHANNOT_PARSING_OK 1
|
||||
#define PHANNOT_PARSING_FAILED 0
|
||||
|
||||
extern int phannot_parse_annotations(zval *result, zval *view_code, zval *template_path, zval *line TSRMLS_DC);
|
||||
int phannot_internal_parse_annotations(zval **result, zval *view_code, zval *template_path, zval *line, zval **error_msg TSRMLS_DC);
|
441
php/r3/annotation/base.c
Normal file
441
php/r3/annotation/base.c
Normal file
|
@ -0,0 +1,441 @@
|
|||
|
||||
/*
|
||||
+------------------------------------------------------------------------+
|
||||
| Phalcon Framework |
|
||||
+------------------------------------------------------------------------+
|
||||
| Copyright (c) 2011-2014 Phalcon Team (http://www.phalconphp.com) |
|
||||
+------------------------------------------------------------------------+
|
||||
| This source file is subject to the New BSD License that is bundled |
|
||||
| with this package in the file docs/LICENSE.txt. |
|
||||
| |
|
||||
| If you did not receive a copy of the license and are unable to |
|
||||
| obtain it through the world-wide-web, please send an email |
|
||||
| to license@phalconphp.com so we can send you a copy immediately. |
|
||||
+------------------------------------------------------------------------+
|
||||
| Authors: Andres Gutierrez <andres@phalconphp.com> |
|
||||
| Eduar Carvajal <eduar@phalconphp.com> |
|
||||
+------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
const phannot_token_names phannot_tokens[] =
|
||||
{
|
||||
{ "INTEGER", PHANNOT_T_INTEGER },
|
||||
{ "DOUBLE", PHANNOT_T_DOUBLE },
|
||||
{ "STRING", PHANNOT_T_STRING },
|
||||
{ "IDENTIFIER", PHANNOT_T_IDENTIFIER },
|
||||
{ "@", PHANNOT_T_AT },
|
||||
{ ",", PHANNOT_T_COMMA },
|
||||
{ "=", PHANNOT_T_EQUALS },
|
||||
{ ":", PHANNOT_T_COLON },
|
||||
{ "(", PHANNOT_T_PARENTHESES_OPEN },
|
||||
{ ")", PHANNOT_T_PARENTHESES_CLOSE },
|
||||
{ "{", PHANNOT_T_BRACKET_OPEN },
|
||||
{ "}", PHANNOT_T_BRACKET_CLOSE },
|
||||
{ "[", PHANNOT_T_SBRACKET_OPEN },
|
||||
{ "]", PHANNOT_T_SBRACKET_CLOSE },
|
||||
{ "ARBITRARY TEXT", PHANNOT_T_ARBITRARY_TEXT },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper to alloc memory within the parser
|
||||
*/
|
||||
static void *phannot_wrapper_alloc(size_t bytes){
|
||||
return emalloc(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper to free memory within the parser
|
||||
*/
|
||||
static void phannot_wrapper_free(void *pointer){
|
||||
efree(pointer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a parser_token to be passed to the parser
|
||||
*/
|
||||
static void phannot_parse_with_token(void* phannot_parser, int opcode, int parsercode, phannot_scanner_token *token, phannot_parser_status *parser_status){
|
||||
|
||||
phannot_parser_token *pToken;
|
||||
|
||||
pToken = emalloc(sizeof(phannot_parser_token));
|
||||
pToken->opcode = opcode;
|
||||
pToken->token = token->value;
|
||||
pToken->token_len = token->len;
|
||||
pToken->free_flag = 1;
|
||||
|
||||
phannot_(phannot_parser, parsercode, pToken, parser_status);
|
||||
|
||||
token->value = NULL;
|
||||
token->len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an error message when it's triggered by the scanner
|
||||
*/
|
||||
static void phannot_scanner_error_msg(phannot_parser_status *parser_status, zval **error_msg TSRMLS_DC){
|
||||
|
||||
int error_length;
|
||||
char *error, *error_part;
|
||||
phannot_scanner_state *state = parser_status->scanner_state;
|
||||
|
||||
ALLOC_INIT_ZVAL(*error_msg);
|
||||
if (state->start) {
|
||||
error_length = 128 + state->start_length + Z_STRLEN_P(state->active_file);
|
||||
error = emalloc(sizeof(char) * error_length);
|
||||
if (state->start_length > 16) {
|
||||
error_part = estrndup(state->start, 16);
|
||||
snprintf(error, 64 + state->start_length, "Scanning error before '%s...' in %s on line %d", error_part, Z_STRVAL_P(state->active_file), state->active_line);
|
||||
efree(error_part);
|
||||
} else {
|
||||
snprintf(error, error_length - 1, "Scanning error before '%s' in %s on line %d", state->start, Z_STRVAL_P(state->active_file), state->active_line);
|
||||
}
|
||||
error[error_length - 1] = '\0';
|
||||
ZVAL_STRING(*error_msg, error, 1);
|
||||
} else {
|
||||
error_length = sizeof(char) * (64 + Z_STRLEN_P(state->active_file));
|
||||
error = emalloc(error_length);
|
||||
snprintf(error, error_length - 1, "Scanning error near to EOF in %s", Z_STRVAL_P(state->active_file));
|
||||
ZVAL_STRING(*error_msg, error, 1);
|
||||
error[error_length - 1] = '\0';
|
||||
}
|
||||
efree(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives the comment tokenizes and parses it
|
||||
*/
|
||||
int phannot_parse_annotations(zval *result, zval *comment, zval *file_path, zval *line TSRMLS_DC){
|
||||
|
||||
zval *error_msg = NULL;
|
||||
|
||||
ZVAL_NULL(result);
|
||||
|
||||
if (Z_TYPE_P(comment) != IS_STRING) {
|
||||
zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), ZEND_STRL("Comment must be a string"), 0 TSRMLS_CC);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if(phannot_internal_parse_annotations(&result, comment, file_path, line, &error_msg TSRMLS_CC) == FAILURE){
|
||||
if (error_msg != NULL) {
|
||||
// phalcon_throw_exception_string(phalcon_annotations_exception_ce, Z_STRVAL_P(error_msg), Z_STRLEN_P(error_msg), 1 TSRMLS_CC);
|
||||
zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), Z_STRVAL_P(error_msg), Z_STRLEN_P(error_msg) , 0 TSRMLS_CC);
|
||||
}
|
||||
else {
|
||||
// phalcon_throw_exception_string(phalcon_annotations_exception_ce, ZEND_STRL("There was an error parsing annotation"), 1 TSRMLS_CC);
|
||||
zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), ZEND_STRL("There was an error parsing annotation") , 0 TSRMLS_CC);
|
||||
}
|
||||
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove comment separators from a docblock
|
||||
*/
|
||||
void phannot_remove_comment_separators(zval *return_value, char *comment, int length, int *start_lines) {
|
||||
|
||||
int start_mode = 1, j, i, open_parentheses;
|
||||
smart_str processed_str = {0};
|
||||
char ch;
|
||||
|
||||
(*start_lines) = 0;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
|
||||
ch = comment[i];
|
||||
|
||||
if (start_mode) {
|
||||
if (ch == ' ' || ch == '*' || ch == '/' || ch == '\t' || ch == 11) {
|
||||
continue;
|
||||
}
|
||||
start_mode = 0;
|
||||
}
|
||||
|
||||
if (ch == '@') {
|
||||
|
||||
smart_str_appendc(&processed_str, ch);
|
||||
i++;
|
||||
|
||||
open_parentheses = 0;
|
||||
for (j = i; j < length; j++) {
|
||||
|
||||
ch = comment[j];
|
||||
|
||||
if (start_mode) {
|
||||
if (ch == ' ' || ch == '*' || ch == '/' || ch == '\t' || ch == 11) {
|
||||
continue;
|
||||
}
|
||||
start_mode = 0;
|
||||
}
|
||||
|
||||
if (open_parentheses == 0) {
|
||||
|
||||
if (isalnum(ch) || '_' == ch || '\\' == ch) {
|
||||
smart_str_appendc(&processed_str, ch);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '(') {
|
||||
smart_str_appendc(&processed_str, ch);
|
||||
open_parentheses++;
|
||||
continue;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
smart_str_appendc(&processed_str, ch);
|
||||
|
||||
if (ch == '(') {
|
||||
open_parentheses++;
|
||||
} else if (ch == ')') {
|
||||
open_parentheses--;
|
||||
} else if (ch == '\n') {
|
||||
(*start_lines)++;
|
||||
start_mode = 1;
|
||||
}
|
||||
|
||||
if (open_parentheses > 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
i = j;
|
||||
smart_str_appendc(&processed_str, ' ');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '\n') {
|
||||
(*start_lines)++;
|
||||
start_mode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
smart_str_0(&processed_str);
|
||||
|
||||
if (processed_str.len) {
|
||||
RETURN_STRINGL(processed_str.c, processed_str.len, 0);
|
||||
} else {
|
||||
RETURN_EMPTY_STRING();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a comment returning an intermediate array representation
|
||||
*/
|
||||
int phannot_internal_parse_annotations(zval **result, zval *comment, zval *file_path, zval *line, zval **error_msg TSRMLS_DC) {
|
||||
|
||||
char *error;
|
||||
phannot_scanner_state *state;
|
||||
phannot_scanner_token token;
|
||||
int scanner_status, status = SUCCESS, start_lines, error_length;
|
||||
phannot_parser_status *parser_status = NULL;
|
||||
void* phannot_parser;
|
||||
zval processed_comment;
|
||||
|
||||
/**
|
||||
* Check if the comment has content
|
||||
*/
|
||||
if (!Z_STRVAL_P(comment)) {
|
||||
ZVAL_BOOL(*result, 0);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (Z_STRLEN_P(comment) < 2) {
|
||||
ZVAL_BOOL(*result, 0);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove comment separators
|
||||
*/
|
||||
phannot_remove_comment_separators(&processed_comment, Z_STRVAL_P(comment), Z_STRLEN_P(comment), &start_lines);
|
||||
|
||||
if (Z_STRLEN(processed_comment) < 2) {
|
||||
ZVAL_BOOL(*result, 0);
|
||||
efree(Z_STRVAL(processed_comment));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the reentrant parser
|
||||
*/
|
||||
phannot_parser = phannot_Alloc(phannot_wrapper_alloc);
|
||||
|
||||
parser_status = emalloc(sizeof(phannot_parser_status));
|
||||
state = emalloc(sizeof(phannot_scanner_state));
|
||||
|
||||
parser_status->status = PHANNOT_PARSING_OK;
|
||||
parser_status->scanner_state = state;
|
||||
parser_status->ret = NULL;
|
||||
parser_status->token = &token;
|
||||
parser_status->syntax_error = NULL;
|
||||
|
||||
/**
|
||||
* Initialize the scanner state
|
||||
*/
|
||||
state->active_token = 0;
|
||||
state->start = Z_STRVAL(processed_comment);
|
||||
state->start_length = 0;
|
||||
state->mode = PHANNOT_MODE_RAW;
|
||||
state->active_file = file_path;
|
||||
|
||||
token.value = NULL;
|
||||
token.len = 0;
|
||||
|
||||
/**
|
||||
* Possible start line
|
||||
*/
|
||||
if (Z_TYPE_P(line) == IS_LONG) {
|
||||
state->active_line = Z_LVAL_P(line) - start_lines;
|
||||
} else {
|
||||
state->active_line = 1;
|
||||
}
|
||||
|
||||
state->end = state->start;
|
||||
|
||||
while(0 <= (scanner_status = phannot_get_token(state, &token))) {
|
||||
|
||||
state->active_token = token.opcode;
|
||||
|
||||
state->start_length = (Z_STRVAL(processed_comment) + Z_STRLEN(processed_comment) - state->start);
|
||||
|
||||
switch (token.opcode) {
|
||||
|
||||
case PHANNOT_T_IGNORE:
|
||||
break;
|
||||
|
||||
case PHANNOT_T_AT:
|
||||
phannot_(phannot_parser, PHANNOT_AT, NULL, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_COMMA:
|
||||
phannot_(phannot_parser, PHANNOT_COMMA, NULL, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_EQUALS:
|
||||
phannot_(phannot_parser, PHANNOT_EQUALS, NULL, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_COLON:
|
||||
phannot_(phannot_parser, PHANNOT_COLON, NULL, parser_status);
|
||||
break;
|
||||
|
||||
case PHANNOT_T_PARENTHESES_OPEN:
|
||||
phannot_(phannot_parser, PHANNOT_PARENTHESES_OPEN, NULL, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_PARENTHESES_CLOSE:
|
||||
phannot_(phannot_parser, PHANNOT_PARENTHESES_CLOSE, NULL, parser_status);
|
||||
break;
|
||||
|
||||
case PHANNOT_T_BRACKET_OPEN:
|
||||
phannot_(phannot_parser, PHANNOT_BRACKET_OPEN, NULL, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_BRACKET_CLOSE:
|
||||
phannot_(phannot_parser, PHANNOT_BRACKET_CLOSE, NULL, parser_status);
|
||||
break;
|
||||
|
||||
case PHANNOT_T_SBRACKET_OPEN:
|
||||
phannot_(phannot_parser, PHANNOT_SBRACKET_OPEN, NULL, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_SBRACKET_CLOSE:
|
||||
phannot_(phannot_parser, PHANNOT_SBRACKET_CLOSE, NULL, parser_status);
|
||||
break;
|
||||
|
||||
case PHANNOT_T_NULL:
|
||||
phannot_(phannot_parser, PHANNOT_NULL, NULL, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_TRUE:
|
||||
phannot_(phannot_parser, PHANNOT_TRUE, NULL, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_FALSE:
|
||||
phannot_(phannot_parser, PHANNOT_FALSE, NULL, parser_status);
|
||||
break;
|
||||
|
||||
case PHANNOT_T_INTEGER:
|
||||
phannot_parse_with_token(phannot_parser, PHANNOT_T_INTEGER, PHANNOT_INTEGER, &token, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_DOUBLE:
|
||||
phannot_parse_with_token(phannot_parser, PHANNOT_T_DOUBLE, PHANNOT_DOUBLE, &token, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_STRING:
|
||||
phannot_parse_with_token(phannot_parser, PHANNOT_T_STRING, PHANNOT_STRING, &token, parser_status);
|
||||
break;
|
||||
case PHANNOT_T_IDENTIFIER:
|
||||
phannot_parse_with_token(phannot_parser, PHANNOT_T_IDENTIFIER, PHANNOT_IDENTIFIER, &token, parser_status);
|
||||
break;
|
||||
/*case PHANNOT_T_ARBITRARY_TEXT:
|
||||
phannot_parse_with_token(phannot_parser, PHANNOT_T_ARBITRARY_TEXT, PHANNOT_ARBITRARY_TEXT, &token, parser_status);
|
||||
break;*/
|
||||
|
||||
default:
|
||||
parser_status->status = PHANNOT_PARSING_FAILED;
|
||||
if (!*error_msg) {
|
||||
error_length = sizeof(char) * (48 + Z_STRLEN_P(state->active_file));
|
||||
error = emalloc(error_length);
|
||||
snprintf(error, error_length - 1, "Scanner: unknown opcode %d on in %s line %d", token.opcode, Z_STRVAL_P(state->active_file), state->active_line);
|
||||
error[error_length - 1] = '\0';
|
||||
ALLOC_INIT_ZVAL(*error_msg);
|
||||
ZVAL_STRING(*error_msg, error, 1);
|
||||
efree(error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (parser_status->status != PHANNOT_PARSING_OK) {
|
||||
status = FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
state->end = state->start;
|
||||
}
|
||||
|
||||
if (status != FAILURE) {
|
||||
switch (scanner_status) {
|
||||
case PHANNOT_SCANNER_RETCODE_ERR:
|
||||
case PHANNOT_SCANNER_RETCODE_IMPOSSIBLE:
|
||||
if (!*error_msg) {
|
||||
phannot_scanner_error_msg(parser_status, error_msg TSRMLS_CC);
|
||||
}
|
||||
status = FAILURE;
|
||||
break;
|
||||
default:
|
||||
phannot_(phannot_parser, 0, NULL, parser_status);
|
||||
}
|
||||
}
|
||||
|
||||
state->active_token = 0;
|
||||
state->start = NULL;
|
||||
|
||||
if (parser_status->status != PHANNOT_PARSING_OK) {
|
||||
status = FAILURE;
|
||||
if (parser_status->syntax_error) {
|
||||
if (!*error_msg) {
|
||||
ALLOC_INIT_ZVAL(*error_msg);
|
||||
ZVAL_STRING(*error_msg, parser_status->syntax_error, 1);
|
||||
}
|
||||
efree(parser_status->syntax_error);
|
||||
}
|
||||
}
|
||||
|
||||
phannot_Free(phannot_parser, phannot_wrapper_free);
|
||||
|
||||
if (status != FAILURE) {
|
||||
if (parser_status->status == PHANNOT_PARSING_OK) {
|
||||
if (parser_status->ret) {
|
||||
ZVAL_ZVAL(*result, parser_status->ret, 0, 0);
|
||||
ZVAL_NULL(parser_status->ret);
|
||||
zval_ptr_dtor(&parser_status->ret);
|
||||
} else {
|
||||
array_init(*result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
efree(Z_STRVAL(processed_comment));
|
||||
|
||||
efree(parser_status);
|
||||
efree(state);
|
||||
|
||||
return status;
|
||||
}
|
BIN
php/r3/annotation/lemon
Executable file
BIN
php/r3/annotation/lemon
Executable file
Binary file not shown.
4564
php/r3/annotation/lemon.c
Normal file
4564
php/r3/annotation/lemon.c
Normal file
File diff suppressed because it is too large
Load diff
687
php/r3/annotation/lempar.c
Normal file
687
php/r3/annotation/lempar.c
Normal file
|
@ -0,0 +1,687 @@
|
|||
/* Driver template for the LEMON parser generator.
|
||||
** The author disclaims copyright to this source code.
|
||||
*/
|
||||
/* First off, code is include which follows the "include" declaration
|
||||
** in the input file. */
|
||||
#include <stdio.h>
|
||||
%%
|
||||
/* Next is all token values, in a form suitable for use by makeheaders.
|
||||
** This section will be null unless lemon is run with the -m switch.
|
||||
*/
|
||||
/*
|
||||
** These constants (all generated automatically by the parser generator)
|
||||
** specify the various kinds of tokens (terminals) that the parser
|
||||
** understands.
|
||||
**
|
||||
** Each symbol here is a terminal symbol in the grammar.
|
||||
*/
|
||||
%%
|
||||
/* Make sure the INTERFACE macro is defined.
|
||||
*/
|
||||
#ifndef INTERFACE
|
||||
# define INTERFACE 1
|
||||
#endif
|
||||
/* The next thing included is series of defines which control
|
||||
** various aspects of the generated parser.
|
||||
** YYCODETYPE is the data type used for storing terminal
|
||||
** and nonterminal numbers. "unsigned char" is
|
||||
** used if there are fewer than 250 terminals
|
||||
** and nonterminals. "int" is used otherwise.
|
||||
** YYNOCODE is a number of type YYCODETYPE which corresponds
|
||||
** to no legal terminal or nonterminal number. This
|
||||
** number is used to fill in empty slots of the hash
|
||||
** table.
|
||||
** YYFALLBACK If defined, this indicates that one or more tokens
|
||||
** have fall-back values which should be used if the
|
||||
** original value of the token will not parse.
|
||||
** YYACTIONTYPE is the data type used for storing terminal
|
||||
** and nonterminal numbers. "unsigned char" is
|
||||
** used if there are fewer than 250 rules and
|
||||
** states combined. "int" is used otherwise.
|
||||
** ParseTOKENTYPE is the data type used for minor tokens given
|
||||
** directly to the parser from the tokenizer.
|
||||
** YYMINORTYPE is the data type used for all minor tokens.
|
||||
** This is typically a union of many types, one of
|
||||
** which is ParseTOKENTYPE. The entry in the union
|
||||
** for base tokens is called "yy0".
|
||||
** YYSTACKDEPTH is the maximum depth of the parser's stack.
|
||||
** ParseARG_SDECL A static variable declaration for the %extra_argument
|
||||
** ParseARG_PDECL A parameter declaration for the %extra_argument
|
||||
** ParseARG_STORE Code to store %extra_argument into yypParser
|
||||
** ParseARG_FETCH Code to extract %extra_argument from yypParser
|
||||
** YYNSTATE the combined number of states.
|
||||
** YYNRULE the number of rules in the grammar
|
||||
** YYERRORSYMBOL is the code number of the error symbol. If not
|
||||
** defined, then do no error processing.
|
||||
*/
|
||||
%%
|
||||
#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
|
||||
#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
|
||||
#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
|
||||
|
||||
/* Next are that tables used to determine what action to take based on the
|
||||
** current state and lookahead token. These tables are used to implement
|
||||
** functions that take a state number and lookahead value and return an
|
||||
** action integer.
|
||||
**
|
||||
** Suppose the action integer is N. Then the action is determined as
|
||||
** follows
|
||||
**
|
||||
** 0 <= N < YYNSTATE Shift N. That is, push the lookahead
|
||||
** token onto the stack and goto state N.
|
||||
**
|
||||
** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE.
|
||||
**
|
||||
** N == YYNSTATE+YYNRULE A syntax error has occurred.
|
||||
**
|
||||
** N == YYNSTATE+YYNRULE+1 The parser accepts its input.
|
||||
**
|
||||
** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused
|
||||
** slots in the yy_action[] table.
|
||||
**
|
||||
** The action table is constructed as a single large table named yy_action[].
|
||||
** Given state S and lookahead X, the action is computed as
|
||||
**
|
||||
** yy_action[ yy_shift_ofst[S] + X ]
|
||||
**
|
||||
** If the index value yy_shift_ofst[S]+X is out of range or if the value
|
||||
** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
|
||||
** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
|
||||
** and that yy_default[S] should be used instead.
|
||||
**
|
||||
** The formula above is for computing the action when the lookahead is
|
||||
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
|
||||
** a reduce action) then the yy_reduce_ofst[] array is used in place of
|
||||
** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
|
||||
** YY_SHIFT_USE_DFLT.
|
||||
**
|
||||
** The following are the tables generated in this section:
|
||||
**
|
||||
** yy_action[] A single table containing all actions.
|
||||
** yy_lookahead[] A table containing the lookahead for each entry in
|
||||
** yy_action. Used to detect hash collisions.
|
||||
** yy_shift_ofst[] For each state, the offset into yy_action for
|
||||
** shifting terminals.
|
||||
** yy_reduce_ofst[] For each state, the offset into yy_action for
|
||||
** shifting non-terminals after a reduce.
|
||||
** yy_default[] Default action for each state.
|
||||
*/
|
||||
%%
|
||||
#define YY_SZ_ACTTAB (sizeof(yy_action)/sizeof(yy_action[0]))
|
||||
|
||||
/* The next table maps tokens into fallback tokens. If a construct
|
||||
** like the following:
|
||||
**
|
||||
** %fallback ID X Y Z.
|
||||
**
|
||||
** appears in the grammer, then ID becomes a fallback token for X, Y,
|
||||
** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
|
||||
** but it does not parse, the type of the token is changed to ID and
|
||||
** the parse is retried before an error is thrown.
|
||||
*/
|
||||
#ifdef YYFALLBACK
|
||||
static const YYCODETYPE yyFallback[] = {
|
||||
%%
|
||||
};
|
||||
#endif /* YYFALLBACK */
|
||||
|
||||
/* The following structure represents a single element of the
|
||||
** parser's stack. Information stored includes:
|
||||
**
|
||||
** + The state number for the parser at this level of the stack.
|
||||
**
|
||||
** + The value of the token stored at this level of the stack.
|
||||
** (In other words, the "major" token.)
|
||||
**
|
||||
** + The semantic value stored at this level of the stack. This is
|
||||
** the information used by the action routines in the grammar.
|
||||
** It is sometimes called the "minor" token.
|
||||
*/
|
||||
struct yyStackEntry {
|
||||
int stateno; /* The state-number */
|
||||
int major; /* The major token value. This is the code
|
||||
** number for the token at this stack level */
|
||||
YYMINORTYPE minor; /* The user-supplied minor token value. This
|
||||
** is the value of the token */
|
||||
};
|
||||
typedef struct yyStackEntry yyStackEntry;
|
||||
|
||||
/* The state of the parser is completely contained in an instance of
|
||||
** the following structure */
|
||||
struct yyParser {
|
||||
int yyidx; /* Index of top element in stack */
|
||||
int yyerrcnt; /* Shifts left before out of the error */
|
||||
ParseARG_SDECL /* A place to hold %extra_argument */
|
||||
yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
|
||||
};
|
||||
typedef struct yyParser yyParser;
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <stdio.h>
|
||||
static FILE *yyTraceFILE = 0;
|
||||
static char *yyTracePrompt = 0;
|
||||
#endif /* NDEBUG */
|
||||
|
||||
#ifndef NDEBUG
|
||||
/*
|
||||
** Turn parser tracing on by giving a stream to which to write the trace
|
||||
** and a prompt to preface each trace message. Tracing is turned off
|
||||
** by making either argument NULL
|
||||
**
|
||||
** Inputs:
|
||||
** <ul>
|
||||
** <li> A FILE* to which trace output should be written.
|
||||
** If NULL, then tracing is turned off.
|
||||
** <li> A prefix string written at the beginning of every
|
||||
** line of trace output. If NULL, then tracing is
|
||||
** turned off.
|
||||
** </ul>
|
||||
**
|
||||
** Outputs:
|
||||
** None.
|
||||
*/
|
||||
void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
|
||||
yyTraceFILE = TraceFILE;
|
||||
yyTracePrompt = zTracePrompt;
|
||||
if( yyTraceFILE==0 ) yyTracePrompt = 0;
|
||||
else if( yyTracePrompt==0 ) yyTraceFILE = 0;
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* For tracing shifts, the names of all terminals and nonterminals
|
||||
** are required. The following table supplies these names */
|
||||
static const char *yyTokenName[] = {
|
||||
%%
|
||||
};
|
||||
#endif /* NDEBUG */
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* For tracing reduce actions, the names of all rules are required.
|
||||
*/
|
||||
static const char *yyRuleName[] = {
|
||||
%%
|
||||
};
|
||||
#endif /* NDEBUG */
|
||||
|
||||
/*
|
||||
** This function returns the symbolic name associated with a token
|
||||
** value.
|
||||
*/
|
||||
const char *ParseTokenName(int tokenType){
|
||||
#ifndef NDEBUG
|
||||
if( tokenType>0 && tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){
|
||||
return yyTokenName[tokenType];
|
||||
}else{
|
||||
return "Unknown";
|
||||
}
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** This function allocates a new parser.
|
||||
** The only argument is a pointer to a function which works like
|
||||
** malloc.
|
||||
**
|
||||
** Inputs:
|
||||
** A pointer to the function used to allocate memory.
|
||||
**
|
||||
** Outputs:
|
||||
** A pointer to a parser. This pointer is used in subsequent calls
|
||||
** to Parse and ParseFree.
|
||||
*/
|
||||
void *ParseAlloc(void *(*mallocProc)(size_t)){
|
||||
yyParser *pParser;
|
||||
pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
|
||||
if( pParser ){
|
||||
pParser->yyidx = -1;
|
||||
}
|
||||
return pParser;
|
||||
}
|
||||
|
||||
/* The following function deletes the value associated with a
|
||||
** symbol. The symbol can be either a terminal or nonterminal.
|
||||
** "yymajor" is the symbol code, and "yypminor" is a pointer to
|
||||
** the value.
|
||||
*/
|
||||
static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
|
||||
switch( yymajor ){
|
||||
/* Here is inserted the actions which take place when a
|
||||
** terminal or non-terminal is destroyed. This can happen
|
||||
** when the symbol is popped from the stack during a
|
||||
** reduce or during error processing or when a parser is
|
||||
** being destroyed before it is finished parsing.
|
||||
**
|
||||
** Note: during a reduce, the only symbols destroyed are those
|
||||
** which appear on the RHS of the rule, but which are not used
|
||||
** inside the C code.
|
||||
*/
|
||||
%%
|
||||
default: break; /* If no destructor action specified: do nothing */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Pop the parser's stack once.
|
||||
**
|
||||
** If there is a destructor routine associated with the token which
|
||||
** is popped from the stack, then call it.
|
||||
**
|
||||
** Return the major token number for the symbol popped.
|
||||
*/
|
||||
static int yy_pop_parser_stack(yyParser *pParser){
|
||||
YYCODETYPE yymajor;
|
||||
yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
|
||||
|
||||
if( pParser->yyidx<0 ) return 0;
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE && pParser->yyidx>=0 ){
|
||||
fprintf(yyTraceFILE,"%sPopping %s\n",
|
||||
yyTracePrompt,
|
||||
yyTokenName[yytos->major]);
|
||||
}
|
||||
#endif
|
||||
yymajor = yytos->major;
|
||||
yy_destructor( yymajor, &yytos->minor);
|
||||
pParser->yyidx--;
|
||||
return yymajor;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate and destroy a parser. Destructors are all called for
|
||||
** all stack elements before shutting the parser down.
|
||||
**
|
||||
** Inputs:
|
||||
** <ul>
|
||||
** <li> A pointer to the parser. This should be a pointer
|
||||
** obtained from ParseAlloc.
|
||||
** <li> A pointer to a function used to reclaim memory obtained
|
||||
** from malloc.
|
||||
** </ul>
|
||||
*/
|
||||
void ParseFree(
|
||||
void *p, /* The parser to be deleted */
|
||||
void (*freeProc)(void*) /* Function used to reclaim memory */
|
||||
){
|
||||
yyParser *pParser = (yyParser*)p;
|
||||
if( pParser==0 ) return;
|
||||
while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
|
||||
(*freeProc)((void*)pParser);
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the appropriate action for a parser given the terminal
|
||||
** look-ahead token iLookAhead.
|
||||
**
|
||||
** If the look-ahead token is YYNOCODE, then check to see if the action is
|
||||
** independent of the look-ahead. If it is, return the action, otherwise
|
||||
** return YY_NO_ACTION.
|
||||
*/
|
||||
static int yy_find_shift_action(
|
||||
yyParser *pParser, /* The parser */
|
||||
int iLookAhead /* The look-ahead token */
|
||||
){
|
||||
int i;
|
||||
int stateno = pParser->yystack[pParser->yyidx].stateno;
|
||||
|
||||
/* if( pParser->yyidx<0 ) return YY_NO_ACTION; */
|
||||
i = yy_shift_ofst[stateno];
|
||||
if( i==YY_SHIFT_USE_DFLT ){
|
||||
return yy_default[stateno];
|
||||
}
|
||||
if( iLookAhead==YYNOCODE ){
|
||||
return YY_NO_ACTION;
|
||||
}
|
||||
i += iLookAhead;
|
||||
if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
|
||||
#ifdef YYFALLBACK
|
||||
int iFallback; /* Fallback token */
|
||||
if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
|
||||
&& (iFallback = yyFallback[iLookAhead])!=0 ){
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
|
||||
yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
|
||||
}
|
||||
#endif
|
||||
return yy_find_shift_action(pParser, iFallback);
|
||||
}
|
||||
#endif
|
||||
return yy_default[stateno];
|
||||
}else{
|
||||
return yy_action[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the appropriate action for a parser given the non-terminal
|
||||
** look-ahead token iLookAhead.
|
||||
**
|
||||
** If the look-ahead token is YYNOCODE, then check to see if the action is
|
||||
** independent of the look-ahead. If it is, return the action, otherwise
|
||||
** return YY_NO_ACTION.
|
||||
*/
|
||||
static int yy_find_reduce_action(
|
||||
yyParser *pParser, /* The parser */
|
||||
int iLookAhead /* The look-ahead token */
|
||||
){
|
||||
int i;
|
||||
int stateno = pParser->yystack[pParser->yyidx].stateno;
|
||||
|
||||
i = yy_reduce_ofst[stateno];
|
||||
if( i==YY_REDUCE_USE_DFLT ){
|
||||
return yy_default[stateno];
|
||||
}
|
||||
if( iLookAhead==YYNOCODE ){
|
||||
return YY_NO_ACTION;
|
||||
}
|
||||
i += iLookAhead;
|
||||
if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
|
||||
return yy_default[stateno];
|
||||
}else{
|
||||
return yy_action[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Perform a shift action.
|
||||
*/
|
||||
static void yy_shift(
|
||||
yyParser *yypParser, /* The parser to be shifted */
|
||||
int yyNewState, /* The new state to shift in */
|
||||
int yyMajor, /* The major token to shift in */
|
||||
YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */
|
||||
){
|
||||
yyStackEntry *yytos;
|
||||
yypParser->yyidx++;
|
||||
if( yypParser->yyidx>=YYSTACKDEPTH ){
|
||||
ParseARG_FETCH;
|
||||
yypParser->yyidx--;
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
|
||||
}
|
||||
#endif
|
||||
while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
|
||||
/* Here code is inserted which will execute if the parser
|
||||
** stack every overflows */
|
||||
%%
|
||||
ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
|
||||
return;
|
||||
}
|
||||
yytos = &yypParser->yystack[yypParser->yyidx];
|
||||
yytos->stateno = yyNewState;
|
||||
yytos->major = yyMajor;
|
||||
yytos->minor = *yypMinor;
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE && yypParser->yyidx>0 ){
|
||||
int i;
|
||||
fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
|
||||
fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
|
||||
for(i=1; i<=yypParser->yyidx; i++)
|
||||
fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
|
||||
fprintf(yyTraceFILE,"\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* The following table contains information about every rule that
|
||||
** is used during the reduce.
|
||||
*/
|
||||
static struct {
|
||||
YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
|
||||
unsigned char nrhs; /* Number of right-hand side symbols in the rule */
|
||||
} yyRuleInfo[] = {
|
||||
%%
|
||||
};
|
||||
|
||||
static void yy_accept(yyParser*); /* Forward Declaration */
|
||||
|
||||
/*
|
||||
** Perform a reduce action and the shift that must immediately
|
||||
** follow the reduce.
|
||||
*/
|
||||
static void yy_reduce(
|
||||
yyParser *yypParser, /* The parser */
|
||||
int yyruleno /* Number of the rule by which to reduce */
|
||||
){
|
||||
int yygoto; /* The next state */
|
||||
int yyact; /* The next action */
|
||||
YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
|
||||
yyStackEntry *yymsp; /* The top of the parser's stack */
|
||||
int yysize; /* Amount to pop the stack */
|
||||
ParseARG_FETCH;
|
||||
yymsp = &yypParser->yystack[yypParser->yyidx];
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE && yyruleno>=0
|
||||
&& yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){
|
||||
fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
|
||||
yyRuleName[yyruleno]);
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
|
||||
switch( yyruleno ){
|
||||
/* Beginning here are the reduction cases. A typical example
|
||||
** follows:
|
||||
** case 0:
|
||||
** #line <lineno> <grammarfile>
|
||||
** { ... } // User supplied code
|
||||
** #line <lineno> <thisfile>
|
||||
** break;
|
||||
*/
|
||||
%%
|
||||
};
|
||||
yygoto = yyRuleInfo[yyruleno].lhs;
|
||||
yysize = yyRuleInfo[yyruleno].nrhs;
|
||||
yypParser->yyidx -= yysize;
|
||||
yyact = yy_find_reduce_action(yypParser,yygoto);
|
||||
if( yyact < YYNSTATE ){
|
||||
yy_shift(yypParser,yyact,yygoto,&yygotominor);
|
||||
}else if( yyact == YYNSTATE + YYNRULE + 1 ){
|
||||
yy_accept(yypParser);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The following code executes when the parse fails
|
||||
*/
|
||||
static void yy_parse_failed(
|
||||
yyParser *yypParser /* The parser */
|
||||
){
|
||||
ParseARG_FETCH;
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
|
||||
}
|
||||
#endif
|
||||
while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
|
||||
/* Here code is inserted which will be executed whenever the
|
||||
** parser fails */
|
||||
%%
|
||||
ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
||||
}
|
||||
|
||||
/*
|
||||
** The following code executes when a syntax error first occurs.
|
||||
*/
|
||||
static void yy_syntax_error(
|
||||
yyParser *yypParser, /* The parser */
|
||||
int yymajor, /* The major type of the error token */
|
||||
YYMINORTYPE yyminor /* The minor type of the error token */
|
||||
){
|
||||
ParseARG_FETCH;
|
||||
#define TOKEN (yyminor.yy0)
|
||||
%%
|
||||
ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
||||
}
|
||||
|
||||
/*
|
||||
** The following is executed when the parser accepts
|
||||
*/
|
||||
static void yy_accept(
|
||||
yyParser *yypParser /* The parser */
|
||||
){
|
||||
ParseARG_FETCH;
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
|
||||
}
|
||||
#endif
|
||||
while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
|
||||
/* Here code is inserted which will be executed whenever the
|
||||
** parser accepts */
|
||||
%%
|
||||
ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
||||
}
|
||||
|
||||
/* The main parser program.
|
||||
** The first argument is a pointer to a structure obtained from
|
||||
** "ParseAlloc" which describes the current state of the parser.
|
||||
** The second argument is the major token number. The third is
|
||||
** the minor token. The fourth optional argument is whatever the
|
||||
** user wants (and specified in the grammar) and is available for
|
||||
** use by the action routines.
|
||||
**
|
||||
** Inputs:
|
||||
** <ul>
|
||||
** <li> A pointer to the parser (an opaque structure.)
|
||||
** <li> The major token number.
|
||||
** <li> The minor token number.
|
||||
** <li> An option argument of a grammar-specified type.
|
||||
** </ul>
|
||||
**
|
||||
** Outputs:
|
||||
** None.
|
||||
*/
|
||||
void Parse(
|
||||
void *yyp, /* The parser */
|
||||
int yymajor, /* The major token code number */
|
||||
ParseTOKENTYPE yyminor /* The value for the token */
|
||||
ParseARG_PDECL /* Optional %extra_argument parameter */
|
||||
){
|
||||
YYMINORTYPE yyminorunion;
|
||||
int yyact; /* The parser action. */
|
||||
int yyendofinput; /* True if we are at the end of input */
|
||||
int yyerrorhit = 0; /* True if yymajor has invoked an error */
|
||||
yyParser *yypParser; /* The parser */
|
||||
|
||||
/* (re)initialize the parser, if necessary */
|
||||
yypParser = (yyParser*)yyp;
|
||||
if( yypParser->yyidx<0 ){
|
||||
if( yymajor==0 ) return;
|
||||
yypParser->yyidx = 0;
|
||||
yypParser->yyerrcnt = -1;
|
||||
yypParser->yystack[0].stateno = 0;
|
||||
yypParser->yystack[0].major = 0;
|
||||
}
|
||||
yyminorunion.yy0 = yyminor;
|
||||
yyendofinput = (yymajor==0);
|
||||
ParseARG_STORE;
|
||||
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
|
||||
}
|
||||
#endif
|
||||
|
||||
do{
|
||||
yyact = yy_find_shift_action(yypParser,yymajor);
|
||||
if( yyact<YYNSTATE ){
|
||||
yy_shift(yypParser,yyact,yymajor,&yyminorunion);
|
||||
yypParser->yyerrcnt--;
|
||||
if( yyendofinput && yypParser->yyidx>=0 ){
|
||||
yymajor = 0;
|
||||
}else{
|
||||
yymajor = YYNOCODE;
|
||||
}
|
||||
}else if( yyact < YYNSTATE + YYNRULE ){
|
||||
yy_reduce(yypParser,yyact-YYNSTATE);
|
||||
}else if( yyact == YY_ERROR_ACTION ){
|
||||
int yymx;
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
|
||||
}
|
||||
#endif
|
||||
#ifdef YYERRORSYMBOL
|
||||
/* A syntax error has occurred.
|
||||
** The response to an error depends upon whether or not the
|
||||
** grammar defines an error token "ERROR".
|
||||
**
|
||||
** This is what we do if the grammar does define ERROR:
|
||||
**
|
||||
** * Call the %syntax_error function.
|
||||
**
|
||||
** * Begin popping the stack until we enter a state where
|
||||
** it is legal to shift the error symbol, then shift
|
||||
** the error symbol.
|
||||
**
|
||||
** * Set the error count to three.
|
||||
**
|
||||
** * Begin accepting and shifting new tokens. No new error
|
||||
** processing will occur until three tokens have been
|
||||
** shifted successfully.
|
||||
**
|
||||
*/
|
||||
if( yypParser->yyerrcnt<0 ){
|
||||
yy_syntax_error(yypParser,yymajor,yyminorunion);
|
||||
}
|
||||
yymx = yypParser->yystack[yypParser->yyidx].major;
|
||||
if( yymx==YYERRORSYMBOL || yyerrorhit ){
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sDiscard input token %s\n",
|
||||
yyTracePrompt,yyTokenName[yymajor]);
|
||||
}
|
||||
#endif
|
||||
yy_destructor(yymajor,&yyminorunion);
|
||||
yymajor = YYNOCODE;
|
||||
}else{
|
||||
while(
|
||||
yypParser->yyidx >= 0 &&
|
||||
yymx != YYERRORSYMBOL &&
|
||||
(yyact = yy_find_shift_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE
|
||||
){
|
||||
yy_pop_parser_stack(yypParser);
|
||||
}
|
||||
if( yypParser->yyidx < 0 || yymajor==0 ){
|
||||
yy_destructor(yymajor,&yyminorunion);
|
||||
yy_parse_failed(yypParser);
|
||||
yymajor = YYNOCODE;
|
||||
}else if( yymx!=YYERRORSYMBOL ){
|
||||
YYMINORTYPE u2;
|
||||
u2.YYERRSYMDT = 0;
|
||||
yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
|
||||
}
|
||||
}
|
||||
yypParser->yyerrcnt = 3;
|
||||
yyerrorhit = 1;
|
||||
#else /* YYERRORSYMBOL is not defined */
|
||||
/* This is what we do if the grammar does not define ERROR:
|
||||
**
|
||||
** * Report an error message, and throw away the input token.
|
||||
**
|
||||
** * If the input token is $, then fail the parse.
|
||||
**
|
||||
** As before, subsequent error messages are suppressed until
|
||||
** three input tokens have been successfully shifted.
|
||||
*/
|
||||
if( yypParser->yyerrcnt<=0 ){
|
||||
yy_syntax_error(yypParser,yymajor,yyminorunion);
|
||||
}
|
||||
yypParser->yyerrcnt = 3;
|
||||
yy_destructor(yymajor,&yyminorunion);
|
||||
if( yyendofinput ){
|
||||
yy_parse_failed(yypParser);
|
||||
}
|
||||
yymajor = YYNOCODE;
|
||||
#endif
|
||||
}else{
|
||||
yy_accept(yypParser);
|
||||
yymajor = YYNOCODE;
|
||||
}
|
||||
}while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
|
||||
return;
|
||||
}
|
1621
php/r3/annotation/parser.c
Normal file
1621
php/r3/annotation/parser.c
Normal file
File diff suppressed because it is too large
Load diff
17
php/r3/annotation/parser.h
Normal file
17
php/r3/annotation/parser.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#define PHANNOT_COMMA 1
|
||||
#define PHANNOT_AT 2
|
||||
#define PHANNOT_IDENTIFIER 3
|
||||
#define PHANNOT_PARENTHESES_OPEN 4
|
||||
#define PHANNOT_PARENTHESES_CLOSE 5
|
||||
#define PHANNOT_STRING 6
|
||||
#define PHANNOT_EQUALS 7
|
||||
#define PHANNOT_COLON 8
|
||||
#define PHANNOT_INTEGER 9
|
||||
#define PHANNOT_DOUBLE 10
|
||||
#define PHANNOT_NULL 11
|
||||
#define PHANNOT_FALSE 12
|
||||
#define PHANNOT_TRUE 13
|
||||
#define PHANNOT_BRACKET_OPEN 14
|
||||
#define PHANNOT_BRACKET_CLOSE 15
|
||||
#define PHANNOT_SBRACKET_OPEN 16
|
||||
#define PHANNOT_SBRACKET_CLOSE 17
|
335
php/r3/annotation/parser.lemon
Normal file
335
php/r3/annotation/parser.lemon
Normal file
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
+------------------------------------------------------------------------+
|
||||
| Phalcon Framework |
|
||||
+------------------------------------------------------------------------+
|
||||
| Copyright (c) 2011-2014 Phalcon Team (http://www.phalconphp.com) |
|
||||
+------------------------------------------------------------------------+
|
||||
| This source file is subject to the New BSD License that is bundled |
|
||||
| with this package in the file docs/LICENSE.txt. |
|
||||
| |
|
||||
| If you did not receive a copy of the license and are unable to |
|
||||
| obtain it through the world-wide-web, please send an email |
|
||||
| to license@phalconphp.com so we can send you a copy immediately. |
|
||||
+------------------------------------------------------------------------+
|
||||
| Authors: Andres Gutierrez <andres@phalconphp.com> |
|
||||
| Eduar Carvajal <eduar@phalconphp.com> |
|
||||
+------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
%token_prefix PHANNOT_
|
||||
%token_type {phannot_parser_token*}
|
||||
%default_type {zval*}
|
||||
%extra_argument {phannot_parser_status *status}
|
||||
%name phannot_
|
||||
|
||||
%left COMMA .
|
||||
|
||||
%include {
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "php.h"
|
||||
#include "ext/standard/php_smart_str.h"
|
||||
#include "Zend/zend_exceptions.h"
|
||||
|
||||
#include "parser.h"
|
||||
#include "scanner.h"
|
||||
#include "annot.h"
|
||||
|
||||
static zval *phannot_ret_literal_zval(int type, phannot_parser_token *T)
|
||||
{
|
||||
zval *ret;
|
||||
|
||||
MAKE_STD_ZVAL(ret);
|
||||
array_init(ret);
|
||||
add_assoc_long(ret, "type", type);
|
||||
if (T) {
|
||||
add_assoc_stringl(ret, "value", T->token, T->token_len, 0);
|
||||
efree(T);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static zval *phannot_ret_array(zval *items)
|
||||
{
|
||||
zval *ret;
|
||||
|
||||
MAKE_STD_ZVAL(ret);
|
||||
array_init(ret);
|
||||
add_assoc_long(ret, "type", PHANNOT_T_ARRAY);
|
||||
|
||||
if (items) {
|
||||
add_assoc_zval(ret, "items", items);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static zval *phannot_ret_zval_list(zval *list_left, zval *right_list)
|
||||
{
|
||||
|
||||
zval *ret;
|
||||
HashPosition pos;
|
||||
HashTable *list;
|
||||
|
||||
MAKE_STD_ZVAL(ret);
|
||||
array_init(ret);
|
||||
|
||||
if (list_left) {
|
||||
|
||||
list = Z_ARRVAL_P(list_left);
|
||||
if (zend_hash_index_exists(list, 0)) {
|
||||
zend_hash_internal_pointer_reset_ex(list, &pos);
|
||||
for (;; zend_hash_move_forward_ex(list, &pos)) {
|
||||
|
||||
zval ** item;
|
||||
|
||||
if (zend_hash_get_current_data_ex(list, (void**) &item, &pos) == FAILURE) {
|
||||
break;
|
||||
}
|
||||
|
||||
Z_ADDREF_PP(item);
|
||||
add_next_index_zval(ret, *item);
|
||||
|
||||
}
|
||||
zval_ptr_dtor(&list_left);
|
||||
} else {
|
||||
add_next_index_zval(ret, list_left);
|
||||
}
|
||||
}
|
||||
|
||||
add_next_index_zval(ret, right_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static zval *phannot_ret_named_item(phannot_parser_token *name, zval *expr)
|
||||
{
|
||||
zval *ret;
|
||||
|
||||
MAKE_STD_ZVAL(ret);
|
||||
array_init(ret);
|
||||
add_assoc_zval(ret, "expr", expr);
|
||||
if (name != NULL) {
|
||||
add_assoc_stringl(ret, "name", name->token, name->token_len, 0);
|
||||
efree(name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static zval *phannot_ret_annotation(phannot_parser_token *name, zval *arguments, phannot_scanner_state *state)
|
||||
{
|
||||
|
||||
zval *ret;
|
||||
|
||||
MAKE_STD_ZVAL(ret);
|
||||
array_init(ret);
|
||||
|
||||
add_assoc_long(ret, "type", PHANNOT_T_ANNOTATION);
|
||||
|
||||
if (name) {
|
||||
add_assoc_stringl(ret, "name", name->token, name->token_len, 0);
|
||||
efree(name);
|
||||
}
|
||||
|
||||
if (arguments) {
|
||||
add_assoc_zval(ret, "arguments", arguments);
|
||||
}
|
||||
|
||||
Z_ADDREF_P(state->active_file);
|
||||
add_assoc_zval(ret, "file", state->active_file);
|
||||
add_assoc_long(ret, "line", state->active_line);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
%syntax_error {
|
||||
if (status->scanner_state->start_length) {
|
||||
{
|
||||
|
||||
char *token_name = NULL;
|
||||
const phannot_token_names *tokens = phannot_tokens;
|
||||
int token_found = 0;
|
||||
int active_token = status->scanner_state->active_token;
|
||||
int near_length = status->scanner_state->start_length;
|
||||
|
||||
if (active_token) {
|
||||
do {
|
||||
if (tokens->code == active_token) {
|
||||
token_found = 1;
|
||||
token_name = tokens->name;
|
||||
break;
|
||||
}
|
||||
++tokens;
|
||||
} while (tokens[0].code != 0);
|
||||
}
|
||||
|
||||
if (!token_name) {
|
||||
token_found = 0;
|
||||
token_name = estrndup("UNKNOWN", strlen("UNKNOWN"));
|
||||
}
|
||||
|
||||
status->syntax_error_len = 128 + strlen(token_name) + Z_STRLEN_P(status->scanner_state->active_file);
|
||||
status->syntax_error = emalloc(sizeof(char) * status->syntax_error_len);
|
||||
|
||||
if (near_length > 0) {
|
||||
if (status->token->value) {
|
||||
snprintf(status->syntax_error, status->syntax_error_len, "Syntax error, unexpected token %s(%s), near to '%s' in %s on line %d", token_name, status->token->value, status->scanner_state->start, Z_STRVAL_P(status->scanner_state->active_file), status->scanner_state->active_line);
|
||||
} else {
|
||||
snprintf(status->syntax_error, status->syntax_error_len, "Syntax error, unexpected token %s, near to '%s' in %s on line %d", token_name, status->scanner_state->start, Z_STRVAL_P(status->scanner_state->active_file), status->scanner_state->active_line);
|
||||
}
|
||||
} else {
|
||||
if (active_token != PHANNOT_T_IGNORE) {
|
||||
if (status->token->value) {
|
||||
snprintf(status->syntax_error, status->syntax_error_len, "Syntax error, unexpected token %s(%s), at the end of docblock in %s on line %d", token_name, status->token->value, Z_STRVAL_P(status->scanner_state->active_file), status->scanner_state->active_line);
|
||||
} else {
|
||||
snprintf(status->syntax_error, status->syntax_error_len, "Syntax error, unexpected token %s, at the end of docblock in %s on line %d", token_name, Z_STRVAL_P(status->scanner_state->active_file), status->scanner_state->active_line);
|
||||
}
|
||||
} else {
|
||||
snprintf(status->syntax_error, status->syntax_error_len, "Syntax error, unexpected EOF, at the end of docblock in %s on line %d", Z_STRVAL_P(status->scanner_state->active_file), status->scanner_state->active_line);
|
||||
}
|
||||
status->syntax_error[status->syntax_error_len-1] = '\0';
|
||||
}
|
||||
|
||||
if (!token_found) {
|
||||
if (token_name) {
|
||||
efree(token_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
status->syntax_error_len = 48 + Z_STRLEN_P(status->scanner_state->active_file);
|
||||
status->syntax_error = emalloc(sizeof(char) * status->syntax_error_len);
|
||||
sprintf(status->syntax_error, "Syntax error, unexpected EOF in %s", Z_STRVAL_P(status->scanner_state->active_file));
|
||||
}
|
||||
|
||||
status->status = PHANNOT_PARSING_FAILED;
|
||||
}
|
||||
|
||||
%token_destructor {
|
||||
if ($$) {
|
||||
if ($$->free_flag) {
|
||||
efree($$->token);
|
||||
}
|
||||
efree($$);
|
||||
}
|
||||
}
|
||||
|
||||
program ::= annotation_language(Q) . {
|
||||
status->ret = Q;
|
||||
}
|
||||
|
||||
%destructor annotation_language { zval_ptr_dtor(&$$); }
|
||||
|
||||
annotation_language(R) ::= annotation_list(L) . {
|
||||
R = L;
|
||||
}
|
||||
|
||||
%destructor annotation_list { zval_ptr_dtor(&$$); }
|
||||
|
||||
annotation_list(R) ::= annotation_list(L) annotation(S) . {
|
||||
R = phannot_ret_zval_list(L, S);
|
||||
}
|
||||
|
||||
annotation_list(R) ::= annotation(S) . {
|
||||
R = phannot_ret_zval_list(NULL, S);
|
||||
}
|
||||
|
||||
|
||||
%destructor annotation { zval_ptr_dtor(&$$); }
|
||||
|
||||
annotation(R) ::= AT IDENTIFIER(I) PARENTHESES_OPEN argument_list(L) PARENTHESES_CLOSE . {
|
||||
R = phannot_ret_annotation(I, L, status->scanner_state);
|
||||
}
|
||||
|
||||
annotation(R) ::= AT IDENTIFIER(I) PARENTHESES_OPEN PARENTHESES_CLOSE . {
|
||||
R = phannot_ret_annotation(I, NULL, status->scanner_state);
|
||||
}
|
||||
|
||||
annotation(R) ::= AT IDENTIFIER(I) . {
|
||||
R = phannot_ret_annotation(I, NULL, status->scanner_state);
|
||||
}
|
||||
|
||||
%destructor argument_list { zval_ptr_dtor(&$$); }
|
||||
|
||||
argument_list(R) ::= argument_list(L) COMMA argument_item(I) . {
|
||||
R = phannot_ret_zval_list(L, I);
|
||||
}
|
||||
|
||||
argument_list(R) ::= argument_item(I) . {
|
||||
R = phannot_ret_zval_list(NULL, I);
|
||||
}
|
||||
|
||||
%destructor argument_item { zval_ptr_dtor(&$$); }
|
||||
|
||||
argument_item(R) ::= expr(E) . {
|
||||
R = phannot_ret_named_item(NULL, E);
|
||||
}
|
||||
|
||||
argument_item(R) ::= STRING(S) EQUALS expr(E) . {
|
||||
R = phannot_ret_named_item(S, E);
|
||||
}
|
||||
|
||||
argument_item(R) ::= STRING(S) COLON expr(E) . {
|
||||
R = phannot_ret_named_item(S, E);
|
||||
}
|
||||
|
||||
argument_item(R) ::= IDENTIFIER(I) EQUALS expr(E) . {
|
||||
R = phannot_ret_named_item(I, E);
|
||||
}
|
||||
|
||||
argument_item(R) ::= IDENTIFIER(I) COLON expr(E) . {
|
||||
R = phannot_ret_named_item(I, E);
|
||||
}
|
||||
|
||||
%destructor expr { zval_ptr_dtor(&$$); }
|
||||
|
||||
expr(R) ::= annotation(S) . {
|
||||
R = S;
|
||||
}
|
||||
|
||||
expr(R) ::= array(A) . {
|
||||
R = A;
|
||||
}
|
||||
|
||||
expr(R) ::= IDENTIFIER(I) . {
|
||||
R = phannot_ret_literal_zval(PHANNOT_T_IDENTIFIER, I);
|
||||
}
|
||||
|
||||
expr(R) ::= INTEGER(I) . {
|
||||
R = phannot_ret_literal_zval(PHANNOT_T_INTEGER, I);
|
||||
}
|
||||
|
||||
expr(R) ::= STRING(S) . {
|
||||
R = phannot_ret_literal_zval(PHANNOT_T_STRING, S);
|
||||
}
|
||||
|
||||
expr(R) ::= DOUBLE(D) . {
|
||||
R = phannot_ret_literal_zval(PHANNOT_T_DOUBLE, D);
|
||||
}
|
||||
|
||||
expr(R) ::= NULL . {
|
||||
R = phannot_ret_literal_zval(PHANNOT_T_NULL, NULL);
|
||||
}
|
||||
|
||||
expr(R) ::= FALSE . {
|
||||
R = phannot_ret_literal_zval(PHANNOT_T_FALSE, NULL);
|
||||
}
|
||||
|
||||
expr(R) ::= TRUE . {
|
||||
R = phannot_ret_literal_zval(PHANNOT_T_TRUE, NULL);
|
||||
}
|
||||
|
||||
array(R) ::= BRACKET_OPEN argument_list(A) BRACKET_CLOSE . {
|
||||
R = phannot_ret_array(A);
|
||||
}
|
||||
|
||||
array(R) ::= SBRACKET_OPEN argument_list(A) SBRACKET_CLOSE . {
|
||||
R = phannot_ret_array(A);
|
||||
}
|
478
php/r3/annotation/parser.out
Normal file
478
php/r3/annotation/parser.out
Normal file
|
@ -0,0 +1,478 @@
|
|||
State 0:
|
||||
program ::= * annotation_language
|
||||
annotation_language ::= * annotation_list
|
||||
annotation_list ::= * annotation_list annotation
|
||||
annotation_list ::= * annotation
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER
|
||||
|
||||
AT shift 16
|
||||
program accept
|
||||
annotation_language shift 23
|
||||
annotation_list shift 9
|
||||
annotation shift 24
|
||||
|
||||
State 1:
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= AT IDENTIFIER PARENTHESES_OPEN * argument_list PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= AT IDENTIFIER PARENTHESES_OPEN * PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER
|
||||
argument_list ::= * argument_list COMMA argument_item
|
||||
argument_list ::= * argument_item
|
||||
argument_item ::= * expr
|
||||
argument_item ::= * STRING EQUALS expr
|
||||
argument_item ::= * STRING COLON expr
|
||||
argument_item ::= * IDENTIFIER EQUALS expr
|
||||
argument_item ::= * IDENTIFIER COLON expr
|
||||
expr ::= * annotation
|
||||
expr ::= * array
|
||||
expr ::= * IDENTIFIER
|
||||
expr ::= * INTEGER
|
||||
expr ::= * STRING
|
||||
expr ::= * DOUBLE
|
||||
expr ::= * NULL
|
||||
expr ::= * FALSE
|
||||
expr ::= * TRUE
|
||||
array ::= * BRACKET_OPEN argument_list BRACKET_CLOSE
|
||||
array ::= * SBRACKET_OPEN argument_list SBRACKET_CLOSE
|
||||
|
||||
AT shift 16
|
||||
IDENTIFIER shift 12
|
||||
PARENTHESES_CLOSE shift 22
|
||||
STRING shift 14
|
||||
INTEGER shift 33
|
||||
DOUBLE shift 35
|
||||
NULL shift 36
|
||||
FALSE shift 37
|
||||
TRUE shift 38
|
||||
BRACKET_OPEN shift 2
|
||||
SBRACKET_OPEN shift 3
|
||||
annotation shift 30
|
||||
argument_list shift 10
|
||||
argument_item shift 17
|
||||
expr shift 28
|
||||
array shift 31
|
||||
|
||||
State 2:
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER
|
||||
argument_list ::= * argument_list COMMA argument_item
|
||||
argument_list ::= * argument_item
|
||||
argument_item ::= * expr
|
||||
argument_item ::= * STRING EQUALS expr
|
||||
argument_item ::= * STRING COLON expr
|
||||
argument_item ::= * IDENTIFIER EQUALS expr
|
||||
argument_item ::= * IDENTIFIER COLON expr
|
||||
expr ::= * annotation
|
||||
expr ::= * array
|
||||
expr ::= * IDENTIFIER
|
||||
expr ::= * INTEGER
|
||||
expr ::= * STRING
|
||||
expr ::= * DOUBLE
|
||||
expr ::= * NULL
|
||||
expr ::= * FALSE
|
||||
expr ::= * TRUE
|
||||
array ::= * BRACKET_OPEN argument_list BRACKET_CLOSE
|
||||
array ::= BRACKET_OPEN * argument_list BRACKET_CLOSE
|
||||
array ::= * SBRACKET_OPEN argument_list SBRACKET_CLOSE
|
||||
|
||||
AT shift 16
|
||||
IDENTIFIER shift 12
|
||||
STRING shift 14
|
||||
INTEGER shift 33
|
||||
DOUBLE shift 35
|
||||
NULL shift 36
|
||||
FALSE shift 37
|
||||
TRUE shift 38
|
||||
BRACKET_OPEN shift 2
|
||||
SBRACKET_OPEN shift 3
|
||||
annotation shift 30
|
||||
argument_list shift 11
|
||||
argument_item shift 17
|
||||
expr shift 28
|
||||
array shift 31
|
||||
|
||||
State 3:
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER
|
||||
argument_list ::= * argument_list COMMA argument_item
|
||||
argument_list ::= * argument_item
|
||||
argument_item ::= * expr
|
||||
argument_item ::= * STRING EQUALS expr
|
||||
argument_item ::= * STRING COLON expr
|
||||
argument_item ::= * IDENTIFIER EQUALS expr
|
||||
argument_item ::= * IDENTIFIER COLON expr
|
||||
expr ::= * annotation
|
||||
expr ::= * array
|
||||
expr ::= * IDENTIFIER
|
||||
expr ::= * INTEGER
|
||||
expr ::= * STRING
|
||||
expr ::= * DOUBLE
|
||||
expr ::= * NULL
|
||||
expr ::= * FALSE
|
||||
expr ::= * TRUE
|
||||
array ::= * BRACKET_OPEN argument_list BRACKET_CLOSE
|
||||
array ::= * SBRACKET_OPEN argument_list SBRACKET_CLOSE
|
||||
array ::= SBRACKET_OPEN * argument_list SBRACKET_CLOSE
|
||||
|
||||
AT shift 16
|
||||
IDENTIFIER shift 12
|
||||
STRING shift 14
|
||||
INTEGER shift 33
|
||||
DOUBLE shift 35
|
||||
NULL shift 36
|
||||
FALSE shift 37
|
||||
TRUE shift 38
|
||||
BRACKET_OPEN shift 2
|
||||
SBRACKET_OPEN shift 3
|
||||
annotation shift 30
|
||||
argument_list shift 13
|
||||
argument_item shift 17
|
||||
expr shift 28
|
||||
array shift 31
|
||||
|
||||
State 4:
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER
|
||||
argument_list ::= argument_list COMMA * argument_item
|
||||
argument_item ::= * expr
|
||||
argument_item ::= * STRING EQUALS expr
|
||||
argument_item ::= * STRING COLON expr
|
||||
argument_item ::= * IDENTIFIER EQUALS expr
|
||||
argument_item ::= * IDENTIFIER COLON expr
|
||||
expr ::= * annotation
|
||||
expr ::= * array
|
||||
expr ::= * IDENTIFIER
|
||||
expr ::= * INTEGER
|
||||
expr ::= * STRING
|
||||
expr ::= * DOUBLE
|
||||
expr ::= * NULL
|
||||
expr ::= * FALSE
|
||||
expr ::= * TRUE
|
||||
array ::= * BRACKET_OPEN argument_list BRACKET_CLOSE
|
||||
array ::= * SBRACKET_OPEN argument_list SBRACKET_CLOSE
|
||||
|
||||
AT shift 16
|
||||
IDENTIFIER shift 12
|
||||
STRING shift 14
|
||||
INTEGER shift 33
|
||||
DOUBLE shift 35
|
||||
NULL shift 36
|
||||
FALSE shift 37
|
||||
TRUE shift 38
|
||||
BRACKET_OPEN shift 2
|
||||
SBRACKET_OPEN shift 3
|
||||
annotation shift 30
|
||||
argument_item shift 27
|
||||
expr shift 28
|
||||
array shift 31
|
||||
|
||||
State 5:
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER
|
||||
argument_item ::= STRING EQUALS * expr
|
||||
expr ::= * annotation
|
||||
expr ::= * array
|
||||
expr ::= * IDENTIFIER
|
||||
expr ::= * INTEGER
|
||||
expr ::= * STRING
|
||||
expr ::= * DOUBLE
|
||||
expr ::= * NULL
|
||||
expr ::= * FALSE
|
||||
expr ::= * TRUE
|
||||
array ::= * BRACKET_OPEN argument_list BRACKET_CLOSE
|
||||
array ::= * SBRACKET_OPEN argument_list SBRACKET_CLOSE
|
||||
|
||||
AT shift 16
|
||||
IDENTIFIER shift 32
|
||||
STRING shift 34
|
||||
INTEGER shift 33
|
||||
DOUBLE shift 35
|
||||
NULL shift 36
|
||||
FALSE shift 37
|
||||
TRUE shift 38
|
||||
BRACKET_OPEN shift 2
|
||||
SBRACKET_OPEN shift 3
|
||||
annotation shift 30
|
||||
expr shift 29
|
||||
array shift 31
|
||||
|
||||
State 6:
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER
|
||||
argument_item ::= IDENTIFIER EQUALS * expr
|
||||
expr ::= * annotation
|
||||
expr ::= * array
|
||||
expr ::= * IDENTIFIER
|
||||
expr ::= * INTEGER
|
||||
expr ::= * STRING
|
||||
expr ::= * DOUBLE
|
||||
expr ::= * NULL
|
||||
expr ::= * FALSE
|
||||
expr ::= * TRUE
|
||||
array ::= * BRACKET_OPEN argument_list BRACKET_CLOSE
|
||||
array ::= * SBRACKET_OPEN argument_list SBRACKET_CLOSE
|
||||
|
||||
AT shift 16
|
||||
IDENTIFIER shift 32
|
||||
STRING shift 34
|
||||
INTEGER shift 33
|
||||
DOUBLE shift 35
|
||||
NULL shift 36
|
||||
FALSE shift 37
|
||||
TRUE shift 38
|
||||
BRACKET_OPEN shift 2
|
||||
SBRACKET_OPEN shift 3
|
||||
annotation shift 30
|
||||
expr shift 18
|
||||
array shift 31
|
||||
|
||||
State 7:
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER
|
||||
argument_item ::= STRING COLON * expr
|
||||
expr ::= * annotation
|
||||
expr ::= * array
|
||||
expr ::= * IDENTIFIER
|
||||
expr ::= * INTEGER
|
||||
expr ::= * STRING
|
||||
expr ::= * DOUBLE
|
||||
expr ::= * NULL
|
||||
expr ::= * FALSE
|
||||
expr ::= * TRUE
|
||||
array ::= * BRACKET_OPEN argument_list BRACKET_CLOSE
|
||||
array ::= * SBRACKET_OPEN argument_list SBRACKET_CLOSE
|
||||
|
||||
AT shift 16
|
||||
IDENTIFIER shift 32
|
||||
STRING shift 34
|
||||
INTEGER shift 33
|
||||
DOUBLE shift 35
|
||||
NULL shift 36
|
||||
FALSE shift 37
|
||||
TRUE shift 38
|
||||
BRACKET_OPEN shift 2
|
||||
SBRACKET_OPEN shift 3
|
||||
annotation shift 30
|
||||
expr shift 21
|
||||
array shift 31
|
||||
|
||||
State 8:
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER
|
||||
argument_item ::= IDENTIFIER COLON * expr
|
||||
expr ::= * annotation
|
||||
expr ::= * array
|
||||
expr ::= * IDENTIFIER
|
||||
expr ::= * INTEGER
|
||||
expr ::= * STRING
|
||||
expr ::= * DOUBLE
|
||||
expr ::= * NULL
|
||||
expr ::= * FALSE
|
||||
expr ::= * TRUE
|
||||
array ::= * BRACKET_OPEN argument_list BRACKET_CLOSE
|
||||
array ::= * SBRACKET_OPEN argument_list SBRACKET_CLOSE
|
||||
|
||||
AT shift 16
|
||||
IDENTIFIER shift 32
|
||||
STRING shift 34
|
||||
INTEGER shift 33
|
||||
DOUBLE shift 35
|
||||
NULL shift 36
|
||||
FALSE shift 37
|
||||
TRUE shift 38
|
||||
BRACKET_OPEN shift 2
|
||||
SBRACKET_OPEN shift 3
|
||||
annotation shift 30
|
||||
expr shift 20
|
||||
array shift 31
|
||||
|
||||
State 9:
|
||||
(1) annotation_language ::= annotation_list *
|
||||
annotation_list ::= annotation_list * annotation
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= * AT IDENTIFIER
|
||||
|
||||
AT shift 16
|
||||
annotation shift 25
|
||||
{default} reduce 1
|
||||
|
||||
State 10:
|
||||
annotation ::= AT IDENTIFIER PARENTHESES_OPEN argument_list * PARENTHESES_CLOSE
|
||||
argument_list ::= argument_list * COMMA argument_item
|
||||
|
||||
COMMA shift 4
|
||||
PARENTHESES_CLOSE shift 26
|
||||
|
||||
State 11:
|
||||
argument_list ::= argument_list * COMMA argument_item
|
||||
array ::= BRACKET_OPEN argument_list * BRACKET_CLOSE
|
||||
|
||||
COMMA shift 4
|
||||
BRACKET_CLOSE shift 39
|
||||
|
||||
State 12:
|
||||
argument_item ::= IDENTIFIER * EQUALS expr
|
||||
argument_item ::= IDENTIFIER * COLON expr
|
||||
(16) expr ::= IDENTIFIER *
|
||||
|
||||
EQUALS shift 6
|
||||
COLON shift 8
|
||||
{default} reduce 16
|
||||
|
||||
State 13:
|
||||
argument_list ::= argument_list * COMMA argument_item
|
||||
array ::= SBRACKET_OPEN argument_list * SBRACKET_CLOSE
|
||||
|
||||
COMMA shift 4
|
||||
SBRACKET_CLOSE shift 19
|
||||
|
||||
State 14:
|
||||
argument_item ::= STRING * EQUALS expr
|
||||
argument_item ::= STRING * COLON expr
|
||||
(18) expr ::= STRING *
|
||||
|
||||
EQUALS shift 5
|
||||
COLON shift 7
|
||||
{default} reduce 18
|
||||
|
||||
State 15:
|
||||
annotation ::= AT IDENTIFIER * PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= AT IDENTIFIER * PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
(6) annotation ::= AT IDENTIFIER *
|
||||
|
||||
PARENTHESES_OPEN shift 1
|
||||
{default} reduce 6
|
||||
|
||||
State 16:
|
||||
annotation ::= AT * IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE
|
||||
annotation ::= AT * IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE
|
||||
annotation ::= AT * IDENTIFIER
|
||||
|
||||
IDENTIFIER shift 15
|
||||
|
||||
State 17:
|
||||
(8) argument_list ::= argument_item *
|
||||
|
||||
{default} reduce 8
|
||||
|
||||
State 18:
|
||||
(12) argument_item ::= IDENTIFIER EQUALS expr *
|
||||
|
||||
{default} reduce 12
|
||||
|
||||
State 19:
|
||||
(24) array ::= SBRACKET_OPEN argument_list SBRACKET_CLOSE *
|
||||
|
||||
{default} reduce 24
|
||||
|
||||
State 20:
|
||||
(13) argument_item ::= IDENTIFIER COLON expr *
|
||||
|
||||
{default} reduce 13
|
||||
|
||||
State 21:
|
||||
(11) argument_item ::= STRING COLON expr *
|
||||
|
||||
{default} reduce 11
|
||||
|
||||
State 22:
|
||||
(5) annotation ::= AT IDENTIFIER PARENTHESES_OPEN PARENTHESES_CLOSE *
|
||||
|
||||
{default} reduce 5
|
||||
|
||||
State 23:
|
||||
(0) program ::= annotation_language *
|
||||
|
||||
{default} reduce 0
|
||||
|
||||
State 24:
|
||||
(3) annotation_list ::= annotation *
|
||||
|
||||
{default} reduce 3
|
||||
|
||||
State 25:
|
||||
(2) annotation_list ::= annotation_list annotation *
|
||||
|
||||
{default} reduce 2
|
||||
|
||||
State 26:
|
||||
(4) annotation ::= AT IDENTIFIER PARENTHESES_OPEN argument_list PARENTHESES_CLOSE *
|
||||
|
||||
{default} reduce 4
|
||||
|
||||
State 27:
|
||||
(7) argument_list ::= argument_list COMMA argument_item *
|
||||
|
||||
{default} reduce 7
|
||||
|
||||
State 28:
|
||||
(9) argument_item ::= expr *
|
||||
|
||||
{default} reduce 9
|
||||
|
||||
State 29:
|
||||
(10) argument_item ::= STRING EQUALS expr *
|
||||
|
||||
{default} reduce 10
|
||||
|
||||
State 30:
|
||||
(14) expr ::= annotation *
|
||||
|
||||
{default} reduce 14
|
||||
|
||||
State 31:
|
||||
(15) expr ::= array *
|
||||
|
||||
{default} reduce 15
|
||||
|
||||
State 32:
|
||||
(16) expr ::= IDENTIFIER *
|
||||
|
||||
{default} reduce 16
|
||||
|
||||
State 33:
|
||||
(17) expr ::= INTEGER *
|
||||
|
||||
{default} reduce 17
|
||||
|
||||
State 34:
|
||||
(18) expr ::= STRING *
|
||||
|
||||
{default} reduce 18
|
||||
|
||||
State 35:
|
||||
(19) expr ::= DOUBLE *
|
||||
|
||||
{default} reduce 19
|
||||
|
||||
State 36:
|
||||
(20) expr ::= NULL *
|
||||
|
||||
{default} reduce 20
|
||||
|
||||
State 37:
|
||||
(21) expr ::= FALSE *
|
||||
|
||||
{default} reduce 21
|
||||
|
||||
State 38:
|
||||
(22) expr ::= TRUE *
|
||||
|
||||
{default} reduce 22
|
||||
|
||||
State 39:
|
||||
(23) array ::= BRACKET_OPEN argument_list BRACKET_CLOSE *
|
||||
|
||||
{default} reduce 23
|
||||
|
605
php/r3/annotation/scanner.c
Normal file
605
php/r3/annotation/scanner.c
Normal file
|
@ -0,0 +1,605 @@
|
|||
/* Generated by re2c 0.13.5 on Sun Feb 16 21:59:06 2014 */
|
||||
#line 1 "scanner.re"
|
||||
|
||||
/*
|
||||
+------------------------------------------------------------------------+
|
||||
| Phalcon Framework |
|
||||
+------------------------------------------------------------------------+
|
||||
| Copyright (c) 2011-2014 Phalcon Team (http://www.phalconphp.com) |
|
||||
+------------------------------------------------------------------------+
|
||||
| This source file is subject to the New BSD License that is bundled |
|
||||
| with this package in the file docs/LICENSE.txt. |
|
||||
| |
|
||||
| If you did not receive a copy of the license and are unable to |
|
||||
| obtain it through the world-wide-web, please send an email |
|
||||
| to license@phalconphp.com so we can send you a copy immediately. |
|
||||
+------------------------------------------------------------------------+
|
||||
| Authors: Andres Gutierrez <andres@phalconphp.com> |
|
||||
| Eduar Carvajal <eduar@phalconphp.com> |
|
||||
+------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "php.h"
|
||||
|
||||
#include "scanner.h"
|
||||
|
||||
#define YYCTYPE unsigned char
|
||||
#define YYCURSOR (s->start)
|
||||
#define YYLIMIT (s->end)
|
||||
#define YYMARKER q
|
||||
|
||||
int phannot_get_token(phannot_scanner_state *s, phannot_scanner_token *token) {
|
||||
|
||||
char next, *q = YYCURSOR, *start = YYCURSOR;
|
||||
int status = PHANNOT_SCANNER_RETCODE_IMPOSSIBLE;
|
||||
|
||||
while (PHANNOT_SCANNER_RETCODE_IMPOSSIBLE == status) {
|
||||
|
||||
if (s->mode == PHANNOT_MODE_RAW) {
|
||||
|
||||
if (*YYCURSOR == '\n') {
|
||||
s->active_line++;
|
||||
}
|
||||
|
||||
next = *(YYCURSOR+1);
|
||||
|
||||
if (*YYCURSOR == '\0' || *YYCURSOR == '@') {
|
||||
if ((next >= 'A' && next <= 'Z') || (next >= 'a' && next <= 'z')) {
|
||||
s->mode = PHANNOT_MODE_ANNOTATION;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
++YYCURSOR;
|
||||
token->opcode = PHANNOT_T_IGNORE;
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
#line 65 "scanner.c"
|
||||
{
|
||||
YYCTYPE yych;
|
||||
unsigned int yyaccept = 0;
|
||||
static const unsigned char yybm[] = {
|
||||
0, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 104, 96, 96, 96, 104, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
104, 96, 32, 96, 96, 96, 96, 64,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
240, 240, 240, 240, 240, 240, 240, 240,
|
||||
240, 240, 96, 96, 96, 96, 96, 96,
|
||||
96, 112, 112, 112, 112, 112, 112, 112,
|
||||
112, 112, 112, 112, 112, 112, 112, 112,
|
||||
112, 112, 112, 112, 112, 112, 112, 112,
|
||||
112, 112, 112, 96, 0, 96, 96, 112,
|
||||
96, 112, 112, 112, 112, 112, 112, 112,
|
||||
112, 112, 112, 112, 112, 112, 112, 112,
|
||||
112, 112, 112, 112, 112, 112, 112, 112,
|
||||
112, 112, 112, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
96, 96, 96, 96, 96, 96, 96, 96,
|
||||
};
|
||||
|
||||
yych = *YYCURSOR;
|
||||
switch (yych) {
|
||||
case 0x00: goto yy38;
|
||||
case '\t':
|
||||
case '\r':
|
||||
case ' ': goto yy34;
|
||||
case '\n': goto yy36;
|
||||
case '"': goto yy10;
|
||||
case '\'': goto yy11;
|
||||
case '(': goto yy14;
|
||||
case ')': goto yy16;
|
||||
case ',': goto yy32;
|
||||
case '-': goto yy2;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9': goto yy4;
|
||||
case ':': goto yy30;
|
||||
case '=': goto yy28;
|
||||
case '@': goto yy26;
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'G':
|
||||
case 'H':
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'K':
|
||||
case 'L':
|
||||
case 'M':
|
||||
case 'O':
|
||||
case 'P':
|
||||
case 'Q':
|
||||
case 'R':
|
||||
case 'S':
|
||||
case 'U':
|
||||
case 'V':
|
||||
case 'W':
|
||||
case 'X':
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
case '_':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'g':
|
||||
case 'h':
|
||||
case 'i':
|
||||
case 'j':
|
||||
case 'k':
|
||||
case 'l':
|
||||
case 'm':
|
||||
case 'o':
|
||||
case 'p':
|
||||
case 'q':
|
||||
case 'r':
|
||||
case 's':
|
||||
case 'u':
|
||||
case 'v':
|
||||
case 'w':
|
||||
case 'x':
|
||||
case 'y':
|
||||
case 'z': goto yy13;
|
||||
case 'F':
|
||||
case 'f': goto yy8;
|
||||
case 'N':
|
||||
case 'n': goto yy6;
|
||||
case 'T':
|
||||
case 't': goto yy9;
|
||||
case '[': goto yy22;
|
||||
case '\\': goto yy12;
|
||||
case ']': goto yy24;
|
||||
case '{': goto yy18;
|
||||
case '}': goto yy20;
|
||||
default: goto yy40;
|
||||
}
|
||||
yy2:
|
||||
++YYCURSOR;
|
||||
if (yybm[0+(yych = *YYCURSOR)] & 128) {
|
||||
goto yy71;
|
||||
}
|
||||
yy3:
|
||||
#line 182 "scanner.re"
|
||||
{
|
||||
status = PHANNOT_SCANNER_RETCODE_ERR;
|
||||
break;
|
||||
}
|
||||
#line 201 "scanner.c"
|
||||
yy4:
|
||||
yyaccept = 0;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
goto yy72;
|
||||
yy5:
|
||||
#line 66 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_INTEGER;
|
||||
token->value = estrndup(start, YYCURSOR - start);
|
||||
token->len = YYCURSOR - start;
|
||||
q = YYCURSOR;
|
||||
return 0;
|
||||
}
|
||||
#line 215 "scanner.c"
|
||||
yy6:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == 'U') goto yy66;
|
||||
if (yych == 'u') goto yy66;
|
||||
goto yy44;
|
||||
yy7:
|
||||
#line 108 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_IDENTIFIER;
|
||||
token->value = estrndup(start, YYCURSOR - start);
|
||||
token->len = YYCURSOR - start;
|
||||
q = YYCURSOR;
|
||||
return 0;
|
||||
}
|
||||
#line 231 "scanner.c"
|
||||
yy8:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == 'A') goto yy61;
|
||||
if (yych == 'a') goto yy61;
|
||||
goto yy44;
|
||||
yy9:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == 'R') goto yy57;
|
||||
if (yych == 'r') goto yy57;
|
||||
goto yy44;
|
||||
yy10:
|
||||
yyaccept = 2;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych <= 0x00) goto yy3;
|
||||
goto yy55;
|
||||
yy11:
|
||||
yyaccept = 2;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych <= 0x00) goto yy3;
|
||||
goto yy50;
|
||||
yy12:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych <= '^') {
|
||||
if (yych <= '@') goto yy3;
|
||||
if (yych <= 'Z') goto yy43;
|
||||
goto yy3;
|
||||
} else {
|
||||
if (yych == '`') goto yy3;
|
||||
if (yych <= 'z') goto yy43;
|
||||
goto yy3;
|
||||
}
|
||||
yy13:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
goto yy44;
|
||||
yy14:
|
||||
++YYCURSOR;
|
||||
#line 116 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_PARENTHESES_OPEN;
|
||||
return 0;
|
||||
}
|
||||
#line 276 "scanner.c"
|
||||
yy16:
|
||||
++YYCURSOR;
|
||||
#line 121 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_PARENTHESES_CLOSE;
|
||||
return 0;
|
||||
}
|
||||
#line 284 "scanner.c"
|
||||
yy18:
|
||||
++YYCURSOR;
|
||||
#line 126 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_BRACKET_OPEN;
|
||||
return 0;
|
||||
}
|
||||
#line 292 "scanner.c"
|
||||
yy20:
|
||||
++YYCURSOR;
|
||||
#line 131 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_BRACKET_CLOSE;
|
||||
return 0;
|
||||
}
|
||||
#line 300 "scanner.c"
|
||||
yy22:
|
||||
++YYCURSOR;
|
||||
#line 136 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_SBRACKET_OPEN;
|
||||
return 0;
|
||||
}
|
||||
#line 308 "scanner.c"
|
||||
yy24:
|
||||
++YYCURSOR;
|
||||
#line 141 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_SBRACKET_CLOSE;
|
||||
return 0;
|
||||
}
|
||||
#line 316 "scanner.c"
|
||||
yy26:
|
||||
++YYCURSOR;
|
||||
#line 146 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_AT;
|
||||
return 0;
|
||||
}
|
||||
#line 324 "scanner.c"
|
||||
yy28:
|
||||
++YYCURSOR;
|
||||
#line 151 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_EQUALS;
|
||||
return 0;
|
||||
}
|
||||
#line 332 "scanner.c"
|
||||
yy30:
|
||||
++YYCURSOR;
|
||||
#line 156 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_COLON;
|
||||
return 0;
|
||||
}
|
||||
#line 340 "scanner.c"
|
||||
yy32:
|
||||
++YYCURSOR;
|
||||
#line 161 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_COMMA;
|
||||
return 0;
|
||||
}
|
||||
#line 348 "scanner.c"
|
||||
yy34:
|
||||
++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
goto yy42;
|
||||
yy35:
|
||||
#line 166 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_IGNORE;
|
||||
return 0;
|
||||
}
|
||||
#line 359 "scanner.c"
|
||||
yy36:
|
||||
++YYCURSOR;
|
||||
#line 171 "scanner.re"
|
||||
{
|
||||
s->active_line++;
|
||||
token->opcode = PHANNOT_T_IGNORE;
|
||||
return 0;
|
||||
}
|
||||
#line 368 "scanner.c"
|
||||
yy38:
|
||||
++YYCURSOR;
|
||||
#line 177 "scanner.re"
|
||||
{
|
||||
status = PHANNOT_SCANNER_RETCODE_EOF;
|
||||
break;
|
||||
}
|
||||
#line 376 "scanner.c"
|
||||
yy40:
|
||||
yych = *++YYCURSOR;
|
||||
goto yy3;
|
||||
yy41:
|
||||
++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
yy42:
|
||||
if (yybm[0+yych] & 8) {
|
||||
goto yy41;
|
||||
}
|
||||
goto yy35;
|
||||
yy43:
|
||||
yyaccept = 1;
|
||||
YYMARKER = ++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
yy44:
|
||||
if (yybm[0+yych] & 16) {
|
||||
goto yy43;
|
||||
}
|
||||
if (yych != '\\') goto yy7;
|
||||
yy45:
|
||||
++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
if (yych <= '^') {
|
||||
if (yych <= '@') goto yy46;
|
||||
if (yych <= 'Z') goto yy47;
|
||||
} else {
|
||||
if (yych == '`') goto yy46;
|
||||
if (yych <= 'z') goto yy47;
|
||||
}
|
||||
yy46:
|
||||
YYCURSOR = YYMARKER;
|
||||
if (yyaccept <= 2) {
|
||||
if (yyaccept <= 1) {
|
||||
if (yyaccept <= 0) {
|
||||
goto yy5;
|
||||
} else {
|
||||
goto yy7;
|
||||
}
|
||||
} else {
|
||||
goto yy3;
|
||||
}
|
||||
} else {
|
||||
if (yyaccept <= 4) {
|
||||
if (yyaccept <= 3) {
|
||||
goto yy60;
|
||||
} else {
|
||||
goto yy65;
|
||||
}
|
||||
} else {
|
||||
goto yy69;
|
||||
}
|
||||
}
|
||||
yy47:
|
||||
yyaccept = 1;
|
||||
YYMARKER = ++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
if (yych <= '[') {
|
||||
if (yych <= '9') {
|
||||
if (yych <= '/') goto yy7;
|
||||
goto yy47;
|
||||
} else {
|
||||
if (yych <= '@') goto yy7;
|
||||
if (yych <= 'Z') goto yy47;
|
||||
goto yy7;
|
||||
}
|
||||
} else {
|
||||
if (yych <= '_') {
|
||||
if (yych <= '\\') goto yy45;
|
||||
if (yych <= '^') goto yy7;
|
||||
goto yy47;
|
||||
} else {
|
||||
if (yych <= '`') goto yy7;
|
||||
if (yych <= 'z') goto yy47;
|
||||
goto yy7;
|
||||
}
|
||||
}
|
||||
yy49:
|
||||
++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
yy50:
|
||||
if (yybm[0+yych] & 32) {
|
||||
goto yy49;
|
||||
}
|
||||
if (yych <= 0x00) goto yy46;
|
||||
if (yych <= '[') goto yy52;
|
||||
++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
if (yych == '\n') goto yy46;
|
||||
goto yy49;
|
||||
yy52:
|
||||
++YYCURSOR;
|
||||
#line 99 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_STRING;
|
||||
token->value = estrndup(q, YYCURSOR - q - 1);
|
||||
token->len = YYCURSOR - q - 1;
|
||||
q = YYCURSOR;
|
||||
return 0;
|
||||
}
|
||||
#line 477 "scanner.c"
|
||||
yy54:
|
||||
++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
yy55:
|
||||
if (yybm[0+yych] & 64) {
|
||||
goto yy54;
|
||||
}
|
||||
if (yych <= 0x00) goto yy46;
|
||||
if (yych <= '[') goto yy52;
|
||||
++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
if (yych == '\n') goto yy46;
|
||||
goto yy54;
|
||||
yy57:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == 'U') goto yy58;
|
||||
if (yych != 'u') goto yy44;
|
||||
yy58:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == 'E') goto yy59;
|
||||
if (yych != 'e') goto yy44;
|
||||
yy59:
|
||||
yyaccept = 3;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yybm[0+yych] & 16) {
|
||||
goto yy43;
|
||||
}
|
||||
if (yych == '\\') goto yy45;
|
||||
yy60:
|
||||
#line 93 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_TRUE;
|
||||
return 0;
|
||||
}
|
||||
#line 514 "scanner.c"
|
||||
yy61:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == 'L') goto yy62;
|
||||
if (yych != 'l') goto yy44;
|
||||
yy62:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == 'S') goto yy63;
|
||||
if (yych != 's') goto yy44;
|
||||
yy63:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == 'E') goto yy64;
|
||||
if (yych != 'e') goto yy44;
|
||||
yy64:
|
||||
yyaccept = 4;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yybm[0+yych] & 16) {
|
||||
goto yy43;
|
||||
}
|
||||
if (yych == '\\') goto yy45;
|
||||
yy65:
|
||||
#line 88 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_FALSE;
|
||||
return 0;
|
||||
}
|
||||
#line 543 "scanner.c"
|
||||
yy66:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == 'L') goto yy67;
|
||||
if (yych != 'l') goto yy44;
|
||||
yy67:
|
||||
yyaccept = 1;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == 'L') goto yy68;
|
||||
if (yych != 'l') goto yy44;
|
||||
yy68:
|
||||
yyaccept = 5;
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yybm[0+yych] & 16) {
|
||||
goto yy43;
|
||||
}
|
||||
if (yych == '\\') goto yy45;
|
||||
yy69:
|
||||
#line 83 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_NULL;
|
||||
return 0;
|
||||
}
|
||||
#line 567 "scanner.c"
|
||||
yy70:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych <= '/') goto yy46;
|
||||
if (yych <= '9') goto yy73;
|
||||
goto yy46;
|
||||
yy71:
|
||||
yyaccept = 0;
|
||||
YYMARKER = ++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
yy72:
|
||||
if (yybm[0+yych] & 128) {
|
||||
goto yy71;
|
||||
}
|
||||
if (yych == '.') goto yy70;
|
||||
goto yy5;
|
||||
yy73:
|
||||
++YYCURSOR;
|
||||
yych = *YYCURSOR;
|
||||
if (yych <= '/') goto yy75;
|
||||
if (yych <= '9') goto yy73;
|
||||
yy75:
|
||||
#line 75 "scanner.re"
|
||||
{
|
||||
token->opcode = PHANNOT_T_DOUBLE;
|
||||
token->value = estrndup(start, YYCURSOR - start);
|
||||
token->len = YYCURSOR - start;
|
||||
q = YYCURSOR;
|
||||
return 0;
|
||||
}
|
||||
#line 597 "scanner.c"
|
||||
}
|
||||
#line 187 "scanner.re"
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
83
php/r3/annotation/scanner.h
Normal file
83
php/r3/annotation/scanner.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
|
||||
/*
|
||||
+------------------------------------------------------------------------+
|
||||
| Phalcon Framework |
|
||||
+------------------------------------------------------------------------+
|
||||
| Copyright (c) 2011-2014 Phalcon Team (http://www.phalconphp.com) |
|
||||
+------------------------------------------------------------------------+
|
||||
| This source file is subject to the New BSD License that is bundled |
|
||||
| with this package in the file docs/LICENSE.txt. |
|
||||
| |
|
||||
| If you did not receive a copy of the license and are unable to |
|
||||
| obtain it through the world-wide-web, please send an email |
|
||||
| to license@phalconphp.com so we can send you a copy immediately. |
|
||||
+------------------------------------------------------------------------+
|
||||
| Authors: Andres Gutierrez <andres@phalconphp.com> |
|
||||
| Eduar Carvajal <eduar@phalconphp.com> |
|
||||
+------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#define PHANNOT_SCANNER_RETCODE_EOF -1
|
||||
#define PHANNOT_SCANNER_RETCODE_ERR -2
|
||||
#define PHANNOT_SCANNER_RETCODE_IMPOSSIBLE -3
|
||||
|
||||
/** Modes */
|
||||
#define PHANNOT_MODE_RAW 0
|
||||
#define PHANNOT_MODE_ANNOTATION 1
|
||||
|
||||
#define PHANNOT_T_IGNORE 297
|
||||
|
||||
#define PHANNOT_T_DOCBLOCK_ANNOTATION 299
|
||||
#define PHANNOT_T_ANNOTATION 300
|
||||
|
||||
/* Literals & Identifiers */
|
||||
#define PHANNOT_T_INTEGER 301
|
||||
#define PHANNOT_T_DOUBLE 302
|
||||
#define PHANNOT_T_STRING 303
|
||||
#define PHANNOT_T_NULL 304
|
||||
#define PHANNOT_T_FALSE 305
|
||||
#define PHANNOT_T_TRUE 306
|
||||
#define PHANNOT_T_IDENTIFIER 307
|
||||
#define PHANNOT_T_ARRAY 308
|
||||
#define PHANNOT_T_ARBITRARY_TEXT 309
|
||||
|
||||
/* Operators */
|
||||
#define PHANNOT_T_AT '@'
|
||||
#define PHANNOT_T_DOT '.'
|
||||
#define PHANNOT_T_COMMA ','
|
||||
#define PHANNOT_T_EQUALS '='
|
||||
#define PHANNOT_T_COLON ':'
|
||||
#define PHANNOT_T_BRACKET_OPEN '{'
|
||||
#define PHANNOT_T_BRACKET_CLOSE '}'
|
||||
#define PHANNOT_T_SBRACKET_OPEN '['
|
||||
#define PHANNOT_T_SBRACKET_CLOSE ']'
|
||||
#define PHANNOT_T_PARENTHESES_OPEN '('
|
||||
#define PHANNOT_T_PARENTHESES_CLOSE ')'
|
||||
|
||||
/* List of tokens and their names */
|
||||
typedef struct _phannot_token_names {
|
||||
char *name;
|
||||
unsigned int code;
|
||||
} phannot_token_names;
|
||||
|
||||
/* Active token state */
|
||||
typedef struct _phannot_scanner_state {
|
||||
char* start;
|
||||
char* end;
|
||||
int active_token;
|
||||
unsigned int start_length;
|
||||
int mode;
|
||||
unsigned int active_line;
|
||||
zval *active_file;
|
||||
} phannot_scanner_state;
|
||||
|
||||
/* Extra information tokens */
|
||||
typedef struct _phannot_scanner_token {
|
||||
char *value;
|
||||
int opcode;
|
||||
int len;
|
||||
} phannot_scanner_token;
|
||||
|
||||
int phannot_get_token(phannot_scanner_state *s, phannot_scanner_token *token);
|
||||
|
||||
extern const phannot_token_names phannot_tokens[];
|
193
php/r3/annotation/scanner.re
Normal file
193
php/r3/annotation/scanner.re
Normal file
|
@ -0,0 +1,193 @@
|
|||
|
||||
/*
|
||||
+------------------------------------------------------------------------+
|
||||
| Phalcon Framework |
|
||||
+------------------------------------------------------------------------+
|
||||
| Copyright (c) 2011-2014 Phalcon Team (http://www.phalconphp.com) |
|
||||
+------------------------------------------------------------------------+
|
||||
| This source file is subject to the New BSD License that is bundled |
|
||||
| with this package in the file docs/LICENSE.txt. |
|
||||
| |
|
||||
| If you did not receive a copy of the license and are unable to |
|
||||
| obtain it through the world-wide-web, please send an email |
|
||||
| to license@phalconphp.com so we can send you a copy immediately. |
|
||||
+------------------------------------------------------------------------+
|
||||
| Authors: Andres Gutierrez <andres@phalconphp.com> |
|
||||
| Eduar Carvajal <eduar@phalconphp.com> |
|
||||
+------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "php.h"
|
||||
|
||||
#include "scanner.h"
|
||||
|
||||
#define YYCTYPE unsigned char
|
||||
#define YYCURSOR (s->start)
|
||||
#define YYLIMIT (s->end)
|
||||
#define YYMARKER q
|
||||
|
||||
int phannot_get_token(phannot_scanner_state *s, phannot_scanner_token *token) {
|
||||
|
||||
char next, *q = YYCURSOR, *start = YYCURSOR;
|
||||
int status = PHANNOT_SCANNER_RETCODE_IMPOSSIBLE;
|
||||
|
||||
while (PHANNOT_SCANNER_RETCODE_IMPOSSIBLE == status) {
|
||||
|
||||
if (s->mode == PHANNOT_MODE_RAW) {
|
||||
|
||||
if (*YYCURSOR == '\n') {
|
||||
s->active_line++;
|
||||
}
|
||||
|
||||
next = *(YYCURSOR+1);
|
||||
|
||||
if (*YYCURSOR == '\0' || *YYCURSOR == '@') {
|
||||
if ((next >= 'A' && next <= 'Z') || (next >= 'a' && next <= 'z')) {
|
||||
s->mode = PHANNOT_MODE_ANNOTATION;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
++YYCURSOR;
|
||||
token->opcode = PHANNOT_T_IGNORE;
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
/*!re2c
|
||||
re2c:indent:top = 2;
|
||||
re2c:yyfill:enable = 0;
|
||||
|
||||
INTEGER = [\-]?[0-9]+;
|
||||
INTEGER {
|
||||
token->opcode = PHANNOT_T_INTEGER;
|
||||
token->value = estrndup(start, YYCURSOR - start);
|
||||
token->len = YYCURSOR - start;
|
||||
q = YYCURSOR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DOUBLE = ([\-]?[0-9]+[\.][0-9]+);
|
||||
DOUBLE {
|
||||
token->opcode = PHANNOT_T_DOUBLE;
|
||||
token->value = estrndup(start, YYCURSOR - start);
|
||||
token->len = YYCURSOR - start;
|
||||
q = YYCURSOR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
'null' {
|
||||
token->opcode = PHANNOT_T_NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
'false' {
|
||||
token->opcode = PHANNOT_T_FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
'true' {
|
||||
token->opcode = PHANNOT_T_TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
STRING = (["] ([\\]["]|[\\].|[\001-\377]\[\\"])* ["])|(['] ([\\][']|[\\].|[\001-\377]\[\\'])* [']);
|
||||
STRING {
|
||||
token->opcode = PHANNOT_T_STRING;
|
||||
token->value = estrndup(q, YYCURSOR - q - 1);
|
||||
token->len = YYCURSOR - q - 1;
|
||||
q = YYCURSOR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
IDENTIFIER = ('\x5C'?[a-zA-Z_]([a-zA-Z0-9_]*)('\x5C'[a-zA-Z_]([a-zA-Z0-9_]*))*);
|
||||
IDENTIFIER {
|
||||
token->opcode = PHANNOT_T_IDENTIFIER;
|
||||
token->value = estrndup(start, YYCURSOR - start);
|
||||
token->len = YYCURSOR - start;
|
||||
q = YYCURSOR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
"(" {
|
||||
token->opcode = PHANNOT_T_PARENTHESES_OPEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
")" {
|
||||
token->opcode = PHANNOT_T_PARENTHESES_CLOSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
"{" {
|
||||
token->opcode = PHANNOT_T_BRACKET_OPEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
"}" {
|
||||
token->opcode = PHANNOT_T_BRACKET_CLOSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
"[" {
|
||||
token->opcode = PHANNOT_T_SBRACKET_OPEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
"]" {
|
||||
token->opcode = PHANNOT_T_SBRACKET_CLOSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
"@" {
|
||||
token->opcode = PHANNOT_T_AT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
"=" {
|
||||
token->opcode = PHANNOT_T_EQUALS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
":" {
|
||||
token->opcode = PHANNOT_T_COLON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
"," {
|
||||
token->opcode = PHANNOT_T_COMMA;
|
||||
return 0;
|
||||
}
|
||||
|
||||
[ \t\r]+ {
|
||||
token->opcode = PHANNOT_T_IGNORE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
[\n] {
|
||||
s->active_line++;
|
||||
token->opcode = PHANNOT_T_IGNORE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
"\000" {
|
||||
status = PHANNOT_SCANNER_RETCODE_EOF;
|
||||
break;
|
||||
}
|
||||
|
||||
[^] {
|
||||
status = PHANNOT_SCANNER_RETCODE_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
50
php/r3/config.m4
Normal file
50
php/r3/config.m4
Normal file
|
@ -0,0 +1,50 @@
|
|||
dnl config.m4 for extension r3
|
||||
|
||||
PHP_ARG_WITH(r3, for r3 support,
|
||||
[ --with-r3 Include r3 support])
|
||||
|
||||
dnl PHP_ARG_ENABLE(r3, whether to enable r3 support,
|
||||
dnl Make sure that the comment is aligned:
|
||||
dnl [ --enable-r3 Enable r3 support])
|
||||
|
||||
if test "$PHP_R3" != "no"; then
|
||||
SEARCH_PATH="/usr/local /usr"
|
||||
SEARCH_FOR="/include/r3/r3.h"
|
||||
if test -r $PHP_R3/$SEARCH_FOR; then
|
||||
R3_DIR=$PHP_R3
|
||||
else
|
||||
AC_MSG_CHECKING([for r3 files in default path])
|
||||
for i in $SEARCH_PATH ; do
|
||||
if test -r $i/$SEARCH_FOR; then
|
||||
R3_DIR=$i
|
||||
AC_MSG_RESULT(found in $i)
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if test -z "$R3_DIR"; then
|
||||
AC_MSG_RESULT([not found])
|
||||
AC_MSG_ERROR([Please reinstall the r3 distribution])
|
||||
fi
|
||||
|
||||
echo $R3_DIR
|
||||
dnl # --with-r3 -> add include path
|
||||
PHP_ADD_INCLUDE($R3_DIR/include)
|
||||
|
||||
LIBNAME=r3
|
||||
LIBSYMBOL=r3_route_create
|
||||
|
||||
PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
|
||||
[
|
||||
PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $R3_DIR/lib, R3_SHARED_LIBADD)
|
||||
AC_DEFINE(HAVE_R3LIB,1,[ ])
|
||||
],[
|
||||
AC_MSG_ERROR([wrong r3 lib version or lib not found])
|
||||
],[
|
||||
-L$R3_DIR/lib -lm
|
||||
])
|
||||
|
||||
PHP_SUBST(R3_SHARED_LIBADD)
|
||||
|
||||
PHP_NEW_EXTENSION(r3, [ct_helper.c hash.c php_expandable_mux.c php_r3.c r3_controller.c r3_functions.c r3_mux.c r3_persistent.c], $ext_shared)
|
||||
fi
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include "php_r3.h"
|
||||
// #include "ct_helper.h"
|
||||
// #include "r3_functions.h"
|
||||
#include "r3_functions.h"
|
||||
// #include "r3_mux.h"
|
||||
// #include "php_expandable_mux.h"
|
||||
// #include "r3_controller.h"
|
||||
|
@ -104,33 +104,3 @@ PHP_MSHUTDOWN_FUNCTION(r3) {
|
|||
PHP_RINIT_FUNCTION(r3) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* r3_compile(array $routes, string $path);
|
||||
*/
|
||||
PHP_FUNCTION(r3_match)
|
||||
{
|
||||
zval *z_routes;
|
||||
char *path;
|
||||
int path_len;
|
||||
|
||||
/* parse parameters */
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "as",
|
||||
&z_routes,
|
||||
&path, &path_len ) == FAILURE) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
zval *z_route;
|
||||
z_route = php_r3_match(z_routes, path, path_len TSRMLS_CC);
|
||||
if ( z_route != NULL ) {
|
||||
*return_value = *z_route;
|
||||
zval_copy_ctor(return_value);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
RETURN_NULL();
|
||||
}
|
||||
|
||||
|
|
|
@ -82,8 +82,8 @@ zval * call_mux_method(zval * object , char * method_name , int method_name_len,
|
|||
|
||||
zend_class_entry ** get_pattern_compiler_ce(TSRMLS_D);
|
||||
|
||||
extern zend_class_entry *ce_r3_exception;
|
||||
*/
|
||||
extern zend_class_entry *ce_r3_exception;
|
||||
|
||||
extern zend_module_entry r3_module_entry;
|
||||
|
||||
|
@ -91,7 +91,7 @@ void r3_init_exception(TSRMLS_D);
|
|||
|
||||
void r3_mux_le_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC);
|
||||
|
||||
PHP_FUNCTION(r3_match);
|
||||
// PHP_FUNCTION(r3_match);
|
||||
|
||||
#define phpext_r3_ptr &r3_module_entry
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ vim:fdm=marker:et:sw=4:ts=4:sts=4:
|
|||
|
||||
// #include "r3_persistent.h"
|
||||
// #include "php_expandable_mux.h"
|
||||
// #include "hash.h"
|
||||
#include "hash.h"
|
||||
|
||||
/**
|
||||
* new_dst = ht_copy_fun_t(NULL, src);
|
||||
|
@ -407,8 +407,8 @@ PHP_FUNCTION(r3_match)
|
|||
}
|
||||
|
||||
zval *z_route;
|
||||
z_route = php_r3_match(z_routes, path, path_len TSRMLS_CC);
|
||||
if ( z_route != NULL ) {
|
||||
z_R3Route = php_r3_match(z_routes, path, path_len TSRMLS_CC);
|
||||
if ( z_R3Route != NULL ) {
|
||||
*return_value = *z_route;
|
||||
zval_copy_ctor(return_value);
|
||||
return;
|
||||
|
@ -438,7 +438,7 @@ PHP_FUNCTION(r3_persistent_dispatch)
|
|||
char *ns, *filename, *path;
|
||||
int ns_len, filename_len, path_len;
|
||||
zval *mux = NULL;
|
||||
zval *route = NULL;
|
||||
zval *R3Route = NULL;
|
||||
zval *z_path = NULL;
|
||||
|
||||
/* parse parameters */
|
||||
|
@ -463,15 +463,15 @@ PHP_FUNCTION(r3_persistent_dispatch)
|
|||
ZVAL_STRINGL(z_path, path ,path_len, 1); // no copy
|
||||
|
||||
// XXX: pass return_value to the method call, so we don't need to copy
|
||||
route = call_mux_method(mux, "dispatch" , sizeof("dispatch"), 1 , z_path, NULL, NULL TSRMLS_CC);
|
||||
R3Route = call_mux_method(mux, "dispatch" , sizeof("dispatch"), 1 , z_path, NULL, NULL TSRMLS_CC);
|
||||
zval_ptr_dtor(&z_path);
|
||||
|
||||
if ( route ) {
|
||||
if ( R3Route ) {
|
||||
*return_value = *route;
|
||||
zval_copy_ctor(return_value);
|
||||
return;
|
||||
}
|
||||
// route not found
|
||||
// R3Route not found
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
|
@ -649,7 +649,7 @@ inline zval * php_r3_match(zval *z_routes, char *path, int path_len TSRMLS_DC) {
|
|||
|
||||
// tell garbage collector to collect it, we need to use pcre_subpats later.
|
||||
|
||||
// check conditions only when route option is provided
|
||||
// check conditions only when R3Route option is provided
|
||||
if ( zend_hash_index_find( Z_ARRVAL_PP(z_route_pp), 3, (void**) &z_route_options_pp) == SUCCESS ) {
|
||||
if ( zend_hash_num_elements(Z_ARRVAL_PP(z_route_options_pp)) ) {
|
||||
if ( 0 == validate_request_method( z_route_options_pp, current_request_method TSRMLS_CC) ) {
|
||||
|
|
|
@ -81,7 +81,7 @@ zend_class_entry ** get_pattern_compiler_ce(TSRMLS_D) {
|
|||
return ce_pattern_compiler;
|
||||
}
|
||||
|
||||
// Returns compiled route zval
|
||||
// Returns compiled R3Route zval
|
||||
zval * compile_route_pattern(zval *z_pattern, zval *z_options, zend_class_entry **ce_pattern_compiler TSRMLS_DC)
|
||||
{
|
||||
// zend_class_entry **ce_pattern_compiler;
|
||||
|
@ -92,10 +92,10 @@ zval * compile_route_pattern(zval *z_pattern, zval *z_options, zend_class_entry
|
|||
}
|
||||
}
|
||||
|
||||
zval *z_compiled_route = NULL; // will be an array
|
||||
zval *z_compiled_R3Route = NULL; // will be an array
|
||||
zend_call_method( NULL, *ce_pattern_compiler, NULL, "compile", strlen("compile"), &z_compiled_route, 2, z_pattern, z_options TSRMLS_CC );
|
||||
|
||||
if ( z_compiled_route == NULL ) {
|
||||
if ( z_compiled_R3Route == NULL ) {
|
||||
return NULL;
|
||||
} else if ( Z_TYPE_P(z_compiled_route) == IS_NULL ) {
|
||||
zval_ptr_dtor(&z_compiled_route);
|
||||
|
@ -467,7 +467,7 @@ PHP_METHOD(Mux, mount) {
|
|||
// zval for new route
|
||||
zval *z_new_routes;
|
||||
|
||||
// zval for route item
|
||||
// zval for R3Route item
|
||||
zval **z_is_pcre; // route[0]
|
||||
zval **z_route_pattern;
|
||||
zval **z_route_callback;
|
||||
|
@ -514,10 +514,10 @@ PHP_METHOD(Mux, mount) {
|
|||
|
||||
// $routeArgs = PatternCompiler::compile($newPattern,
|
||||
// array_merge_recursive($route[3], $options) );
|
||||
zval *z_compiled_route = compile_route_pattern(z_new_pattern, *z_route_options, ce_pattern_compiler TSRMLS_CC);
|
||||
zval *z_compiled_R3Route = compile_route_pattern(z_new_pattern, *z_route_options, ce_pattern_compiler TSRMLS_CC);
|
||||
|
||||
|
||||
if ( z_compiled_route == NULL || Z_TYPE_P(z_compiled_route) == IS_NULL ) {
|
||||
if ( z_compiled_R3Route == NULL || Z_TYPE_P(z_compiled_route) == IS_NULL ) {
|
||||
php_error( E_ERROR, "Cannot compile pattern: %s", new_pattern);
|
||||
}
|
||||
|
||||
|
@ -535,7 +535,7 @@ PHP_METHOD(Mux, mount) {
|
|||
Z_ADDREF_P(z_compiled_route);
|
||||
Z_ADDREF_P(z_new_routes);
|
||||
|
||||
// create new route and append to mux->routes
|
||||
// create new R3Route and append to mux->routes
|
||||
add_index_bool(z_new_routes, 0 , 1); // pcre flag == false
|
||||
add_index_zval(z_new_routes, 1, *z_compiled_route_pattern);
|
||||
add_index_zval(z_new_routes, 2 , *z_route_callback);
|
||||
|
@ -558,7 +558,7 @@ PHP_METHOD(Mux, mount) {
|
|||
|
||||
int new_pattern_len = pattern_len + Z_STRLEN_PP(z_route_pattern);
|
||||
|
||||
// Merge the mount options with the route options
|
||||
// Merge the mount options with the R3Route options
|
||||
zval *z_new_route_options;
|
||||
MAKE_STD_ZVAL(z_new_route_options);
|
||||
array_init(z_new_route_options);
|
||||
|
@ -681,11 +681,11 @@ PHP_METHOD(Mux, getRoute) {
|
|||
}
|
||||
|
||||
zval *z_routes_by_id = NULL;
|
||||
zval **z_route = NULL;
|
||||
zval **z_R3Route = NULL;
|
||||
z_routes_by_id = zend_read_property( ce_r3_mux , this_ptr, "routesById", sizeof("routesById")-1, 1 TSRMLS_CC);
|
||||
|
||||
// php_var_dump(&z_routes_by_id, 1 TSRMLS_CC);
|
||||
if ( zend_hash_find( Z_ARRVAL_P(z_routes_by_id) , route_id, route_id_len + 1, (void**) &z_route ) == SUCCESS ) {
|
||||
if ( zend_hash_find( Z_ARRVAL_P(z_routes_by_id) , route_id, route_id_len + 1, (void**) &z_R3Route ) == SUCCESS ) {
|
||||
*return_value = **z_route;
|
||||
zval_copy_ctor(return_value);
|
||||
return;
|
||||
|
@ -788,7 +788,7 @@ PHP_METHOD(Mux, compile) {
|
|||
zend_call_method( NULL, NULL, NULL, "usort", strlen("usort"), &rv, 2,
|
||||
z_routes, z_sort_callback TSRMLS_CC );
|
||||
zval_ptr_dtor(&z_sort_callback); // recycle sort callback zval
|
||||
// php_error(E_ERROR,"route sort failed.");
|
||||
// php_error(E_ERROR,"R3Route sort failed.");
|
||||
// zend_update_property(ce_r3_mux, getThis(), "routes", sizeof("routes")-1, z_routes TSRMLS_CC);
|
||||
}
|
||||
|
||||
|
@ -843,7 +843,7 @@ PHP_METHOD(Mux, dispatch) {
|
|||
int path_len;
|
||||
zval *z_path;
|
||||
|
||||
zval *z_return_route = NULL;
|
||||
zval *z_return_R3Route = NULL;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &path, &path_len) == FAILURE) {
|
||||
RETURN_FALSE;
|
||||
|
@ -856,7 +856,7 @@ PHP_METHOD(Mux, dispatch) {
|
|||
zend_hash_quick_find( &ce_r3_mux->function_table, "match", sizeof("match"), zend_inline_hash_func(ZEND_STRS("match")), (void **) &fe);
|
||||
zend_call_method( &this_ptr, ce_r3_mux, &fe, "match", strlen("match"), &z_return_route, 1, z_path, NULL TSRMLS_CC );
|
||||
|
||||
if ( ! z_return_route || Z_TYPE_P(z_return_route) == IS_NULL ) {
|
||||
if ( ! z_return_R3Route || Z_TYPE_P(z_return_route) == IS_NULL ) {
|
||||
zval_ptr_dtor(&z_path);
|
||||
RETURN_NULL();
|
||||
}
|
||||
|
@ -908,11 +908,11 @@ PHP_METHOD(Mux, dispatch) {
|
|||
zval *z_substr;
|
||||
|
||||
if ( zend_hash_quick_find( Z_ARRVAL_PP(z_options) , "vars", sizeof("vars"), zend_inline_hash_func(ZEND_STRS("vars")), (void**) &z_route_vars ) == FAILURE ) {
|
||||
php_error(E_ERROR, "require route vars");
|
||||
php_error(E_ERROR, "require R3Route vars");
|
||||
RETURN_FALSE;
|
||||
}
|
||||
if ( zend_hash_index_find( Z_ARRVAL_PP(z_options) , 0 , (void**) &z_route_vars_0 ) == FAILURE ) {
|
||||
php_error(E_ERROR, "require route vars[0]");
|
||||
php_error(E_ERROR, "require R3Route vars[0]");
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
|
@ -955,7 +955,7 @@ PHP_METHOD(Mux, dispatch) {
|
|||
}
|
||||
}
|
||||
|
||||
if ( z_return_route ) {
|
||||
if ( z_return_R3Route ) {
|
||||
*return_value = *z_return_route;
|
||||
zval_copy_ctor(return_value);
|
||||
}
|
||||
|
@ -972,7 +972,7 @@ PHP_METHOD(Mux, match) {
|
|||
}
|
||||
|
||||
zval **z_route_pp = NULL;
|
||||
zval *z_route = NULL;
|
||||
zval *z_R3Route = NULL;
|
||||
if ( zend_hash_find( Z_ARRVAL_P( zend_read_property(ce_r3_mux, this_ptr, "staticRoutes", sizeof("staticRoutes") - 1, 1 TSRMLS_CC) ), path, path_len, (void**)&z_route_pp) == SUCCESS ) {
|
||||
if ( Z_TYPE_PP(z_route_pp) != IS_NULL ) {
|
||||
*return_value = **z_route_pp;
|
||||
|
@ -981,8 +981,8 @@ PHP_METHOD(Mux, match) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
z_route = php_r3_match(zend_read_property(ce_r3_mux , this_ptr , "routes", sizeof("routes")-1, 1 TSRMLS_CC), path, path_len TSRMLS_CC);
|
||||
if ( z_route != NULL ) {
|
||||
z_R3Route = php_r3_match(zend_read_property(ce_r3_mux , this_ptr , "routes", sizeof("routes")-1, 1 TSRMLS_CC), path, path_len TSRMLS_CC);
|
||||
if ( z_R3Route != NULL ) {
|
||||
*return_value = *z_route;
|
||||
zval_copy_ctor(return_value);
|
||||
Z_ADDREF_P(z_route);
|
||||
|
@ -1058,7 +1058,7 @@ PHP_METHOD(Mux, appendPCRERoute) {
|
|||
zend_call_method( NULL, *ce_pattern_compiler, NULL, "compile", strlen("compile"), &rv, 1, z_pattern, NULL TSRMLS_CC );
|
||||
|
||||
if ( rv == NULL || Z_TYPE_P(rv) == IS_NULL ) {
|
||||
zend_throw_exception(ce_r3_exception, "Can not compile route pattern", 0 TSRMLS_CC);
|
||||
zend_throw_exception(ce_r3_exception, "Can not compile R3Route pattern", 0 TSRMLS_CC);
|
||||
RETURN_FALSE;
|
||||
}
|
||||
add_next_index_zval(z_routes, rv);
|
||||
|
@ -1115,9 +1115,9 @@ inline void mux_add_route(INTERNAL_FUNCTION_PARAMETERS)
|
|||
MAKE_STD_ZVAL(z_pattern);
|
||||
ZVAL_STRINGL(z_pattern, pattern, pattern_len, 1);
|
||||
|
||||
zval *z_compiled_route = compile_route_pattern(z_pattern, z_options, NULL TSRMLS_CC);
|
||||
if ( z_compiled_route == NULL ) {
|
||||
zend_throw_exception(ce_r3_exception, "Unable to compile route pattern.", 0 TSRMLS_CC);
|
||||
zval *z_compiled_R3Route = compile_route_pattern(z_pattern, z_options, NULL TSRMLS_CC);
|
||||
if ( z_compiled_R3Route == NULL ) {
|
||||
zend_throw_exception(ce_r3_exception, "Unable to compile R3Route pattern.", 0 TSRMLS_CC);
|
||||
RETURN_FALSE;
|
||||
}
|
||||
zval_ptr_dtor(&z_pattern);
|
||||
|
@ -1160,7 +1160,7 @@ inline void mux_add_route(INTERNAL_FUNCTION_PARAMETERS)
|
|||
add_index_zval( z_new_route, 3 , z_options);
|
||||
add_next_index_zval(z_routes, z_new_route);
|
||||
|
||||
// if there is no option specified in z_options, we can add the route to our static route hash
|
||||
// if there is no option specified in z_options, we can add the R3Route to our static R3Route hash
|
||||
if ( zend_hash_num_elements(Z_ARRVAL_P(z_options)) ) {
|
||||
zval * z_static_routes = zend_read_property(ce_r3_mux, this_ptr, "staticRoutes", sizeof("staticRoutes")-1, 1 TSRMLS_CC);
|
||||
if ( z_static_routes ) {
|
||||
|
@ -1173,7 +1173,7 @@ inline void mux_add_route(INTERNAL_FUNCTION_PARAMETERS)
|
|||
zval * z_routes_by_id = zend_read_property(ce_r3_mux, this_ptr, "routesById", sizeof("routesById")-1, 1 TSRMLS_CC);
|
||||
|
||||
/*
|
||||
zval *id_route = NULL;
|
||||
zval *id_R3Route = NULL;
|
||||
ALLOC_ZVAL(id_route);
|
||||
ZVAL_COPY_VALUE(id_route, z_new_route);
|
||||
zval_copy_ctor(id_route);
|
||||
|
|
2
r3.pc.in
2
r3.pc.in
|
@ -6,6 +6,6 @@ libdir=@libdir@
|
|||
Name: r3
|
||||
Description: High-performance URL router library
|
||||
Version: @PACKAGE_VERSION@
|
||||
Requires:
|
||||
Requires: libpcre2-8
|
||||
Libs: -L${libdir} -lr3
|
||||
CFlags: -I${includedir}
|
||||
|
|
6
run-benchmark
Executable file
6
run-benchmark
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
i=10
|
||||
while [ $i -gt 0 ] ; do
|
||||
bash tests/bench
|
||||
i=$(($i - 1))
|
||||
done
|
27
src/CMakeLists.txt
Normal file
27
src/CMakeLists.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
add_library(r3 STATIC
|
||||
edge.c
|
||||
match_entry.c
|
||||
memory.c
|
||||
node.c
|
||||
slug.c
|
||||
str.c
|
||||
token.c)
|
||||
|
||||
target_compile_definitions(r3
|
||||
PRIVATE
|
||||
_GNU_SOURCE)
|
||||
|
||||
target_include_directories(r3
|
||||
PUBLIC
|
||||
${PCRE2_INCLUDE_DIR}
|
||||
${PROJECT_BINARY_DIR}
|
||||
${PROJECT_SOURCE_DIR}/3rdparty
|
||||
${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
target_link_libraries(r3
|
||||
PUBLIC
|
||||
${PCRE2_LIBRARIES})
|
||||
|
||||
install(
|
||||
TARGETS r3
|
||||
DESTINATION lib)
|
|
@ -1,23 +1,32 @@
|
|||
lib_LTLIBRARIES = libr3.la
|
||||
AM_CFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) $(JSONC_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include -Wall -std=c99
|
||||
AM_LDFLAGS=$(DEPS_LIBS) $(GVC_DEPS_LIBS) $(JSONC_LIBS)
|
||||
MAYBE_COVERAGE=--coverage
|
||||
|
||||
noinst_LTLIBRARIES = libr3core.la
|
||||
# lib_LIBRARIES = libr3.a
|
||||
libr3_la_SOURCES = node.c edge.c str.c token.c zmalloc.c
|
||||
libr3core_la_SOURCES = node.c edge.c str.c token.c match_entry.c slug.c memory.c
|
||||
|
||||
if ENABLE_JSON
|
||||
libr3core_la_SOURCES += json.c
|
||||
endif
|
||||
|
||||
if COND_GCOV
|
||||
# MAYBE_COVERAGE=--coverage --no-inline
|
||||
AM_CFLAGS += $(MAYBE_COVERAGE)
|
||||
endif
|
||||
|
||||
MOSTLYCLEANFILES = *.gcov *.gcda *.gcno
|
||||
|
||||
|
||||
|
||||
# libr3_la_LDFLAGS = -export-symbols-regex '^r3_|^match_'
|
||||
|
||||
libr3_la_LIBADD=$(DEPS_LIBS)
|
||||
AM_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include -Wall -std=c99
|
||||
|
||||
if USE_JEMALLOC
|
||||
AM_CFLAGS += -ljemalloc
|
||||
endif
|
||||
|
||||
if ENABLE_DEBUG
|
||||
AM_CFLAGS += -ggdb -fprofile-arcs -ftest-coverage
|
||||
endif
|
||||
# libr3_la_LIBADD=$(DEPS_LIBS) $(LIBOBJS) $(ALLOCA)
|
||||
# libr3core_la_LIBADD=$(DEPS_LIBS)
|
||||
# libr3core_la_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include -Wall -std=c99
|
||||
|
||||
if ENABLE_GRAPHVIZ
|
||||
libr3_la_SOURCES += gvc.c
|
||||
libr3_la_LIBADD += $(GVC_DEPS_LIBS)
|
||||
AM_CFLAGS += $(GVC_DEPS_CFLAGS)
|
||||
libr3core_la_SOURCES += gvc.c
|
||||
endif
|
||||
|
||||
# AM_CFLAGS=$(DEPS_CFLAGS)
|
||||
|
|
107
src/edge.c
107
src/edge.c
|
@ -1,9 +1,10 @@
|
|||
/*
|
||||
* edge.c
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -12,90 +13,76 @@
|
|||
// Jemalloc memory management
|
||||
// #include <jemalloc/jemalloc.h>
|
||||
|
||||
// PCRE
|
||||
#include <pcre.h>
|
||||
|
||||
// Judy array
|
||||
// #include <Judy.h>
|
||||
#include <config.h>
|
||||
|
||||
#include "r3.h"
|
||||
#include "r3_str.h"
|
||||
#include "str_array.h"
|
||||
#include "zmalloc.h"
|
||||
#include "r3_slug.h"
|
||||
#include "slug.h"
|
||||
|
||||
edge * r3_edge_create(char * pattern, int pattern_len, node * child) {
|
||||
edge * e = (edge*) zmalloc( sizeof(edge) );
|
||||
e->pattern = pattern;
|
||||
e->pattern_len = pattern_len;
|
||||
e->opcode = 0;
|
||||
|
||||
#define CHECK_PTR(ptr) if (ptr == NULL) return NULL;
|
||||
|
||||
|
||||
|
||||
void r3_edge_initl(R3Edge *e, const char * pattern, int pattern_len, R3Node * child)
|
||||
{
|
||||
e->pattern.base = (char*) pattern;
|
||||
e->pattern.len = (unsigned int)pattern_len;
|
||||
// e->opcode = 0;
|
||||
e->child = child;
|
||||
e->parent = NULL;
|
||||
|
||||
// update childs parent edge
|
||||
child->parent_edge = e;
|
||||
|
||||
// default stats
|
||||
// e->hits = 0;
|
||||
e->score = 0;
|
||||
return e;
|
||||
e->has_slug = r3_path_contains_slug_char(e->pattern.base, e->pattern.len);
|
||||
}
|
||||
|
||||
// R3Edge * r3_edge_createl(const char * pattern, int pattern_len, R3Node * child)
|
||||
// {
|
||||
// R3Edge * e = (R3Edge*) malloc( sizeof(R3Edge) );
|
||||
// CHECK_PTR(e);
|
||||
// e->pattern = (char*) pattern;
|
||||
// e->pattern_len = pattern_len;
|
||||
// e->opcode = 0;
|
||||
// e->child = child;
|
||||
// e->has_slug = r3_path_contains_slug_char(e->pattern);
|
||||
// return e;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* r3_edge_branch splits the edge and append the rest part as the child of the
|
||||
* first level child
|
||||
*
|
||||
* branch the edge pattern at "dl" offset,
|
||||
* insert a dummy child between the edges.
|
||||
* and insert a dummy child between the edges.
|
||||
*
|
||||
*
|
||||
* A -> [prefix..suffix] -> B
|
||||
* A -> [prefix] -> B -> [suffix] -> New Child (Copy Data, Edges from B)
|
||||
* A -> [EDGE: abcdefg] -> B -> [EDGE:branch1], [EDGE:branch2]
|
||||
* A -> [EDGE: abcd] -> B1 -> [efg] -> B2 (new child with copied data from B)
|
||||
*
|
||||
*/
|
||||
node * r3_edge_branch(edge *e, int dl) {
|
||||
node *new_child;
|
||||
edge *e1;
|
||||
char * s1 = e->pattern + dl;
|
||||
int s1_len = 0;
|
||||
R3Node * r3_edge_branch(R3Edge *e, int dl) {
|
||||
R3Node * new_child;
|
||||
R3Edge * new_edge;
|
||||
|
||||
edge **tmp_edges = e->child->edges;
|
||||
int tmp_edge_len = e->child->edge_len;
|
||||
// the rest string
|
||||
const char * s1 = e->pattern.base + dl;
|
||||
int s1_len = e->pattern.len - dl;
|
||||
|
||||
// the suffix edge of the leaf
|
||||
new_child = r3_tree_create(3);
|
||||
s1_len = e->pattern_len - dl;
|
||||
|
||||
/* create the parent edge of new child */
|
||||
e1 = r3_edge_create(zstrndup(s1, s1_len), s1_len, new_child);
|
||||
|
||||
// Migrate the child edges to the new edge we just created.
|
||||
for ( int i = 0 ; i < tmp_edge_len ; i++ ) {
|
||||
r3_node_append_edge(new_child, tmp_edges[i]);
|
||||
e->child->edges[i] = NULL;
|
||||
}
|
||||
e->child->edge_len = 0;
|
||||
new_child->endpoint = e->child->endpoint;
|
||||
e->child->endpoint = 0; // reset endpoint
|
||||
|
||||
r3_node_append_edge(e->child, e1);
|
||||
new_child->data = e->child->data; // copy data pointer
|
||||
e->child->data = NULL;
|
||||
new_edge = r3_node_append_edge(new_child);
|
||||
r3_edge_initl(new_edge, s1, s1_len, e->child);
|
||||
e->child = new_child;
|
||||
|
||||
// truncate the original edge pattern
|
||||
char *oldpattern = e->pattern;
|
||||
e->pattern = zstrndup(e->pattern, dl);
|
||||
e->pattern_len = dl;
|
||||
zfree(oldpattern);
|
||||
|
||||
e->pattern.len = dl;
|
||||
return new_child;
|
||||
}
|
||||
|
||||
void r3_edge_free(edge * e) {
|
||||
zfree(e->pattern);
|
||||
void r3_edge_free(R3Edge * e) {
|
||||
if (e) {
|
||||
if ( e->child ) {
|
||||
r3_tree_free(e->child);
|
||||
}
|
||||
// free itself
|
||||
zfree(e);
|
||||
// free(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
81
src/gvc.c
81
src/gvc.c
|
@ -1,34 +1,37 @@
|
|||
/*
|
||||
* gvz.c
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <gvc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "r3.h"
|
||||
#include "r3_gvc.h"
|
||||
#include "zmalloc.h"
|
||||
|
||||
void r3_tree_build_ag_nodes(Agraph_t * g, Agnode_t * ag_parent_node, const node * n, int * node_cnt) {
|
||||
if (!n)
|
||||
return;
|
||||
|
||||
static char * node_id_str(int id) {
|
||||
char * name = zmalloc(sizeof(char) * 20);
|
||||
sprintf(name, "#%d", id);
|
||||
return name;
|
||||
for ( int i = 0 ; i < n->edges.size ; i++ ) {
|
||||
edge * e = n->edges.entries + i;
|
||||
(*node_cnt)++;
|
||||
|
||||
Agnode_t *agn_child = NULL;
|
||||
Agedge_t *agn_edge = NULL;
|
||||
|
||||
char *nodename = NULL;
|
||||
if ( e && e->child && e->child->combined_pattern ) {
|
||||
int r = asprintf(&nodename,"%s", e->child->combined_pattern);
|
||||
if (r) {};
|
||||
} else {
|
||||
int r = asprintf(&nodename,"#%d", *node_cnt);
|
||||
if (r) {};
|
||||
}
|
||||
|
||||
void r3_tree_build_ag_nodes(Agraph_t * g, Agnode_t * ag_parent_node, node * n, int node_cnt) {
|
||||
edge * e;
|
||||
Agnode_t *agn_child;
|
||||
Agedge_t *agn_edge;
|
||||
|
||||
for ( int i = 0 ; i < n->edge_len ; i++ ) {
|
||||
e = n->edges[i];
|
||||
|
||||
node_cnt++;
|
||||
agn_child = agnode(g, node_id_str(node_cnt) , 1);
|
||||
agn_child = agnode(g, nodename, 1);
|
||||
agn_edge = agedge(g, ag_parent_node, agn_child, 0, 1);
|
||||
agsafeset(agn_edge, "label", e->pattern, "");
|
||||
if (e->child && e->child->endpoint) {
|
||||
|
@ -40,27 +43,25 @@ void r3_tree_build_ag_nodes(Agraph_t * g, Agnode_t * ag_parent_node, node * n, i
|
|||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Render a tree to tree graph image via graphviz (dot)
|
||||
*/
|
||||
int r3_tree_render_dot(node * tree)
|
||||
int r3_tree_render(const node * tree, const char *layout, const char * format, FILE *fp)
|
||||
{
|
||||
Agraph_t *g;
|
||||
/* set up a graphviz context - but only once even for multiple graphs */
|
||||
static GVC_t *gvc;
|
||||
|
||||
if (!gvc) {
|
||||
GVC_t *gvc = NULL;
|
||||
gvc = gvContext();
|
||||
}
|
||||
/* Create a simple digraph */
|
||||
g = agopen("g", Agdirected, 0);
|
||||
// g = agopen("g", Agdirected, 0);
|
||||
g = agopen("g", Agundirected, 0);
|
||||
|
||||
// create self node
|
||||
Agnode_t *ag_root = agnode(g, "{root}", 1);
|
||||
r3_tree_build_ag_nodes(g, ag_root, tree, 0);
|
||||
gvLayout(gvc, g, "dot");
|
||||
gvRender(gvc, g, "dot", stdout);
|
||||
int n = 0;
|
||||
r3_tree_build_ag_nodes(g, ag_root, tree, &n);
|
||||
gvLayout(gvc, g, layout);
|
||||
gvRender(gvc, g, format, fp);
|
||||
gvFreeLayout(gvc, g);
|
||||
agclose(g);
|
||||
return 0;
|
||||
|
@ -69,26 +70,42 @@ int r3_tree_render_dot(node * tree)
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Render a tree to tree graph image via graphviz (dot)
|
||||
*/
|
||||
int r3_tree_render_file(node * tree, char * format, char * filename)
|
||||
int r3_tree_render_dot(const node * tree, const char *layout, FILE *fp)
|
||||
{
|
||||
return r3_tree_render(tree, layout, "dot", fp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render a tree to tree graph image via graphviz (dot)
|
||||
*/
|
||||
int r3_tree_render_file(const node * tree, const char * format, const char * filename)
|
||||
{
|
||||
Agraph_t *g;
|
||||
|
||||
/* set up a graphviz context - but only once even for multiple graphs */
|
||||
GVC_t *gvc = NULL;
|
||||
gvc = gvContext();
|
||||
/*
|
||||
// set up a graphviz context - but only once even for multiple graphs
|
||||
static GVC_t *gvc;
|
||||
|
||||
if (!gvc) {
|
||||
gvc = gvContext();
|
||||
}
|
||||
*/
|
||||
|
||||
/* Create a simple digraph */
|
||||
g = agopen("g", Agdirected, 0);
|
||||
// g = agopen("g", Agdirected, 0);
|
||||
g = agopen("g", Agundirected, 0);
|
||||
|
||||
// create self node
|
||||
Agnode_t *ag_root = agnode(g, "{root}", 1);
|
||||
r3_tree_build_ag_nodes(g, ag_root, tree, 0);
|
||||
int n = 0;
|
||||
r3_tree_build_ag_nodes(g, ag_root, tree, &n);
|
||||
|
||||
gvLayout(gvc, g, "dot");
|
||||
gvRenderFilename(gvc, g, format, filename);
|
||||
|
|
97
src/json.c
Normal file
97
src/json.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* json.c
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <json-c/json.h>
|
||||
#include "r3.h"
|
||||
#include "r3_json.h"
|
||||
|
||||
json_object * r3_route_to_json_object(const R3Route * r) {
|
||||
json_object *obj;
|
||||
|
||||
obj = json_object_new_object();
|
||||
json_object_object_add(obj, "path", json_object_new_string(r->path.base));
|
||||
json_object_object_add(obj, "allowed_methods", json_object_new_int(r->request_method));
|
||||
|
||||
|
||||
if (r->host) {
|
||||
json_object_object_add(obj, "host", json_object_new_string(r->host.base));
|
||||
}
|
||||
if (r->remote_addr_pattern) {
|
||||
json_object_object_add(obj, "remote_addr_pattern", json_object_new_string(r->remote_addr_pattern.base));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
json_object * r3_edge_to_json_object(const R3Edge * e) {
|
||||
json_object *obj;
|
||||
|
||||
obj = json_object_new_object();
|
||||
json_object_object_add(obj, "pattern", json_object_new_string(e->pattern.base));
|
||||
json_object_object_add(obj, "opcode", json_object_new_int(e->opcode));
|
||||
json_object_object_add(obj, "slug", json_object_new_boolean(e->has_slug));
|
||||
|
||||
if (e->child) {
|
||||
json_object *node_obj = r3_node_to_json_object(e->child);
|
||||
json_object_object_add(obj, "child", node_obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
json_object * r3_node_to_json_object(const R3Node * n) {
|
||||
json_object *obj;
|
||||
|
||||
obj = json_object_new_object();
|
||||
|
||||
if (n->combined_pattern) {
|
||||
json_object_object_add(obj, "re", json_object_new_string(n->combined_pattern));
|
||||
}
|
||||
json_object_object_add(obj, "endpoint", json_object_new_int(n->endpoint));
|
||||
json_object_object_add(obj, "compare", json_object_new_int(n->compare_type));
|
||||
|
||||
|
||||
int i;
|
||||
|
||||
if ( n->edge_len > 0 ) {
|
||||
json_object *edges_array = json_object_new_array();
|
||||
json_object_object_add(obj, "edges", edges_array);
|
||||
for (i = 0 ; i < n->edge_len ; i++ ) {
|
||||
json_object *edge_json_obj = r3_edge_to_json_object(n->edges.entries + i);
|
||||
json_object_array_add(edges_array, edge_json_obj);
|
||||
}
|
||||
}
|
||||
|
||||
if ( n->route_len > 0 ) {
|
||||
json_object *routes_array = json_object_new_array();
|
||||
json_object_object_add(obj, "routes", routes_array);
|
||||
for (i = 0; i < n->route_len; i++ ) {
|
||||
json_object *route_json_obj = r3_route_to_json_object(n->routes.entries + i);
|
||||
json_object_array_add(routes_array, route_json_obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
const char * r3_node_to_json_string_ext(const R3Node * n, int options) {
|
||||
json_object *obj = r3_node_to_json_object(n);
|
||||
return json_object_to_json_string_ext(obj, options);
|
||||
}
|
||||
|
||||
const char * r3_node_to_json_pretty_string(const R3Node * n) {
|
||||
json_object *obj = r3_node_to_json_object(n);
|
||||
return json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED);
|
||||
}
|
||||
|
||||
const char * r3_node_to_json_string(const R3Node * n) {
|
||||
json_object *obj = r3_node_to_json_object(n);
|
||||
return json_object_to_json_string(obj);
|
||||
}
|
||||
|
||||
|
27
src/list.c
27
src/list.c
|
@ -1,19 +1,17 @@
|
|||
/*
|
||||
* list.c
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* list.c Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "r3_list.h"
|
||||
#include "zmalloc.h"
|
||||
|
||||
/* Naive linked list implementation */
|
||||
|
||||
list *
|
||||
list_create()
|
||||
{
|
||||
list *l = (list *) zmalloc(sizeof(list));
|
||||
list *l = (list *) malloc(sizeof(list));
|
||||
l->count = 0;
|
||||
l->head = NULL;
|
||||
l->tail = NULL;
|
||||
|
@ -37,31 +35,26 @@ list_free(l)
|
|||
li = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(l->mutex));
|
||||
pthread_mutex_destroy(&(l->mutex));
|
||||
zfree(l);
|
||||
free(l);
|
||||
}
|
||||
}
|
||||
|
||||
list_item *
|
||||
list_add_element(l, ptr)
|
||||
list *l;
|
||||
void *ptr;
|
||||
list_item * list_add_element(list * l, void * ptr)
|
||||
{
|
||||
list_item *li;
|
||||
|
||||
pthread_mutex_lock(&(l->mutex));
|
||||
|
||||
li = (list_item *) zmalloc(sizeof(list_item));
|
||||
li = (list_item *) malloc(sizeof(list_item));
|
||||
li->value = ptr;
|
||||
li->next = NULL;
|
||||
li->prev = l->tail;
|
||||
|
||||
if (l->tail == NULL) {
|
||||
l->head = l->tail = li;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
l->tail = li;
|
||||
}
|
||||
l->count++;
|
||||
|
@ -85,19 +78,17 @@ list_remove_element(l, ptr)
|
|||
if (li->value == ptr) {
|
||||
if (li->prev == NULL) {
|
||||
l->head = li->next;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
li->prev->next = li->next;
|
||||
}
|
||||
|
||||
if (li->next == NULL) {
|
||||
l->tail = li->prev;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
li->next->prev = li->prev;
|
||||
}
|
||||
l->count--;
|
||||
zfree(li);
|
||||
free(li);
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
|
|
28
src/match_entry.c
Normal file
28
src/match_entry.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* match_entry.c
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "r3.h"
|
||||
|
||||
match_entry * match_entry_createl(const char * path, int path_len) {
|
||||
match_entry * entry = r3_mem_alloc( sizeof(match_entry) );
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
r3_vector_reserve(&entry->vars.tokens, 3);
|
||||
entry->path.base = path;
|
||||
entry->path.len = path_len;
|
||||
return entry;
|
||||
}
|
||||
|
||||
void match_entry_free(match_entry * entry) {
|
||||
assert(entry);
|
||||
free(entry->vars.tokens.entries);
|
||||
free(entry);
|
||||
}
|
45
src/memory.c
Normal file
45
src/memory.c
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2014 DeNA Co., Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "memory.h"
|
||||
|
||||
void r3_fatal(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "fatal:%s\n", msg);
|
||||
abort();
|
||||
}
|
||||
|
||||
void r3_vector__expand(r3_vector_t *vector, unsigned int element_size, unsigned int new_capacity)
|
||||
{
|
||||
void *new_entries;
|
||||
assert(vector->capacity < new_capacity);
|
||||
if (!vector->capacity)
|
||||
vector->capacity = 4;
|
||||
while (vector->capacity < new_capacity)
|
||||
vector->capacity *= 2;
|
||||
new_entries = r3_mem_realloc(vector->entries, element_size * vector->capacity);
|
||||
vector->entries = new_entries;
|
||||
}
|
1034
src/node.c
1034
src/node.c
File diff suppressed because it is too large
Load diff
19
src/r3_debug.h
Normal file
19
src/r3_debug.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef R3_DEBUG_H
|
||||
#define R3_DEBUG_H
|
||||
|
||||
// #define DEBUG 1
|
||||
#ifdef DEBUG
|
||||
|
||||
#define info(fmt, ...) \
|
||||
do { fprintf(stderr, fmt, ##__VA_ARGS__); } while (0)
|
||||
|
||||
#define debug(fmt, ...) \
|
||||
do { fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
|
||||
__LINE__, __func__, __VA_ARGS__); } while (0)
|
||||
|
||||
#else
|
||||
#define info(...);
|
||||
#define debug(...);
|
||||
#endif
|
||||
|
||||
#endif /* !DEBUG_H */
|
191
src/slug.c
Normal file
191
src/slug.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* slug.c
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "r3.h"
|
||||
#include "r3_slug.h"
|
||||
#include "slug.h"
|
||||
#include "r3_debug.h"
|
||||
|
||||
|
||||
|
||||
r3_slug_t * r3_slug_new(const char * path, int path_len) {
|
||||
r3_slug_t * s = malloc(sizeof(r3_slug_t));
|
||||
if (!s)
|
||||
return NULL;
|
||||
s->path = (char*) path;
|
||||
s->path_len = path_len;
|
||||
|
||||
s->begin = NULL;
|
||||
s->end = NULL;
|
||||
s->len = 0;
|
||||
|
||||
s->pattern = NULL;
|
||||
s->pattern_len = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
void r3_slug_free(r3_slug_t * s) {
|
||||
free(s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return 1 means OK
|
||||
* Return 0 means Empty
|
||||
* Return -1 means Error
|
||||
*/
|
||||
int r3_slug_check(r3_slug_t *s) {
|
||||
// if it's empty
|
||||
if (s->begin == NULL && s->len == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (s->begin && s->begin == s->end && s->len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if the head is defined, we should also have end pointer
|
||||
if (s->begin && s->end == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char * r3_slug_to_str(const r3_slug_t *s) {
|
||||
char *str = NULL;
|
||||
int r = asprintf(&str, "slug: '%.*s', pattern: '%.*s', path: '%.*s'", s->len, s->begin, s->pattern_len, s->pattern, s->path_len, s->path);
|
||||
if (r) {};
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
r3_slug_t * r3_slug_parse_next(r3_slug_t *s, char **errstr) {
|
||||
return r3_slug_parse(s->end, s->path_len - (s->end - s->begin), errstr);
|
||||
}
|
||||
|
||||
Return 0 => Empty, slug not found
|
||||
Return 1 => Slug found
|
||||
Return -1 => Slug parsing error
|
||||
*/
|
||||
|
||||
int r3_slug_parse(r3_slug_t *s, const char *needle, int needle_len, const char *offset, char **errstr) {
|
||||
s->path = (char*) needle;
|
||||
s->path_len = needle_len;
|
||||
|
||||
if (offset == NULL) {
|
||||
offset = (char*) needle; // from the begining of the needle
|
||||
}
|
||||
|
||||
// there is no slug
|
||||
if (!r3_path_contains_slug_char(offset, needle_len - (offset-needle))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cnt = 0;
|
||||
int state = 0;
|
||||
const char * p = offset;
|
||||
|
||||
while( (p-needle) < needle_len) {
|
||||
// escape one character
|
||||
if (*p == '\\' ) {
|
||||
p++; p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// slug starts with '{'
|
||||
if (state == 0 && *p == '{') {
|
||||
s->begin = ++p;
|
||||
state++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// in the middle of the slug (pattern)
|
||||
if (state == 1 && *p == ':') {
|
||||
// start from next
|
||||
s->pattern = ++p;
|
||||
continue;
|
||||
}
|
||||
|
||||
// slug closed.
|
||||
if (state == 1 && *p == '}') {
|
||||
s->end = p;
|
||||
s->len = s->end - s->begin;
|
||||
if (s->pattern) {
|
||||
s->pattern_len = p - s->pattern;
|
||||
}
|
||||
cnt++;
|
||||
state--;
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
|
||||
// might be inside the pattern
|
||||
if ( *p == '{' ) {
|
||||
state++;
|
||||
} else if ( *p == '}' ) {
|
||||
state--;
|
||||
}
|
||||
p++;
|
||||
};
|
||||
|
||||
if (state != 0) {
|
||||
if (errstr) {
|
||||
char *err = NULL;
|
||||
int r = asprintf(&err, "Incomplete slug pattern. PATH (%d): '%s', OFFSET: %ld, STATE: %d", needle_len, needle, p - needle, state);
|
||||
if (r) {};
|
||||
*errstr = err;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
info("found slug\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* provide a quick way to count slugs, simply search for '{'
|
||||
*/
|
||||
int r3_slug_count(const char * needle, int len, char **errstr) {
|
||||
int cnt = 0;
|
||||
int state = 0;
|
||||
char * p = (char*) needle;
|
||||
|
||||
while( (p-needle) < len) {
|
||||
if (*p == '\\' ) {
|
||||
p++; p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state == 1 && *p == '}') {
|
||||
cnt++;
|
||||
}
|
||||
if ( *p == '{' ) {
|
||||
state++;
|
||||
} else if ( *p == '}' ) {
|
||||
state--;
|
||||
}
|
||||
p++;
|
||||
};
|
||||
info("FOUND PATTERN: '%s' (%d), STATE: %d\n", needle, len, state);
|
||||
if (state != 0) {
|
||||
if (errstr) {
|
||||
char *err = NULL;
|
||||
int r = asprintf(&err, "Incomplete slug pattern. PATTERN (%d): '%s', OFFSET: %ld, STATE: %d", len, needle, p - needle, state);
|
||||
if (r) {};
|
||||
*errstr = err;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
59
src/slug.h
Normal file
59
src/slug.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* slug.h
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#ifndef SLUG_H
|
||||
#define SLUG_H
|
||||
|
||||
typedef struct {
|
||||
/**
|
||||
* source path
|
||||
*/
|
||||
const char * path;
|
||||
|
||||
int path_len;
|
||||
|
||||
/**
|
||||
* slug start pointer
|
||||
*/
|
||||
const char * begin;
|
||||
|
||||
/**
|
||||
* slug end pointer
|
||||
*/
|
||||
const char * end;
|
||||
|
||||
/**
|
||||
* slug length
|
||||
*/
|
||||
int len;
|
||||
|
||||
// slug pattern pointer if we have one
|
||||
const char * pattern;
|
||||
|
||||
// the length of custom pattern, if the pattern is found.
|
||||
int pattern_len;
|
||||
|
||||
} r3_slug_t;
|
||||
|
||||
|
||||
r3_slug_t * r3_slug_new(const char * path, int path_len);
|
||||
|
||||
int r3_slug_check(r3_slug_t *s);
|
||||
|
||||
int r3_slug_parse(r3_slug_t *s, const char *needle, int needle_len, const char *offset, char **errstr);
|
||||
|
||||
char * r3_slug_to_str(const r3_slug_t *s);
|
||||
|
||||
void r3_slug_free(r3_slug_t * s);
|
||||
|
||||
static inline int r3_path_contains_slug_char(const char *str, unsigned int len) {
|
||||
for (unsigned int i = 0; i < len; i++) {
|
||||
if (str[i] == '{') return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !SLUG_H */
|
151
src/str.c
151
src/str.c
|
@ -1,19 +1,27 @@
|
|||
/*
|
||||
* str.c
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "r3.h"
|
||||
#include "r3_str.h"
|
||||
#include "str_array.h"
|
||||
#include "zmalloc.h"
|
||||
#include "r3_slug.h"
|
||||
#include "str.h"
|
||||
#include "slug.h"
|
||||
|
||||
int r3_pattern_to_opcode(char * pattern, int len) {
|
||||
static const char * strnchr(const char* str, unsigned int len, int ch) {
|
||||
for (unsigned int i = 0; i < len; i++) {
|
||||
if (str[i] == ch) return str + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int r3_pattern_to_opcode(const char * pattern, unsigned int len) {
|
||||
if ( strncmp(pattern, "\\w+",len) == 0 ) {
|
||||
return OP_EXPECT_MORE_WORDS;
|
||||
}
|
||||
|
@ -35,36 +43,16 @@ int r3_pattern_to_opcode(char * pattern, int len) {
|
|||
if ( strncmp(pattern, "[^-]+", len) == 0 ) {
|
||||
return OP_EXPECT_NODASH;
|
||||
}
|
||||
if ( strncmp(pattern, ".*", len) == 0 ) {
|
||||
return OP_GREEDY_ANY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* provide a quick way to count slugs, simply search for '{'
|
||||
*/
|
||||
int slug_count(char * p, int len) {
|
||||
int s = 0;
|
||||
int lev = 0;
|
||||
while( len-- ) {
|
||||
if ( lev == 0 && *p == '{' )
|
||||
s++;
|
||||
if ( *p == '{' ) {
|
||||
lev++;
|
||||
}
|
||||
if ( *p == '}' ) {
|
||||
lev--;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool contains_slug(char * str) {
|
||||
return strchr(str, '{') != NULL ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
char * inside_slug(char * needle, int needle_len, char *offset) {
|
||||
char * r3_inside_slug(const char * needle, int needle_len, char *offset, char **errstr) {
|
||||
char * s1 = offset;
|
||||
char * s2 = offset;
|
||||
|
||||
|
@ -79,7 +67,7 @@ char * inside_slug(char * needle, int needle_len, char *offset) {
|
|||
s1--;
|
||||
}
|
||||
|
||||
char * end = needle + needle_len;
|
||||
const char * end = needle + needle_len;
|
||||
while( (s2 + 1) < end ) {
|
||||
if ( *s2 == '}' ) {
|
||||
found_s2 = 1;
|
||||
|
@ -90,17 +78,26 @@ char * inside_slug(char * needle, int needle_len, char *offset) {
|
|||
if (found_s1 && found_s2) {
|
||||
return s1;
|
||||
}
|
||||
if (found_s1 || found_s2) {
|
||||
// wrong slug pattern
|
||||
if(errstr) {
|
||||
int r = asprintf(errstr, "Incomplete slug pattern");
|
||||
if (r) {};
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char * slug_find_placeholder(char *s1, int *len) {
|
||||
char *c;
|
||||
char *s2;
|
||||
const char * r3_slug_find_placeholder(const char *s1, unsigned int str_len, unsigned int *len) {
|
||||
const char *c;
|
||||
const char *s2;
|
||||
int cnt = 0;
|
||||
if ( NULL != (c = strchr(s1, '{')) ) {
|
||||
if ((c = strnchr(s1, str_len, '{'))) {
|
||||
// find closing '}'
|
||||
s2 = c;
|
||||
while(*s2) {
|
||||
unsigned int j = str_len - (c - s1);
|
||||
for (unsigned int i = 0; i < j; i++) {
|
||||
if (*s2 == '{' )
|
||||
cnt++;
|
||||
else if (*s2 == '}' )
|
||||
|
@ -125,15 +122,16 @@ char * slug_find_placeholder(char *s1, int *len) {
|
|||
/**
|
||||
* given a slug string, duplicate the pattern string of the slug
|
||||
*/
|
||||
char * slug_find_pattern(char *s1, int *len) {
|
||||
char *c;
|
||||
char *s2;
|
||||
int cnt = 1;
|
||||
if ( NULL != (c = strchr(s1, ':')) ) {
|
||||
const char * r3_slug_find_pattern(const char *s1, unsigned int str_len, unsigned int *len) {
|
||||
const char *c;
|
||||
const char *s2;
|
||||
unsigned int cnt = 1;
|
||||
if ( (c = strnchr(s1, str_len, ':')) ) {
|
||||
c++;
|
||||
// find closing '}'
|
||||
s2 = c;
|
||||
while(s2) {
|
||||
unsigned int j = str_len - (c - s1);
|
||||
for (unsigned int i = 0; i < j; i++) {
|
||||
if (*s2 == '{' )
|
||||
cnt++;
|
||||
else if (*s2 == '}' )
|
||||
|
@ -146,41 +144,70 @@ char * slug_find_pattern(char *s1, int *len) {
|
|||
} else {
|
||||
return NULL;
|
||||
}
|
||||
if (cnt!=0) {
|
||||
return NULL;
|
||||
}
|
||||
*len = s2 - c;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* given a slug string, duplicate the parameter name string of the slug
|
||||
*/
|
||||
const char * r3_slug_find_name(const char *s1, unsigned int str_len, unsigned int *len) {
|
||||
const char * c;
|
||||
const char * s2;
|
||||
unsigned int plholder;
|
||||
if ((c = r3_slug_find_placeholder(s1, str_len, &plholder))) {
|
||||
c++;
|
||||
if (( s2 = strnchr(c, plholder, ':') )) {
|
||||
*len = s2 - c;
|
||||
return c;
|
||||
} else {
|
||||
*len = plholder - 2;
|
||||
return c;
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param char * sep separator
|
||||
*/
|
||||
char * slug_compile(char * str, int len)
|
||||
char * r3_slug_compile(const char * str, unsigned int len)
|
||||
{
|
||||
char *s1 = NULL, *o = NULL;
|
||||
char *pat = NULL;
|
||||
const char *s1 = NULL;
|
||||
char *o = NULL;
|
||||
const char *pat = NULL;
|
||||
char sep = '/';
|
||||
|
||||
|
||||
// append prefix
|
||||
int s1_len;
|
||||
s1 = slug_find_placeholder(str, &s1_len);
|
||||
unsigned int s1_len;
|
||||
s1 = r3_slug_find_placeholder(str, len, &s1_len);
|
||||
|
||||
if ( s1 == NULL ) {
|
||||
return zstrdup(str);
|
||||
if ( !s1 ) {
|
||||
return strndup(str,len);
|
||||
}
|
||||
|
||||
char * out = NULL;
|
||||
if ((out = zcalloc(200)) == NULL) {
|
||||
if (!(out = calloc(1, sizeof(char) * 200))) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
o = out;
|
||||
strncat(o, "^", 1);
|
||||
o++;
|
||||
|
||||
strncat(o, str, s1 - str); // string before slug
|
||||
o += (s1 - str);
|
||||
|
||||
|
||||
int pat_len;
|
||||
pat = slug_find_pattern(s1, &pat_len);
|
||||
unsigned int pat_len;
|
||||
pat = r3_slug_find_pattern(s1, s1_len, &pat_len);
|
||||
|
||||
if (pat) {
|
||||
*o = '(';
|
||||
|
@ -194,7 +221,7 @@ char * slug_compile(char * str, int len)
|
|||
o+= strlen("([^*]+)");
|
||||
}
|
||||
s1 += s1_len;
|
||||
strncat(o, s1, strlen(s1));
|
||||
strncat(o, s1, len - (s1 - str)); // string after slug
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -203,13 +230,7 @@ char * ltrim_slash(char* str)
|
|||
{
|
||||
char * p = str;
|
||||
while (*p == '/') p++;
|
||||
return zstrdup(p);
|
||||
}
|
||||
|
||||
void str_repeat(char *s, char *c, int len) {
|
||||
while(len--) {
|
||||
s[len - 1] = *c;
|
||||
}
|
||||
return strdup(p);
|
||||
}
|
||||
|
||||
void print_indent(int level) {
|
||||
|
@ -219,14 +240,16 @@ void print_indent(int level) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef HAVE_STRDUP
|
||||
char *zstrdup(const char *s) {
|
||||
char *strdup(const char *s) {
|
||||
char *out;
|
||||
int count = 0;
|
||||
while( s[count] )
|
||||
++count;
|
||||
++count;
|
||||
out = zmalloc(sizeof(char) * count);
|
||||
out = malloc(sizeof(char) * count);
|
||||
out[--count] = 0;
|
||||
while( --count >= 0 )
|
||||
out[count] = s[count];
|
||||
|
@ -234,14 +257,16 @@ char *zstrdup(const char *s) {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifndef HAVE_STRNDUP
|
||||
char *zstrndup(const char *s, int n) {
|
||||
char *strndup(const char *s, int n) {
|
||||
char *out;
|
||||
int count = 0;
|
||||
while( count < n && s[count] )
|
||||
++count;
|
||||
++count;
|
||||
out = zmalloc(sizeof(char) * count);
|
||||
out = malloc(sizeof(char) * count);
|
||||
out[--count] = 0;
|
||||
while( --count >= 0 )
|
||||
out[count] = s[count];
|
||||
|
|
14
src/str.h
Normal file
14
src/str.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef R3_STR_INTERN_H
|
||||
#define R3_STR_INTERN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void print_indent(int level);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
73
src/token.c
73
src/token.c
|
@ -1,59 +1,54 @@
|
|||
/*
|
||||
* token.c
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "r3.h"
|
||||
#include "r3_slug.h"
|
||||
#include "str_array.h"
|
||||
#include "r3_str.h"
|
||||
#include "zmalloc.h"
|
||||
|
||||
str_array * str_array_create(int cap) {
|
||||
str_array * list = (str_array*) zmalloc( sizeof(str_array) );
|
||||
list->len = 0;
|
||||
list->cap = cap;
|
||||
list->tokens = (char**) zmalloc( sizeof(char*) * cap);
|
||||
return list;
|
||||
}
|
||||
#include "memory.h"
|
||||
|
||||
void str_array_free(str_array *l) {
|
||||
for ( int i = 0; i < l->len ; i++ ) {
|
||||
char * t = l->tokens[ i ];
|
||||
zfree(t);
|
||||
}
|
||||
zfree(l);
|
||||
assert(l);
|
||||
free(l->tokens.entries);
|
||||
}
|
||||
|
||||
bool str_array_is_full(str_array * l) {
|
||||
return l->len >= l->cap;
|
||||
bool str_array_append(str_array * l, const char * token, unsigned int len) {
|
||||
r3_vector_reserve(&l->tokens, l->tokens.size + 1);
|
||||
r3_iovec_t *temp = l->tokens.entries + l->tokens.size++;
|
||||
memset(temp, 0, sizeof(*temp));
|
||||
temp->base = token;
|
||||
temp->len = len;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool str_array_resize(str_array *l, int new_cap) {
|
||||
l->tokens = zrealloc(l->tokens, sizeof(char**) * new_cap);
|
||||
l->cap = new_cap;
|
||||
return l->tokens != NULL;
|
||||
}
|
||||
|
||||
bool str_array_append(str_array * l, char * token) {
|
||||
if ( str_array_is_full(l) ) {
|
||||
bool ret = str_array_resize(l, l->cap + 20);
|
||||
if (ret == FALSE ) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
l->tokens[ l->len++ ] = token;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void str_array_dump(str_array *l) {
|
||||
void str_array_dump_slugs(const str_array *l) {
|
||||
if (l->tokens.size) {
|
||||
printf("[");
|
||||
for ( int i = 0; i < l->len ; i++ ) {
|
||||
printf("\"%s\"", l->tokens[i] );
|
||||
if ( i + 1 != l->len ) {
|
||||
for ( int i = 0; i < l->tokens.size ; i++ ) {
|
||||
printf("\"%*.*s\"", l->slugs.entries[i].len,l->slugs.entries[i].len,l->slugs.entries[i].base );
|
||||
if ( i + 1 != l->tokens.size ) {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
printf("]\n");
|
||||
} else {
|
||||
printf("[]\n");
|
||||
}
|
||||
}
|
||||
|
||||
void str_array_dump(const str_array *l) {
|
||||
printf("[");
|
||||
for ( int i = 0; i < l->tokens.size ; i++ ) {
|
||||
printf("\"%*.*s\"", l->tokens.entries[i].len,l->tokens.entries[i].len,l->tokens.entries[i].base );
|
||||
// printf("\"%s\"", l->tokens.entries[i] );
|
||||
if ( i + 1 != l->tokens.size ) {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
|
|
368
src/zmalloc.c
368
src/zmalloc.c
|
@ -1,368 +0,0 @@
|
|||
/* zmalloc - total amount of allocated memory aware version of malloc()
|
||||
*
|
||||
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* This function provide us access to the original libc free(). This is useful
|
||||
* for instance to free results obtained by backtrace_symbols(). We need
|
||||
* to define this function before including zmalloc.h that may shadow the
|
||||
* free implementation if we use jemalloc or another non standard allocator. */
|
||||
void zlibc_free(void *ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "config.h"
|
||||
#include "zmalloc.h"
|
||||
|
||||
#ifdef HAVE_MALLOC_SIZE
|
||||
#define PREFIX_SIZE (0)
|
||||
#else
|
||||
#if defined(__sun) || defined(__sparc) || defined(__sparc__)
|
||||
#define PREFIX_SIZE (sizeof(long long))
|
||||
#else
|
||||
#define PREFIX_SIZE (sizeof(size_t))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Explicitly override malloc/free etc when using tcmalloc. */
|
||||
#if defined(USE_TCMALLOC)
|
||||
#define malloc(size) tc_malloc(size)
|
||||
#define calloc(count,size) tc_calloc(count,size)
|
||||
#define realloc(ptr,size) tc_realloc(ptr,size)
|
||||
#define free(ptr) tc_free(ptr)
|
||||
#elif defined(USE_JEMALLOC) && (JEMALLOC_VERSION_MAJOR > 2)
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#define malloc(size) je_malloc(size)
|
||||
#define calloc(count,size) je_calloc(count,size)
|
||||
#define realloc(ptr,size) je_realloc(ptr,size)
|
||||
#define free(ptr) je_free(ptr)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATOMIC
|
||||
#define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n))
|
||||
#define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n))
|
||||
#else
|
||||
#define update_zmalloc_stat_add(__n) do { \
|
||||
pthread_mutex_lock(&used_memory_mutex); \
|
||||
used_memory += (__n); \
|
||||
pthread_mutex_unlock(&used_memory_mutex); \
|
||||
} while(0)
|
||||
|
||||
#define update_zmalloc_stat_sub(__n) do { \
|
||||
pthread_mutex_lock(&used_memory_mutex); \
|
||||
used_memory -= (__n); \
|
||||
pthread_mutex_unlock(&used_memory_mutex); \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
#define update_zmalloc_stat_alloc(__n) do { \
|
||||
size_t _n = (__n); \
|
||||
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
|
||||
if (zmalloc_thread_safe) { \
|
||||
update_zmalloc_stat_add(_n); \
|
||||
} else { \
|
||||
used_memory += _n; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define update_zmalloc_stat_free(__n) do { \
|
||||
size_t _n = (__n); \
|
||||
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
|
||||
if (zmalloc_thread_safe) { \
|
||||
update_zmalloc_stat_sub(_n); \
|
||||
} else { \
|
||||
used_memory -= _n; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static size_t used_memory = 0;
|
||||
static int zmalloc_thread_safe = 0;
|
||||
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void zmalloc_default_oom(size_t size) {
|
||||
fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
|
||||
size);
|
||||
fflush(stderr);
|
||||
abort();
|
||||
}
|
||||
|
||||
static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;
|
||||
|
||||
void *zmalloc(size_t size) {
|
||||
void *ptr = malloc(size+PREFIX_SIZE);
|
||||
|
||||
if (!ptr) zmalloc_oom_handler(size);
|
||||
#ifdef HAVE_MALLOC_SIZE
|
||||
update_zmalloc_stat_alloc(zmalloc_size(ptr));
|
||||
return ptr;
|
||||
#else
|
||||
*((size_t*)ptr) = size;
|
||||
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
|
||||
return (char*)ptr+PREFIX_SIZE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *zcalloc(size_t size) {
|
||||
void *ptr = calloc(1, size+PREFIX_SIZE);
|
||||
|
||||
if (!ptr) zmalloc_oom_handler(size);
|
||||
#ifdef HAVE_MALLOC_SIZE
|
||||
update_zmalloc_stat_alloc(zmalloc_size(ptr));
|
||||
return ptr;
|
||||
#else
|
||||
*((size_t*)ptr) = size;
|
||||
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
|
||||
return (char*)ptr+PREFIX_SIZE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *zrealloc(void *ptr, size_t size) {
|
||||
#ifndef HAVE_MALLOC_SIZE
|
||||
void *realptr;
|
||||
#endif
|
||||
size_t oldsize;
|
||||
void *newptr;
|
||||
|
||||
if (ptr == NULL) return zmalloc(size);
|
||||
#ifdef HAVE_MALLOC_SIZE
|
||||
oldsize = zmalloc_size(ptr);
|
||||
newptr = realloc(ptr,size);
|
||||
if (!newptr) zmalloc_oom_handler(size);
|
||||
|
||||
update_zmalloc_stat_free(oldsize);
|
||||
update_zmalloc_stat_alloc(zmalloc_size(newptr));
|
||||
return newptr;
|
||||
#else
|
||||
realptr = (char*)ptr-PREFIX_SIZE;
|
||||
oldsize = *((size_t*)realptr);
|
||||
newptr = realloc(realptr,size+PREFIX_SIZE);
|
||||
if (!newptr) zmalloc_oom_handler(size);
|
||||
|
||||
*((size_t*)newptr) = size;
|
||||
update_zmalloc_stat_free(oldsize);
|
||||
update_zmalloc_stat_alloc(size);
|
||||
return (char*)newptr+PREFIX_SIZE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Provide zmalloc_size() for systems where this function is not provided by
|
||||
* malloc itself, given that in that case we store a header with this
|
||||
* information as the first bytes of every allocation. */
|
||||
#ifndef HAVE_MALLOC_SIZE
|
||||
size_t zmalloc_size(void *ptr) {
|
||||
void *realptr = (char*)ptr-PREFIX_SIZE;
|
||||
size_t size = *((size_t*)realptr);
|
||||
/* Assume at least that all the allocations are padded at sizeof(long) by
|
||||
* the underlying allocator. */
|
||||
if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1));
|
||||
return size+PREFIX_SIZE;
|
||||
}
|
||||
#endif
|
||||
|
||||
void zfree(void *ptr) {
|
||||
#ifndef HAVE_MALLOC_SIZE
|
||||
void *realptr;
|
||||
size_t oldsize;
|
||||
#endif
|
||||
|
||||
if (ptr == NULL) return;
|
||||
#ifdef HAVE_MALLOC_SIZE
|
||||
update_zmalloc_stat_free(zmalloc_size(ptr));
|
||||
free(ptr);
|
||||
#else
|
||||
realptr = (char*)ptr-PREFIX_SIZE;
|
||||
oldsize = *((size_t*)realptr);
|
||||
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
|
||||
free(realptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
char *zstrdup(const char *s) {
|
||||
size_t l = strlen(s)+1;
|
||||
char *p = zmalloc(l);
|
||||
|
||||
memcpy(p,s,l);
|
||||
return p;
|
||||
}
|
||||
|
||||
char * zstrndup (const char *s, size_t n)
|
||||
{
|
||||
char *result;
|
||||
size_t len = strlen (s);
|
||||
|
||||
if (n < len)
|
||||
len = n;
|
||||
|
||||
result = (char *) zmalloc (len + 1);
|
||||
if (!result)
|
||||
return 0;
|
||||
|
||||
result[len] = '\0';
|
||||
return (char *) memcpy (result, s, len);
|
||||
}
|
||||
|
||||
size_t zmalloc_used_memory(void) {
|
||||
size_t um;
|
||||
|
||||
if (zmalloc_thread_safe) {
|
||||
#ifdef HAVE_ATOMIC
|
||||
um = __sync_add_and_fetch(&used_memory, 0);
|
||||
#else
|
||||
pthread_mutex_lock(&used_memory_mutex);
|
||||
um = used_memory;
|
||||
pthread_mutex_unlock(&used_memory_mutex);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
um = used_memory;
|
||||
}
|
||||
|
||||
return um;
|
||||
}
|
||||
|
||||
void zmalloc_enable_thread_safeness(void) {
|
||||
zmalloc_thread_safe = 1;
|
||||
}
|
||||
|
||||
void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
|
||||
zmalloc_oom_handler = oom_handler;
|
||||
}
|
||||
|
||||
/* Get the RSS information in an OS-specific way.
|
||||
*
|
||||
* WARNING: the function zmalloc_get_rss() is not designed to be fast
|
||||
* and may not be called in the busy loops where Redis tries to release
|
||||
* memory expiring or swapping out objects.
|
||||
*
|
||||
* For this kind of "fast RSS reporting" usages use instead the
|
||||
* function RedisEstimateRSS() that is a much faster (and less precise)
|
||||
* version of the function. */
|
||||
|
||||
#if defined(HAVE_PROC_STAT)
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
size_t zmalloc_get_rss(void) {
|
||||
int page = sysconf(_SC_PAGESIZE);
|
||||
size_t rss;
|
||||
char buf[4096];
|
||||
char filename[256];
|
||||
int fd, count;
|
||||
char *p, *x;
|
||||
|
||||
snprintf(filename,256,"/proc/%d/stat",getpid());
|
||||
if ((fd = open(filename,O_RDONLY)) == -1) return 0;
|
||||
if (read(fd,buf,4096) <= 0) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
p = buf;
|
||||
count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
|
||||
while(p && count--) {
|
||||
p = strchr(p,' ');
|
||||
if (p) p++;
|
||||
}
|
||||
if (!p) return 0;
|
||||
x = strchr(p,' ');
|
||||
if (!x) return 0;
|
||||
*x = '\0';
|
||||
|
||||
rss = strtoll(p,NULL,10);
|
||||
rss *= page;
|
||||
return rss;
|
||||
}
|
||||
#elif defined(HAVE_TASKINFO)
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <mach/task.h>
|
||||
#include <mach/mach_init.h>
|
||||
|
||||
size_t zmalloc_get_rss(void) {
|
||||
task_t task = MACH_PORT_NULL;
|
||||
struct task_basic_info t_info;
|
||||
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
|
||||
|
||||
if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS)
|
||||
return 0;
|
||||
task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
|
||||
|
||||
return t_info.resident_size;
|
||||
}
|
||||
#else
|
||||
size_t zmalloc_get_rss(void) {
|
||||
/* If we can't get the RSS in an OS-specific way for this system just
|
||||
* return the memory usage we estimated in zmalloc()..
|
||||
*
|
||||
* Fragmentation will appear to be always 1 (no fragmentation)
|
||||
* of course... */
|
||||
return zmalloc_used_memory();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fragmentation = RSS / allocated-bytes */
|
||||
float zmalloc_get_fragmentation_ratio(size_t rss) {
|
||||
return (float)rss/zmalloc_used_memory();
|
||||
}
|
||||
|
||||
#if defined(HAVE_PROC_SMAPS)
|
||||
size_t zmalloc_get_private_dirty(void) {
|
||||
char line[1024];
|
||||
size_t pd = 0;
|
||||
FILE *fp = fopen("/proc/self/smaps","r");
|
||||
|
||||
if (!fp) return 0;
|
||||
while(fgets(line,sizeof(line),fp) != NULL) {
|
||||
if (strncmp(line,"Private_Dirty:",14) == 0) {
|
||||
char *p = strchr(line,'k');
|
||||
if (p) {
|
||||
*p = '\0';
|
||||
pd += strtol(line+14,NULL,10) * 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return pd;
|
||||
}
|
||||
#else
|
||||
size_t zmalloc_get_private_dirty(void) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
27
tests/CMakeLists.txt
Normal file
27
tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
function(add_r3_test NAME)
|
||||
add_executable(${NAME} ${ARGN})
|
||||
|
||||
target_include_directories(${NAME}
|
||||
PRIVATE
|
||||
${CHECK_INCLUDE_DIRS}
|
||||
${PROJECT_BINARY_DIR}
|
||||
${PROJECT_SOURCE_DIR}/src)
|
||||
|
||||
target_link_libraries(${NAME}
|
||||
${CHECK_LDFLAGS}
|
||||
r3)
|
||||
|
||||
add_test(NAME ${NAME} COMMAND ${NAME})
|
||||
endfunction()
|
||||
|
||||
add_r3_test(check_tree check_tree.c)
|
||||
add_r3_test(check_slug check_slug.c)
|
||||
add_r3_test(check_routes check_routes.c)
|
||||
add_r3_test(check_str_array check_str_array.c)
|
||||
add_r3_test(check_host check_host.c)
|
||||
add_r3_test(check_http_scheme check_http_scheme.c)
|
||||
add_r3_test(check_remote_addr check_remote_addr.c)
|
||||
add_r3_test(check_routes2 check_routes2.c)
|
||||
|
||||
add_executable(bench bench.c)
|
||||
target_link_libraries(bench r3)
|
|
@ -1,13 +1,11 @@
|
|||
# INCLUDES = @CHECK_CFLAGS@
|
||||
# if HAVE_CHECK
|
||||
# TESTS = check_tree
|
||||
# else
|
||||
# TESTS =
|
||||
# endif
|
||||
TESTS = check_tree
|
||||
AM_CFLAGS = -ggdb $(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include @CHECK_CFLAGS@
|
||||
AM_LDFLAGS = $(DEPS_LIBS) -L$(top_builddir)/src -lr3 @CHECK_LIBS@
|
||||
TESTS =
|
||||
|
||||
AM_CFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) $(JSONC_CFLAGS) @CHECK_CFLAGS@ -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/src -Wall -std=c99 -ggdb `pkg-config --cflags --libs check`
|
||||
AM_LDFLAGS=$(DEPS_LIBS) $(GVC_DEPS_LIBS) $(JSONC_LIBS) @CHECK_LIBS@ $(top_builddir)/libr3.la
|
||||
|
||||
if USE_JEMALLOC
|
||||
AM_CFLAGS += -ljemalloc
|
||||
endif
|
||||
|
||||
noinst_HEADERS = \
|
||||
bench.h \
|
||||
|
@ -16,36 +14,49 @@ noinst_HEADERS = \
|
|||
dist_noinst_DATA = \
|
||||
$(NULL)
|
||||
|
||||
if USE_JEMALLOC
|
||||
AM_CFLAGS += -ljemalloc
|
||||
endif
|
||||
|
||||
if ENABLE_GRAPHVIZ
|
||||
TESTS += check_gvc
|
||||
check_gvc_SOURCES = check_gvc.c
|
||||
# check_gvc_LDADD = $(GVC_DEPS_LIBS) $(DEPS_LIBS) -L$(top_builddir)/src -lr3 @CHECK_LIBS@
|
||||
# check_gvc_CFLAGS = $(GVC_DEPS_CFLAGS) $(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include @CHECK_CFLAGS@
|
||||
AM_CFLAGS += $(GVC_DEPS_CFLAGS)
|
||||
AM_LDFLAGS += $(GVC_DEPS_LIBS)
|
||||
endif
|
||||
|
||||
|
||||
bin_PROGRAMS = benchmark
|
||||
benchmark_SOURCES = bench.c
|
||||
|
||||
|
||||
|
||||
# noinst_PROGRAMS = $(TESTS)
|
||||
check_tree_SOURCES = check_tree.c
|
||||
# check_tree_LDADD=$(DEPS_LIBS) -L$(top_builddir)/src -lr3 @CHECK_LIBS@
|
||||
# check_tree_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include @CHECK_CFLAGS@
|
||||
|
||||
TESTS += check_slug
|
||||
check_slug_SOURCES = check_slug.c
|
||||
|
||||
TESTS += check_tree
|
||||
check_tree_SOURCES = check_tree.c
|
||||
|
||||
TESTS += check_routes
|
||||
check_routes_SOURCES = check_routes.c
|
||||
|
||||
TESTS += check_str_array
|
||||
check_str_array_SOURCES = check_str_array.c
|
||||
|
||||
TESTS += check_host
|
||||
check_host_SOURCES = check_host.c
|
||||
|
||||
TESTS += check_remote_addr
|
||||
check_remote_addr_SOURCES = check_remote_addr.c
|
||||
|
||||
TESTS += check_http_scheme
|
||||
check_http_scheme_SOURCES = check_http_scheme.c
|
||||
|
||||
TESTS += check_routes2
|
||||
check_routes2_SOURCES = check_routes2.c
|
||||
|
||||
|
||||
if ENABLE_JSON
|
||||
TESTS += check_json
|
||||
check_json_SOURCES = check_json.c
|
||||
endif
|
||||
|
||||
|
||||
if ENABLE_GRAPHVIZ
|
||||
TESTS += check_gvc
|
||||
check_gvc_SOURCES = check_gvc.c
|
||||
endif
|
||||
|
||||
|
||||
|
||||
check_PROGRAMS = $(TESTS)
|
||||
|
||||
noinst_PROGRAMS = bench
|
||||
bench_SOURCES = bench.c
|
||||
|
||||
# AM_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir)/include
|
||||
# AM_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
/*
|
||||
* bench.c
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdarg.h> /* va_list, va_start, va_arg, va_end */
|
||||
|
||||
#include "r3.h"
|
||||
#include "r3_str.h"
|
||||
#include "str_array.h"
|
||||
#include "zmalloc.h"
|
||||
#include "r3_slug.h"
|
||||
#include "bench.h"
|
||||
|
||||
|
||||
|
||||
unsigned long unixtime() {
|
||||
struct timeval tp;
|
||||
long sec = 0L;
|
||||
if (gettimeofday((struct timeval *) &tp, (NUL)) == 0) {
|
||||
if (gettimeofday((struct timeval *) &tp, (NULL)) == 0) {
|
||||
return tp.tv_sec;
|
||||
}
|
||||
return 0;
|
||||
|
@ -31,9 +29,7 @@ double microtime() {
|
|||
struct timeval tp;
|
||||
long sec = 0L;
|
||||
double msec = 0.0;
|
||||
char ret[100];
|
||||
|
||||
if (gettimeofday((struct timeval *) &tp, (NUL)) == 0) {
|
||||
if (gettimeofday((struct timeval *) &tp, (NULL)) == 0) {
|
||||
msec = (double) (tp.tv_usec / MICRO_IN_SEC);
|
||||
sec = tp.tv_sec;
|
||||
if (msec >= 1.0)
|
||||
|
@ -99,7 +95,7 @@ void bench_append_csv(char *filename, int countOfB, ...) {
|
|||
|
||||
int main()
|
||||
{
|
||||
node * n = r3_tree_create(1);
|
||||
R3Node * n = r3_tree_create(1);
|
||||
|
||||
int route_data = 999;
|
||||
|
||||
|
@ -441,10 +437,10 @@ r3_tree_insert_path(n, "/garply/grault/quux", NULL);
|
|||
r3_tree_insert_path(n, "/garply/grault/corge", NULL);
|
||||
|
||||
MEASURE(tree_compile)
|
||||
r3_tree_compile(n);
|
||||
r3_tree_compile(n, NULL);
|
||||
END_MEASURE(tree_compile)
|
||||
|
||||
node *m;
|
||||
R3Node * m;
|
||||
m = r3_tree_match(n , "/qux/bar/corge", NULL);
|
||||
assert(m != NULL);
|
||||
assert( *((int*) m->data) == 999 );
|
||||
|
@ -459,20 +455,25 @@ r3_tree_insert_path(n, "/garply/grault/corge", NULL);
|
|||
BENCHMARK(str_match_entry)
|
||||
match_entry * e = match_entry_createl("/qux/bar/corge", strlen("/qux/bar/corge") );
|
||||
r3_tree_match_entry(n , e);
|
||||
zfree(e);
|
||||
match_entry_free(e);
|
||||
END_BENCHMARK(str_match_entry)
|
||||
BENCHMARK_SUMMARY(str_match_entry);
|
||||
|
||||
|
||||
node * tree2 = r3_tree_create(1);
|
||||
R3Node * tree2 = r3_tree_create(1);
|
||||
r3_tree_insert_path(tree2, "/post/{year}/{month}", NULL);
|
||||
r3_tree_compile(tree2);
|
||||
r3_tree_compile(tree2, NULL);
|
||||
// r3_tree_dump(tree2,0);
|
||||
|
||||
BENCHMARK(pcre_dispatch)
|
||||
r3_tree_matchl(tree2, "/post/2014/12", strlen("/post/2014/12"), NULL);
|
||||
END_BENCHMARK(pcre_dispatch)
|
||||
BENCHMARK_SUMMARY(pcre_dispatch);
|
||||
|
||||
|
||||
BENCHMARK_RECORD_CSV("bench_str.csv", 4, BR(str_dispatch), BR(pcre_dispatch), BR(tree_compile), BR(str_match_entry) );
|
||||
|
||||
r3_tree_free(tree2);
|
||||
r3_tree_free(n);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* bench.h
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* check_gvc.c
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
@ -9,9 +9,8 @@
|
|||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
#include "r3.h"
|
||||
#include "r3_str.h"
|
||||
#include "r3_gvc.h"
|
||||
#include "str_array.h"
|
||||
#include "r3_slug.h"
|
||||
#include "bench.h"
|
||||
|
||||
START_TEST (test_gvc_render_dot)
|
||||
|
@ -26,9 +25,9 @@ START_TEST (test_gvc_render_dot)
|
|||
r3_tree_insert_path(n, "/garply/grault/foo", NULL);
|
||||
r3_tree_insert_path(n, "/garply/grault/bar", NULL);
|
||||
|
||||
r3_tree_compile(n);
|
||||
r3_tree_compile(n, NULL);
|
||||
|
||||
r3_tree_render_dot(n);
|
||||
r3_tree_render_dot(n, "dot", stderr);
|
||||
|
||||
r3_tree_free(n);
|
||||
}
|
||||
|
@ -36,7 +35,6 @@ END_TEST
|
|||
|
||||
START_TEST (test_gvc_render_file)
|
||||
{
|
||||
|
||||
node * n = r3_tree_create(1);
|
||||
|
||||
r3_tree_insert_path(n, "/foo/bar/baz", NULL);
|
||||
|
@ -49,7 +47,10 @@ START_TEST (test_gvc_render_file)
|
|||
r3_tree_insert_path(n, "/user/{id}", NULL);
|
||||
r3_tree_insert_path(n, "/post/{title:\\w+}", NULL);
|
||||
|
||||
r3_tree_compile(n);
|
||||
char *errstr = NULL;
|
||||
int errcode;
|
||||
errcode = r3_tree_compile(n, &errstr);
|
||||
|
||||
r3_tree_render_file(n, "png", "check_gvc.png");
|
||||
r3_tree_free(n);
|
||||
}
|
||||
|
|
92
tests/check_host.c
Normal file
92
tests/check_host.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "r3.h"
|
||||
#include "r3_slug.h"
|
||||
|
||||
START_TEST (test_hosts)
|
||||
{
|
||||
R3Node * n = r3_tree_create(10);
|
||||
R3Route * route = NULL;
|
||||
match_entry * entry;
|
||||
R3Route *matched_route;
|
||||
|
||||
char * uri0 = "/foo";
|
||||
char * host0 = "foo.com";
|
||||
route = r3_tree_insert_routel(n, 0, uri0, strlen(uri0), &uri0);
|
||||
route->host.base = host0;
|
||||
route->host.len = strlen(host0);
|
||||
|
||||
char * uri1 = "/bar";
|
||||
char * host1 = "*.bar.com";
|
||||
route = r3_tree_insert_routel(n, 0, uri1, strlen(uri1), &uri1);
|
||||
route->host.base = host1;
|
||||
route->host.len = strlen(host1);
|
||||
|
||||
char * err = NULL;
|
||||
r3_tree_compile(n, &err);
|
||||
ck_assert(err == NULL);
|
||||
|
||||
entry = match_entry_create("/foo");
|
||||
entry->host.base = host0;
|
||||
entry->host.len = strlen(host0);
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri0);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
entry->host.base = "www.bar.com";
|
||||
entry->host.len = strlen("www.bar.com");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri1);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
entry->host.base = "bar.com";
|
||||
entry->host.len = strlen("bar.com");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
entry->host.base = ".bar.com";
|
||||
entry->host.len = strlen(".bar.com");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
entry->host.base = "a.bar.com";
|
||||
entry->host.len = strlen("a.bar.com");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri1);
|
||||
match_entry_free(entry);
|
||||
|
||||
r3_tree_free(n);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
|
||||
Suite* r3_suite (void) {
|
||||
Suite *suite = suite_create("r3 host tests");
|
||||
TCase *tcase = tcase_create("testcase");
|
||||
tcase_add_test(tcase, test_hosts);
|
||||
suite_add_tcase(suite, tcase);
|
||||
return suite;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
int number_failed;
|
||||
Suite *suite = r3_suite();
|
||||
SRunner *runner = srunner_create(suite);
|
||||
srunner_run_all(runner, CK_NORMAL);
|
||||
number_failed = srunner_ntests_failed(runner);
|
||||
srunner_free(runner);
|
||||
return number_failed;
|
||||
}
|
105
tests/check_http_scheme.c
Normal file
105
tests/check_http_scheme.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "r3.h"
|
||||
#include "r3_slug.h"
|
||||
|
||||
START_TEST (test_http_scheme)
|
||||
{
|
||||
R3Node * n = r3_tree_create(10);
|
||||
R3Route * route = NULL;
|
||||
match_entry * entry;
|
||||
R3Route *matched_route;
|
||||
|
||||
char * uri0 = "/foo";
|
||||
route = r3_tree_insert_routel(n, 0, uri0, strlen(uri0), &uri0);
|
||||
|
||||
char * uri1 = "/bar";
|
||||
route = r3_tree_insert_routel(n, 0, uri1, strlen(uri1), &uri1);
|
||||
route->http_scheme = SCHEME_HTTPS;
|
||||
|
||||
char * uri2 = "/boo";
|
||||
route = r3_tree_insert_routel(n, 0, uri2, strlen(uri2), &uri2);
|
||||
route->http_scheme = SCHEME_HTTP | SCHEME_HTTPS;
|
||||
|
||||
char * err = NULL;
|
||||
r3_tree_compile(n, &err);
|
||||
ck_assert(err == NULL);
|
||||
|
||||
|
||||
entry = match_entry_create("/foo");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri0);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/foo");
|
||||
entry->http_scheme = SCHEME_HTTP;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri0);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
entry->http_scheme = SCHEME_HTTP;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
entry->http_scheme = SCHEME_HTTPS;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri1);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/boo");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/boo");
|
||||
entry->http_scheme = SCHEME_HTTP;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri2);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/boo");
|
||||
entry->http_scheme = SCHEME_HTTPS;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri2);
|
||||
match_entry_free(entry);
|
||||
|
||||
r3_tree_free(n);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
|
||||
Suite* r3_suite (void) {
|
||||
Suite *suite = suite_create("r3 remote_addr tests");
|
||||
TCase *tcase = tcase_create("testcase");
|
||||
tcase_add_test(tcase, test_http_scheme);
|
||||
suite_add_tcase(suite, tcase);
|
||||
return suite;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
int number_failed;
|
||||
Suite *suite = r3_suite();
|
||||
SRunner *runner = srunner_create(suite);
|
||||
srunner_run_all(runner, CK_NORMAL);
|
||||
number_failed = srunner_ntests_failed(runner);
|
||||
srunner_free(runner);
|
||||
return number_failed;
|
||||
}
|
59
tests/check_json.c
Normal file
59
tests/check_json.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* check_json.c
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
#include <json-c/json.h>
|
||||
#include "r3.h"
|
||||
#include "r3_slug.h"
|
||||
#include "r3_json.h"
|
||||
#include "zmalloc.h"
|
||||
|
||||
START_TEST (test_json_encode)
|
||||
{
|
||||
R3Node * n;
|
||||
n = r3_tree_create(10);
|
||||
|
||||
ck_assert(n);
|
||||
|
||||
r3_tree_insert_path(n, "/zoo", NULL);
|
||||
r3_tree_insert_path(n, "/foo", NULL);
|
||||
r3_tree_insert_path(n, "/bar", NULL);
|
||||
|
||||
r3_tree_insert_route(n, METHOD_GET, "/post/get", NULL);
|
||||
r3_tree_insert_route(n, METHOD_POST, "/post/post", NULL);
|
||||
|
||||
r3_tree_compile(n, NULL);
|
||||
|
||||
json_object * obj = r3_node_to_json_object(n);
|
||||
ck_assert(obj);
|
||||
|
||||
const char *json = r3_node_to_json_pretty_string(n);
|
||||
|
||||
printf("JSON: %s\n",json);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite* r3_suite (void) {
|
||||
Suite *suite = suite_create("json test");
|
||||
TCase *tcase = tcase_create("json test");
|
||||
tcase_add_test(tcase, test_json_encode);
|
||||
suite_add_tcase(suite, tcase);
|
||||
return suite;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
int number_failed;
|
||||
Suite *suite = r3_suite();
|
||||
SRunner *runner = srunner_create(suite);
|
||||
srunner_run_all(runner, CK_NORMAL);
|
||||
number_failed = srunner_ntests_failed(runner);
|
||||
srunner_free(runner);
|
||||
return number_failed;
|
||||
}
|
||||
|
216
tests/check_remote_addr.c
Normal file
216
tests/check_remote_addr.c
Normal file
|
@ -0,0 +1,216 @@
|
|||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "r3.h"
|
||||
#include "r3_slug.h"
|
||||
|
||||
START_TEST (test_remote_addrs)
|
||||
{
|
||||
R3Node * n = r3_tree_create(10);
|
||||
R3Route * route = NULL;
|
||||
match_entry * entry;
|
||||
R3Route *matched_route;
|
||||
|
||||
char * uri0 = "/foo";
|
||||
route = r3_tree_insert_routel(n, 0, uri0, strlen(uri0), &uri0);
|
||||
route->remote_addr_v4 = 0;
|
||||
route->remote_addr_v4_bits = 0;
|
||||
|
||||
char * uri1 = "/bar";
|
||||
route = r3_tree_insert_routel(n, 0, uri1, strlen(uri1), &uri1);
|
||||
route->remote_addr_v4 = inet_network("127.0.0.1");
|
||||
route->remote_addr_v4_bits = 32;
|
||||
|
||||
char * uri2 = "/boo";
|
||||
route = r3_tree_insert_routel(n, 0, uri2, strlen(uri2), &uri2);
|
||||
route->remote_addr_v4 = inet_network("127.0.0.1");
|
||||
route->remote_addr_v4_bits = 24;
|
||||
|
||||
char * err = NULL;
|
||||
r3_tree_compile(n, &err);
|
||||
ck_assert(err == NULL);
|
||||
|
||||
|
||||
entry = match_entry_create("/foo");
|
||||
entry->remote_addr.base = "127.0.0.1";
|
||||
entry->remote_addr.len = sizeof("127.0.0.1") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri0);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
entry->remote_addr.base = "127.0.0.1";
|
||||
entry->remote_addr.len = sizeof("127.0.0.1") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri1);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
entry->remote_addr.base = "127.0.0.2";
|
||||
entry->remote_addr.len = sizeof("127.0.0.2") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/boo");
|
||||
entry->remote_addr.base = "127.0.0.1";
|
||||
entry->remote_addr.len = sizeof("127.0.0.1") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri2);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/boo");
|
||||
entry->remote_addr.base = "127.0.0.2";
|
||||
entry->remote_addr.len = sizeof("127.0.0.2") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri2);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/boo");
|
||||
entry->remote_addr.base = "127.0.1.2";
|
||||
entry->remote_addr.len = sizeof("127.0.1.2") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/boo");
|
||||
entry->remote_addr.base = "127.0.1.333"; // invalid ip address
|
||||
entry->remote_addr.len = sizeof("127.0.1.333") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
r3_tree_free(n);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
void parse_ipv6(const char *ipv6, int nmask, R3Route * route)
|
||||
{
|
||||
struct in6_addr addr6;
|
||||
int ret = inet_pton(AF_INET6, ipv6, (void *)&addr6);
|
||||
ck_assert(ret == 1);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
route->remote_addr_v6[i] = ntohl(addr6.__in6_u.__u6_addr32[i]);
|
||||
|
||||
if (nmask >= 32) {
|
||||
route->remote_addr_v6_bits[i] = 32;
|
||||
} else if (nmask > 0) {
|
||||
route->remote_addr_v6_bits[i] = nmask;
|
||||
} else {
|
||||
route->remote_addr_v6_bits[i] = 0;
|
||||
}
|
||||
|
||||
nmask -= 32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
START_TEST (test_remote_addrs_ipv6)
|
||||
{
|
||||
R3Node * n = r3_tree_create(10);
|
||||
R3Route * route = NULL;
|
||||
match_entry * entry;
|
||||
R3Route *matched_route;
|
||||
|
||||
char * uri0 = "/foo";
|
||||
route = r3_tree_insert_routel(n, 0, uri0, strlen(uri0), &uri0);
|
||||
parse_ipv6("fe80:fe80::1", 128, route); // "fe80:fe80::1"
|
||||
|
||||
char * uri1 = "/bar";
|
||||
route = r3_tree_insert_routel(n, 0, uri1, strlen(uri1), &uri1);
|
||||
parse_ipv6("fe80:fe80::", 32, route); // "fe80:fe80::/32"
|
||||
|
||||
char * uri2 = "/goo";
|
||||
route = r3_tree_insert_routel(n, 0, uri2, strlen(uri2), &uri2);
|
||||
parse_ipv6("::1", 128, route); // "::1"
|
||||
|
||||
char * err = NULL;
|
||||
r3_tree_compile(n, &err);
|
||||
ck_assert(err == NULL);
|
||||
|
||||
entry = match_entry_create("/foo");
|
||||
entry->remote_addr.base = "fe80:fe80::1";
|
||||
entry->remote_addr.len = sizeof("fe80:fe80::1") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri0);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/foo");
|
||||
entry->remote_addr.base = "fe80:fe80::2";
|
||||
entry->remote_addr.len = sizeof("fe80:fe80::2") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/foo");
|
||||
entry->remote_addr.base = "fe88:fe80::1";
|
||||
entry->remote_addr.len = sizeof("fe88:fe80::1") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
entry->remote_addr.base = "fe80:fe80::1";
|
||||
entry->remote_addr.len = sizeof("fe80:fe80::1") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri1);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/bar");
|
||||
entry->remote_addr.base = "fe88:fe80::1";
|
||||
entry->remote_addr.len = sizeof("fe88:fe80::1") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/goo");
|
||||
entry->remote_addr.base = "::1";
|
||||
entry->remote_addr.len = sizeof("::1") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri2);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/goo");
|
||||
entry->remote_addr.base = "::2";
|
||||
entry->remote_addr.len = sizeof("::2") - 1;
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route == NULL);
|
||||
match_entry_free(entry);
|
||||
|
||||
r3_tree_free(n);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
Suite* r3_suite (void) {
|
||||
Suite *suite = suite_create("r3 remote_addr tests");
|
||||
TCase *tcase = tcase_create("testcase");
|
||||
tcase_add_test(tcase, test_remote_addrs);
|
||||
tcase_add_test(tcase, test_remote_addrs_ipv6);
|
||||
suite_add_tcase(suite, tcase);
|
||||
return suite;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
int number_failed;
|
||||
Suite *suite = r3_suite();
|
||||
SRunner *runner = srunner_create(suite);
|
||||
srunner_run_all(runner, CK_NORMAL);
|
||||
number_failed = srunner_ntests_failed(runner);
|
||||
srunner_free(runner);
|
||||
return number_failed;
|
||||
}
|
2061
tests/check_routes.c
Normal file
2061
tests/check_routes.c
Normal file
File diff suppressed because it is too large
Load diff
117
tests/check_routes2.c
Normal file
117
tests/check_routes2.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "r3.h"
|
||||
#include "r3_slug.h"
|
||||
|
||||
START_TEST (greedy_pattern)
|
||||
{
|
||||
R3Node * n = r3_tree_create(10);
|
||||
match_entry * entry;
|
||||
R3Route *matched_route;
|
||||
|
||||
char * uri0 = "/foo{:.*}";
|
||||
r3_tree_insert_routel(n, 0, uri0, strlen(uri0), &uri0);
|
||||
|
||||
char * err = NULL;
|
||||
r3_tree_compile(n, &err);
|
||||
ck_assert(err == NULL);
|
||||
|
||||
entry = match_entry_create("/foo/bar");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri0);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/foo");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri0);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/foo/");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri0);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/foo/bar/foo/mmasdfasdfasd/f/asdf/as/df");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri0);
|
||||
match_entry_free(entry);
|
||||
|
||||
r3_tree_free(n);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (common_pattern)
|
||||
{
|
||||
R3Node * n = r3_tree_create(10);
|
||||
match_entry * entry;
|
||||
R3Route *r, *matched_route;
|
||||
|
||||
char * uri0 = "/foo/{slug}";
|
||||
r = r3_tree_insert_routel(n, 0, uri0, strlen(uri0), &uri0);
|
||||
ck_assert(r != NULL);
|
||||
char * uri1 = "/foo/{slug}/bar";
|
||||
r = r3_tree_insert_routel(n, 0, uri1, strlen(uri1), &uri1);
|
||||
ck_assert(r != NULL);
|
||||
|
||||
char * err = NULL;
|
||||
r3_tree_compile(n, &err);
|
||||
ck_assert(err == NULL);
|
||||
|
||||
entry = match_entry_create("/foo/bar");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri0);
|
||||
match_entry_free(entry);
|
||||
|
||||
entry = match_entry_create("/foo/bar/bar");
|
||||
matched_route = r3_tree_match_route(n, entry);
|
||||
ck_assert(matched_route != NULL);
|
||||
ck_assert(matched_route->data == &uri1);
|
||||
match_entry_free(entry);
|
||||
|
||||
r3_tree_free(n);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (incomplete_pattern)
|
||||
{
|
||||
R3Node * n = r3_tree_create(10);
|
||||
R3Route * r;
|
||||
|
||||
char * uri0 = "{slug";
|
||||
r = r3_tree_insert_routel(n, 0, uri0, strlen(uri0), &uri0);
|
||||
ck_assert(r == NULL);
|
||||
char * uri1 = "/foo/{slug";
|
||||
r = r3_tree_insert_routel(n, 0, uri1, strlen(uri1), &uri1);
|
||||
ck_assert(r == NULL);
|
||||
|
||||
r3_tree_free(n);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite* r3_suite (void) {
|
||||
Suite *suite = suite_create("r3 routes2 tests");
|
||||
TCase *tcase = tcase_create("testcase");
|
||||
tcase_add_test(tcase, greedy_pattern);
|
||||
tcase_add_test(tcase, common_pattern);
|
||||
tcase_add_test(tcase, incomplete_pattern);
|
||||
suite_add_tcase(suite, tcase);
|
||||
return suite;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
int number_failed;
|
||||
Suite *suite = r3_suite();
|
||||
SRunner *runner = srunner_create(suite);
|
||||
srunner_run_all(runner, CK_NORMAL);
|
||||
number_failed = srunner_ntests_failed(runner);
|
||||
srunner_free(runner);
|
||||
return number_failed;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* check_slug.c
|
||||
* Copyright (C) 2014 c9s <c9s@c9smba.local>
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
@ -9,9 +9,9 @@
|
|||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
#include "r3.h"
|
||||
#include "r3_str.h"
|
||||
#include "str_array.h"
|
||||
#include "zmalloc.h"
|
||||
#include "r3_slug.h"
|
||||
#include "slug.h"
|
||||
#include "r3_debug.h"
|
||||
|
||||
START_TEST (test_pattern_to_opcode)
|
||||
{
|
||||
|
@ -22,84 +22,178 @@ START_TEST (test_pattern_to_opcode)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_slug_compile)
|
||||
START_TEST (test_r3_slug_compile)
|
||||
{
|
||||
char * path = "/user/{id}";
|
||||
char * c = NULL;
|
||||
ck_assert_str_eq( c = slug_compile(path, strlen(path) ) , "/user/([^/]+)" );
|
||||
zfree(c);
|
||||
ck_assert_str_eq( c = r3_slug_compile(path, strlen(path) ) , "^/user/([^/]+)" );
|
||||
free(c);
|
||||
|
||||
char * path2 = "/what/{id}-foo";
|
||||
ck_assert_str_eq( c = slug_compile(path2, strlen(path2) ) , "/what/([^/]+)-foo" );
|
||||
zfree(c);
|
||||
ck_assert_str_eq( c = r3_slug_compile(path2, strlen(path2) ) , "^/what/([^/]+)-foo" );
|
||||
free(c);
|
||||
|
||||
char * path3 = "-{id}";
|
||||
ck_assert_str_eq( c = slug_compile(path3, strlen(path3)), "-([^/]+)" );
|
||||
zfree(c);
|
||||
ck_assert_str_eq( c = r3_slug_compile(path3, strlen(path3)), "^-([^/]+)" );
|
||||
free(c);
|
||||
|
||||
char * path4 = "-{idx:\\d{3}}";
|
||||
ck_assert_str_eq( c = slug_compile(path4, strlen(path4)), "-(\\d{3})" );
|
||||
zfree(c);
|
||||
ck_assert_str_eq( c = r3_slug_compile(path4, strlen(path4)), "^-(\\d{3})" );
|
||||
free(c);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_contains_slug)
|
||||
{
|
||||
char * path = "/user/{id}/{name}";
|
||||
ck_assert( contains_slug(path) );
|
||||
char *test_str = "/user/{id}/{name}";
|
||||
ck_assert( r3_path_contains_slug_char(test_str, strlen(test_str)) );
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_slug_find_pattern)
|
||||
START_TEST (test_r3_slug_find_pattern)
|
||||
{
|
||||
int len;
|
||||
char * namerex = slug_find_pattern("{name:\\s+}", &len);
|
||||
unsigned int len;
|
||||
char *test_str = "{name:\\s+}";
|
||||
const char * namerex = r3_slug_find_pattern(test_str, strlen(test_str), &len);
|
||||
ck_assert( strncmp(namerex, "\\s+", len) == 0 );
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
START_TEST (test_slug_find_placeholder)
|
||||
START_TEST (test_r3_slug_find_name)
|
||||
{
|
||||
int slug_len = 0;
|
||||
char * slug;
|
||||
slug = slug_find_placeholder("/user/{name:\\s+}/to/{id}", &slug_len);
|
||||
unsigned int len;
|
||||
char *test_str = "{name:\\s+}";
|
||||
const char * namerex = r3_slug_find_name(test_str, strlen(test_str), &len);
|
||||
ck_assert( strncmp(namerex, "name", len) == 0 );
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_r3_slug_find_name_without_pattern)
|
||||
{
|
||||
unsigned int len;
|
||||
char *test_str = "{name}";
|
||||
const char * namerex = r3_slug_find_name(test_str, strlen(test_str), &len);
|
||||
ck_assert( strncmp(namerex, "name", len) == 0 );
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_r3_slug_find_name_with_multiple_slug)
|
||||
{
|
||||
unsigned int len;
|
||||
char *test_str = "{name}/{name2}";
|
||||
const char * namerex = r3_slug_find_name(test_str, strlen(test_str), &len);
|
||||
ck_assert( strncmp(namerex, "name", len) == 0 );
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_r3_slug_find_placeholder)
|
||||
{
|
||||
unsigned int slug_len = 0;
|
||||
const char * slug;
|
||||
char *test_str = "/user/{name:\\s+}/to/{id}";
|
||||
slug = r3_slug_find_placeholder(test_str, strlen(test_str), &slug_len);
|
||||
ck_assert( strncmp(slug, "{name:\\s+}", slug_len) == 0 );
|
||||
|
||||
|
||||
slug = slug_find_placeholder("/user/{idx:\\d{3}}/to/{idy:\\d{3}}", &slug_len);
|
||||
test_str = "/user/{idx:\\d{3}}/to/{idy:\\d{3}}";
|
||||
slug = r3_slug_find_placeholder(test_str, strlen(test_str), &slug_len);
|
||||
ck_assert( slug_len == strlen("{idx:\\d{3}}") );
|
||||
ck_assert( strncmp(slug, "{idx:\\d{3}}", slug_len) == 0 );
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_inside_slug)
|
||||
START_TEST (test_r3_inside_slug)
|
||||
{
|
||||
int slug_len = 0;
|
||||
char * pattern = "/user/{name:\\s+}/to/{id}";
|
||||
char * offset = strchr(pattern, '{') + 2;
|
||||
ck_assert( (int)inside_slug(pattern, strlen(pattern), offset) );
|
||||
ck_assert( *(inside_slug(pattern, strlen(pattern), offset)) == '{' );
|
||||
ck_assert( ! inside_slug(pattern, strlen(pattern), pattern) );
|
||||
ck_assert( r3_inside_slug(pattern, strlen(pattern), offset, NULL) != NULL );
|
||||
ck_assert( *(r3_inside_slug(pattern, strlen(pattern), offset, NULL)) == '{' );
|
||||
ck_assert( ! r3_inside_slug(pattern, strlen(pattern), pattern, NULL) );
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_slug_count)
|
||||
START_TEST (test_incomplete_slug)
|
||||
{
|
||||
int slug_len = 0;
|
||||
int cnt = 0;
|
||||
char * errstr = NULL;
|
||||
char * pattern = "/user/{name:\\d{3}}/to/{id";
|
||||
cnt = r3_slug_count(pattern, strlen(pattern), &errstr);
|
||||
ck_assert_int_eq(cnt, -1);
|
||||
ck_assert(errstr != NULL);
|
||||
printf("%s\n",errstr);
|
||||
free(errstr);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
/*
|
||||
START_TEST (test_slug_parse_with_pattern)
|
||||
{
|
||||
char * pattern = "/user/{name:\\d{3}}";
|
||||
char * errstr = NULL;
|
||||
r3_slug_t s;
|
||||
int ret;
|
||||
ret = r3_slug_parse(&s, pattern, strlen(pattern), pattern, &errstr);
|
||||
ck_assert(ret);
|
||||
|
||||
char * out = r3_slug_to_str(&s);
|
||||
ck_assert(out);
|
||||
printf("%s\n",out);
|
||||
free(out);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
START_TEST (test_slug_parse_without_pattern)
|
||||
{
|
||||
char * pattern = "/user/{name}";
|
||||
char * errstr = NULL;
|
||||
r3_slug_t *s = r3_slug_new(pattern, strlen(pattern));
|
||||
int ret;
|
||||
ret = r3_slug_parse(s, pattern, strlen(pattern), pattern, &errstr);
|
||||
ck_assert(s);
|
||||
|
||||
char * out = r3_slug_to_str(s);
|
||||
ck_assert(out);
|
||||
printf("%s\n",out);
|
||||
free(out);
|
||||
|
||||
r3_slug_free(s);
|
||||
}
|
||||
END_TEST
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
START_TEST (test_r3_slug_count)
|
||||
{
|
||||
int cnt = 0;
|
||||
char * pattern = "/user/{name:\\s+}/to/{id}";
|
||||
ck_assert_int_eq( slug_count(pattern, strlen(pattern) ), 2 );
|
||||
char * errstr = NULL;
|
||||
cnt = r3_slug_count(pattern, strlen(pattern), &errstr);
|
||||
ck_assert_int_eq(cnt, 2);
|
||||
if(errstr) free(errstr);
|
||||
|
||||
char * pattern2 = "/user/{name:\\d{3}}/to/{id}";
|
||||
ck_assert_int_eq( slug_count(pattern2, strlen(pattern) ), 2 );
|
||||
cnt = r3_slug_count(pattern2, strlen(pattern2), &errstr);
|
||||
ck_assert_int_eq(cnt, 2);
|
||||
if(errstr) free(errstr);
|
||||
|
||||
char * pattern3 = "/user/{name:\\d{3}}/to/{id}";
|
||||
cnt = r3_slug_count(pattern3, strlen(pattern3), &errstr);
|
||||
ck_assert_int_eq(cnt, 2);
|
||||
if(errstr) free(errstr);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_slug_find_placeholder_with_broken_slug)
|
||||
START_TEST (test_r3_slug_find_placeholder_with_broken_slug)
|
||||
{
|
||||
int slug_len = 0;
|
||||
char * slug = slug_find_placeholder("/user/{name:\\s+/to/{id", &slug_len);
|
||||
ck_assert(! slug);
|
||||
unsigned int slug_len = 0;
|
||||
char *sl_test = "/user/{name:\\s+/to/{id";
|
||||
const char * slug = r3_slug_find_placeholder(sl_test, strlen(sl_test), &slug_len);
|
||||
ck_assert(slug == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
@ -109,13 +203,20 @@ Suite* r3_suite (void) {
|
|||
TCase *tcase = tcase_create("test_slug");
|
||||
tcase_set_timeout(tcase, 30);
|
||||
tcase_add_test(tcase, test_contains_slug);
|
||||
tcase_add_test(tcase, test_inside_slug);
|
||||
tcase_add_test(tcase, test_slug_find_pattern);
|
||||
tcase_add_test(tcase, test_slug_find_placeholder);
|
||||
tcase_add_test(tcase, test_slug_find_placeholder_with_broken_slug);
|
||||
tcase_add_test(tcase, test_slug_count);
|
||||
tcase_add_test(tcase, test_slug_compile);
|
||||
tcase_add_test(tcase, test_r3_inside_slug);
|
||||
tcase_add_test(tcase, test_r3_slug_find_pattern);
|
||||
tcase_add_test(tcase, test_r3_slug_find_placeholder);
|
||||
tcase_add_test(tcase, test_r3_slug_find_placeholder_with_broken_slug);
|
||||
tcase_add_test(tcase, test_r3_slug_count);
|
||||
tcase_add_test(tcase, test_r3_slug_compile);
|
||||
tcase_add_test(tcase, test_pattern_to_opcode);
|
||||
tcase_add_test(tcase, test_incomplete_slug);
|
||||
tcase_add_test(tcase, test_r3_slug_find_name);
|
||||
tcase_add_test(tcase, test_r3_slug_find_name_without_pattern);
|
||||
tcase_add_test(tcase, test_r3_slug_find_name_with_multiple_slug);
|
||||
|
||||
// tcase_add_test(tcase, test_slug_parse_with_pattern);
|
||||
// tcase_add_test(tcase, test_slug_parse_without_pattern);
|
||||
|
||||
suite_add_tcase(suite, tcase);
|
||||
return suite;
|
||||
|
|
84
tests/check_str_array.c
Normal file
84
tests/check_str_array.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* check_str_array.c
|
||||
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <check.h>
|
||||
#include "str_array.h"
|
||||
|
||||
START_TEST (test_str_array)
|
||||
{
|
||||
str_array *vars = r3_mem_alloc(sizeof(str_array));
|
||||
memset(vars, 0, sizeof(*vars));
|
||||
|
||||
char *test_str = "abc";
|
||||
ck_assert( str_array_append(vars, test_str, strlen(test_str)));
|
||||
ck_assert( vars->tokens.size == 1 );
|
||||
|
||||
char *test_str1 = "foo";
|
||||
ck_assert( str_array_append(vars, test_str1, strlen(test_str1)));
|
||||
ck_assert( vars->tokens.size == 2 );
|
||||
|
||||
char *test_str2 = "bar";
|
||||
ck_assert( str_array_append(vars, test_str2, strlen(test_str2)));
|
||||
ck_assert( vars->tokens.size == 3 );
|
||||
|
||||
char *test_str3 = "zoo";
|
||||
ck_assert( str_array_append(vars, test_str3, strlen(test_str3)));
|
||||
ck_assert( vars->tokens.size == 4 );
|
||||
|
||||
str_array_free(vars);
|
||||
free(vars);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_access_macros)
|
||||
{
|
||||
str_array *vars = r3_mem_alloc(sizeof(str_array));
|
||||
memset(vars, 0, sizeof(*vars));
|
||||
ck_assert( str_array_len(vars) == 0);
|
||||
ck_assert( str_array_cap(vars) == 0);
|
||||
|
||||
r3_vector_reserve(&vars->tokens, 4);
|
||||
ck_assert( str_array_len(vars) == 0);
|
||||
ck_assert( str_array_cap(vars) == 4);
|
||||
|
||||
char *token1 = "first";
|
||||
char *token2 = "second";
|
||||
ck_assert( str_array_append(vars, token1, strlen(token1)));
|
||||
ck_assert( str_array_append(vars, token2, strlen(token2)));
|
||||
ck_assert( str_array_len(vars) == 2);
|
||||
ck_assert( str_array_cap(vars) == 4);
|
||||
|
||||
ck_assert( strncmp(str_array_fetch(vars,0).base, "first", 5) == 0);
|
||||
ck_assert( str_array_fetch(vars,0).len == 5);
|
||||
ck_assert( strncmp(str_array_fetch(vars,1).base, "second", 6) == 0);
|
||||
ck_assert( str_array_fetch(vars,1).len == 6);
|
||||
|
||||
str_array_free(vars);
|
||||
free(vars);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite* r3_suite (void) {
|
||||
Suite *suite = suite_create("str_array test");
|
||||
TCase *tcase = tcase_create("testcase");
|
||||
tcase_add_test(tcase, test_str_array);
|
||||
tcase_add_test(tcase, test_access_macros);
|
||||
suite_add_tcase(suite, tcase);
|
||||
return suite;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
int number_failed;
|
||||
Suite *suite = r3_suite();
|
||||
SRunner *runner = srunner_create(suite);
|
||||
srunner_run_all(runner, CK_NORMAL);
|
||||
number_failed = srunner_ntests_failed(runner);
|
||||
srunner_free(runner);
|
||||
return number_failed;
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue