Compare commits

..

227 commits

Author SHA1 Message Date
ade1527bef Add pcre2 include dir (required for non-system PCRE2 installations) 2023-12-24 23:13:53 -05:00
c9s
c1aab00c40
Merge pull request #157 from Nordix/cmake-corrections 2023-11-12 22:51:25 +08:00
Björn Svensson
9887545ab0 Install headers in correct path when using CMake
pkg-config provides users with the include path `../include/r3`
while CMake installs the headers directly under `../include/`.
This corrects the installation path to match the generated r3.pc
and to get the same result as when using autoconf.
2023-11-03 09:49:16 +01:00
Björn Svensson
c105117b40
Use PCRE2 instead of PCRE (#153)
PCRE is now at end of life and is no longer actively maintained.
Lift the dependency to the next major version, i.e. PCRE2.

Implementation notes:
- Removed the pcre study option since:
  "The new API ... was simplified by abolishing the separate "study" optimizing
  function; in PCRE2, patterns are automatically optimized where possible."
- If asprintf() fails the content of the 'strp' variable is undefined.
  Lets check the return value and return NULL upon error.
- Pattern and subject can straightforwardly be cast to PCRE2_SPTR since we
  only work with 8-bit code units.
2023-10-23 12:38:24 +02:00
Björn Svensson
9168f7e4d4 Support C23 projects
Don't define true/false/bool when r3 is used by C23 projects
since C23 already has a bool type and predefined constants.
2023-10-23 10:45:18 +02:00
Björn Svensson
58d8b0c028 Use coverity-scan-action in CI
Simplifies the CI job and adds a cache to avoid downloading the 1GB+
tool archive on every run.
See: https://github.com/vapier/coverity-scan-action

Only run this job when a commit is pushed to the default branch 2.0
(like when a PR is merged) since the required Coverity token/secret
is not available when this job is triggered by forks.
2023-10-23 10:35:38 +02:00
Yo-An Lin
91405adda5
Merge pull request #152 from c9s/c9s-patch-1 2023-02-12 10:52:51 +08:00
Yo-An Lin
b6639e557f
Update coverity.yml 2022-08-24 16:05:06 +08:00
Yo-An Lin
c8123430a9
remove coverity env var since it is not working 2022-07-21 00:41:08 +08:00
Bjorn Svensson
9b69f38527
Fix leak in r3_tree_insert_pathl_ex() (#150)
Includes a testcase triggering the issue when building with
the leak sanitizer.

Resource leak (CID 355063) found by Coverity,
2022-07-20 13:23:45 +02:00
Yo-An Lin
25cc816025
Fix coverity token variable (#151) 2022-07-20 13:08:42 +02:00
Yo-An Lin
edbeb0631d
Merge pull request #149 from Nordix/coverity-in-ci
Add Coverity Scan to CI
2022-07-19 17:20:04 +08:00
Björn Svensson
04d52a6dd1 Fix Coverity warning
The return value was checked on all calls to r3_slug_find_placeholder()
except in one place, which triggered a Coverity warning.

This adds a check (assert), and enables the other asserts for non-debug
builds to catch segmentation faults early.
2021-11-10 22:20:46 +01:00
Björn Svensson
cfed420cd3 Add Coverity Scan to CI 2021-11-10 22:20:34 +01:00
Yo-An Lin
76e2770304
Merge pull request #148 from Nordix/faulty-slug-in-insert-route
Handle incomplete slug pattern when inserting a route
2021-11-05 09:27:52 +08:00
Yo-An Lin
90450def8b
Merge pull request #147 from Nordix/memory-pool-cleanups
Remove unused memory pool and memory code
2021-11-05 09:26:46 +08:00
Björn Svensson
2f3e0b01ff Handle incomplete slug in insert route 2021-11-04 20:22:10 +01:00
Björn Svensson
bf11f8e2e6 Remove unused code in memory.c/.h 2021-11-03 21:58:33 +01:00
Björn Svensson
f0dd25bd53 Remove unused memory pool 2021-11-03 21:22:48 +01:00
Yo-An Lin
e533bae646
Merge pull request #146 from Nordix/coveralls-action
Use recommended Coveralls GitHub action in CI
2021-10-28 21:14:43 +08:00
Björn Svensson
b44f052897 Use recommended Coveralls GitHub action in CI 2021-10-19 11:18:53 +02:00
Yo-An Lin
06c4fc41bf
Merge pull request #140 from Nordix/test-leak-fixes
Correcting leaks in tests
2021-10-19 02:45:18 +08:00
Yo-An Lin
35c5d13df8
Merge pull request #144 from Nordix/migrate-ci-to-github-actions
Add CI via Github Actions
2021-10-19 02:44:40 +08:00
Yo-An Lin
4f3c1cd994
Merge pull request #145 from Nordix/misaligned-address-error
Remove fixed alignment
2021-10-19 02:44:14 +08:00
Björn Svensson
5114d894e5 Remove fixed alignment 2021-10-18 11:36:54 +02:00
Björn Svensson
79a3bc7c50 Add CI via Github Actions
This is an initial setup with similarities to how we test
via TravisCI; build via autotools and build via cmake.
Additional testmatrix for sanitizer testing.
2021-10-18 09:08:42 +02:00
Björn Svensson
9c8feff07f Correcting leaks in tests
A couple of testcases did not free its `match_entry` allocations,
which triggers leak indications.
Visible when build and run using the leak sanitizer, i.e
`CFLAGS="-fno-omit-frame-pointer -fsanitize=leak" cmake ..`
2021-10-14 08:27:19 +02:00
Yo-An Lin
b13dc8aa6e
Merge pull request #131 from iresty/feature-ipv6
feature: supported to use ipv6 address match route.
2021-10-14 10:31:23 +08:00
Yo-An Lin
eca8d9992e
Merge pull request #143 from Nordix/str_array-fixes
Corrections in `str_array`
2021-10-14 10:29:49 +08:00
Yo-An Lin
c9743f3904
Merge pull request #142 from Nordix/invalid-read-corrections
Correcting buffer over-read errors
2021-10-14 10:29:19 +08:00
Björn Svensson
383717a8c7 Update the faulty access macros for str_array
Adds a testcase for the usage of the access macros, and also
updates the testsuite to only include what str_array requires.
This shows that str_array can be used separately by users.
2021-10-13 08:47:20 +02:00
Björn Svensson
673bb2f6d9 Remove declarations for nonexisting functions in str_array.h 2021-10-13 08:47:20 +02:00
Björn Svensson
4babe735e8 Remove cyclic dependency between str_array.h and r3.h
This also makes sure we dont get warnings regarding bool
redefinitions when building with clang.
2021-10-13 08:46:47 +02:00
Björn Svensson
0072b618b6 Check for existence of stdbool.h during CMake builds
The headerfile is searched for during autogen/autoconf builds to
determine when to include stdbool.h.
This let CMake builds have the same behavior.
2021-10-12 10:52:44 +02:00
Björn Svensson
00ec8b7f2b Correct buffer over-read errors
When inserting multiple routes with common slug patterns
there are reads beyond end of strings.

The scenario is added as a testcase and can be triggered by
the address-sanitizer when built using:
`CFLAGS="-fno-omit-frame-pointer -fsanitize=address" cmake ..`

Indicated as a `buffer-overflow`
2021-10-11 10:19:09 +02:00
Yo-An Lin
d2fcf2f2d4
Merge pull request #135 from fishgege/patch-1
bugfix: insert path success, but get data failed
2021-10-07 22:08:13 +08:00
Yo-An Lin
da0c89b6db
Merge pull request #137 from dthadi3/ppc64le
Travis-ci: added support for ppc64le
2021-10-07 22:07:24 +08:00
Yo-An Lin
3228fb8cb5
Merge pull request #141 from Nordix/multi-edge-corrections
Correcting issue and memory leak when using multiple edges
2021-10-07 22:07:02 +08:00
Björn Svensson
ff1ef2c148 Correcting issue with multiple edges
When `r3_node_find_common_prefix()` searches for the common prefix it
selects the first matched edge, which might not be the best match.

This issue gives memoryleaks which can be viewed in legacy
testcase `check_tree::test_insert_pathl()` by building using:
`CFLAGS="-fno-omit-frame-pointer -fsanitize=leak" cmake ..`
See testcase procedures:
    ret = r3_tree_insert_path(n, "/foo/{id}",  NULL);
    ..
    ret = r3_tree_insert_path(n, "/foo/{idx}/{idy}",  NULL);
    ..
    ret = r3_tree_insert_path(n, "/foo/{idx}/{idh}",  NULL); <-- leaks

Also added a testcase that triggers the problem including a leak for
reproduction on baseline.
2021-09-20 16:55:37 +02:00
Devendranath Thadi
ac7af9697c Travis-ci: added support for ppc64le 2020-11-18 14:14:29 +00:00
董宇
36198aefba
bugfix: insert path success, but get data failed
background : use  r3_tree_insert_pathl_ex to add path,A : abcdefg   B:abc
When the node A already exists, we set B, then  will split the node and make a branch, suboath_len  is 0 
In this case,  branch a  zero len subpath, which tree's data not set
2019-12-15 13:05:49 +08:00
Yo-An Lin
e20e48a5ce
Merge pull request #128 from iresty/bug-end-maybe-0-len
bugfix: the end matching length is 0.
2019-08-06 10:22:30 +08:00
Yuansheng
79fd587615 feature: supported to match ipv6 address. 2019-08-01 21:38:14 -04:00
Yuansheng
1b9e007dfc bugfix: make a try for endpoint. 2019-07-21 08:46:37 -04:00
Yuansheng
81f77ffe5a test: added test cases. 2019-07-21 08:46:37 -04:00
Yuansheng
0d70b8f5de bugfix: the end matching length is 0. 2019-07-21 08:46:37 -04:00
Yo-An Lin
577cfa0ccb
Merge pull request #130 from iresty/typo
change: typo.
2019-07-21 19:30:50 +08:00
Yo-An Lin
3dac164cec
Merge pull request #126 from iresty/optimize-wildcard
optimize: optimized pattern `.*`, which can be used prefix matching.
2019-07-21 19:30:37 +08:00
Yuansheng
f8a3741211 change: typo. 2019-07-21 09:14:37 +08:00
Yuansheng
845c47907f test: describe the test case name. 2019-07-21 07:48:11 +08:00
Yuansheng
2ad6b4c4f0 test: skip one test case, will fix it later. 2019-07-20 22:58:17 +08:00
Yuansheng
3364df80ee test: run test case check_routes2. 2019-07-20 22:55:44 +08:00
Yuansheng
db91289ab6 change: rename OP_EXPECT_NOLINEBREAKS to OP_GREEDY_ANY. 2019-07-20 10:52:56 -04:00
Yuansheng
d516237aab optimize: optimized pattern .*, which can be used prefix matching. 2019-07-20 10:52:56 -04:00
Yo-An Lin
41685d402d
Merge pull request #129 from iresty/make-warning
change: avoided compiling warning.
2019-07-20 18:00:48 +08:00
Yo-An Lin
27d4d3191e
Merge pull request #125 from iresty/feature-scheme
feature: supported to match http scheme.
2019-07-20 17:59:48 +08:00
Yo-An Lin
fd34c7244a
Merge branch '2.0' into feature-scheme 2019-07-20 17:56:59 +08:00
Yo-An Lin
2b332bffba
Merge pull request #127 from iresty/check-more
test: run test case `check_remote_addr` in travis.
2019-07-20 17:55:17 +08:00
Yuansheng
91cfc9d282 test: run the test case check_http_scheme. 2019-07-20 07:21:05 +08:00
Yuansheng
fdc698f924 change: avoided compiling warning message. 2019-07-20 07:12:37 +08:00
Yuansheng
0485554d44 change(tests/check_slug): avoided compiling warning. 2019-07-20 07:06:56 +08:00
Yuansheng
4b066dd0fc test: run all of the test cases. 2019-07-19 09:15:47 -04:00
Yuansheng
43666a1183 feature: supported to match http scheme. 2019-06-26 23:06:32 +08:00
Yo-An Lin
3f410ef5d4
Merge pull request #124 from iresty/make-WARN
make: avoided the warning message when do make at Ubuntu OS.
2019-06-21 12:55:15 +08:00
Yuansheng
8e18a995d8 make: avoided the warning message when do make.
change: code style, delete some useless spaces(visual code).

eg:

warning: ignoring return value of ‘asprintf’, declared with attribute warn_unused_result
2019-06-21 10:45:54 +08:00
Yo-An Lin
410f66bc1a
Merge pull request #123 from iresty/feature-remote-ip
feature: supported to match remote ip address.
2019-06-20 11:47:46 +08:00
Yuansheng
848b8efdca feature: supported to match remote ip address. 2019-06-19 23:36:37 -04:00
Yo-An Lin
2a08bc62d5
Merge pull request #122 from iresty/feature-wildcard-host
feature: supported wildcard match way for host.
2019-06-20 11:29:52 +08:00
Yuansheng
917fa6e385 change: removed useless comments. 2019-06-20 11:21:04 +08:00
Yuansheng
4ab222a308 feature: supported wildcard match way for host. 2019-06-19 10:07:14 -04:00
Yo-An Lin
9761bdeec3
Merge pull request #119 from GuacheSuede/patch-1
Quick Includes Bug fix
2019-01-23 23:02:41 +08:00
Sakamoto
69e31ce07a
Quick Includes Bug fix 2019-01-23 01:02:04 +08:00
Yo-An Lin
22a6b99b34
Merge pull request #115 from msteinert/remove-zmalloc
Remove zmalloc
2018-07-10 07:36:49 -07:00
Michael Steinert
8abc03ea56 cmake: Add some comments regarding r3.pc.in 2018-07-10 08:24:05 -05:00
Michael Steinert
b3dbf75da5 Remove zmalloc dependency
Zmalloc comes from Redis. It's purpose is to track total allocations so
that a maximum can be set. For more information see the following links:

https://groups.google.com/forum/#!topic/redis-db/dPRdpowqJsY
https://stackoverflow.com/questions/22729730/when-would-one-use-malloc-over-zmalloc

This allocator is slower, not needed, and conflicts with Zlib (when
linking statically). This patch removes Zmalloc in favor of the system
allocator. Application developers can still choose to override the
allocator in their applications, e.g. Jemalloc, TCMalloc, etc.

Fixes #104
2018-07-10 08:20:46 -05:00
Yo-An Lin
afd2c486c6
Merge pull request #113 from msteinert/cleanup-cmake
Cleanup cmake
2018-07-10 06:11:41 -07:00
Michael Steinert
e701bfd596 Add CMake to the CI matrix 2018-07-05 11:52:34 -05:00
Michael Steinert
d1c06cd3e9 Cleanup CMake build files 2018-07-05 11:43:22 -05:00
Michael Steinert
2be2a08750 Fix -Wincompatible-pointer-types warning 2018-07-05 11:43:22 -05:00
Michael Steinert
e9afe4b6cf Fix -Wdiscarded-qualifiers warnings 2018-07-05 11:43:22 -05:00
Yo-An Lin
3b9bb5745a
Merge pull request #114 from msteinert/check-tree-fixes
Fix issues with check_tree test
2018-07-06 00:42:01 +08:00
Michael Steinert
179ee52801 Attempt to update CI environment 2018-07-05 11:31:27 -05:00
Michael Steinert
081ccb2bde Fix issues with check_tree test
This patch fixes a few issues in the check_tree test:

* Fix various memory leaks in test code
* Fix a double-free in test code
* Fix a memory leak in r3_tree_compile_patterns

One memory leak remains in the library code but it isn't obvious to me
how to fix it at the moment.
2018-07-05 09:58:20 -05:00
Yo-An Lin
cf92c9dc96
Merge pull request #108 from martijnotto/2.0
Initial working version of cmake builds
2018-02-25 02:02:25 +08:00
Martijn Otto
6565274ce8 We need 3rdparty for the normal build as well 2018-02-24 17:53:02 +01:00
Martijn Otto
1fceec760f We need to link to 3rdparty to be able to build the tests 2018-02-24 17:50:33 +01:00
Martijn Otto
2f96c59929 Further WIP, got the tests to link against pthread (was broken due to pkg-config not specifying -pthread as -lpthread and cmake thus not recognizing it as a library) 2018-02-24 17:45:15 +01:00
Martijn Otto
a2396fe15b WIP on support for CMake builds, fails to build tests (probably error in FindCheck not declaring dependency on pthread) 2018-02-23 09:39:39 +01:00
Yo-An Lin
2b37680087
Merge pull request #107 from martijnotto/2.0
const correctness, branched assignment, unused variables, non-standard feature test, example UB, incorrect memset declaration
2018-02-21 23:01:03 +08:00
Martijn Otto
59d182b1c1 Use the generated config.h instead to enable feature macros 2018-02-21 15:57:25 +01:00
Martijn Otto
1eeff1598a Fix declaration of memset 2018-02-21 10:04:07 +01:00
Martijn Otto
f8131b53d6 Fix UB in examples 2018-02-21 09:50:34 +01:00
Martijn Otto
55de164023 Set the feature test macro before including the header to enable non-standard functions used by libr3 2018-02-21 09:49:54 +01:00
Martijn Otto
8d0b90f6e1 Remove unused variables 2018-02-21 09:14:31 +01:00
Martijn Otto
5cab368af2 Add extra parenthesis around branched assignments 2018-02-21 09:11:53 +01:00
Martijn Otto
021c54308a Fix const correctness warnings 2018-02-20 18:18:43 +01:00
Yo-An Lin
0d78599a57 Merge pull request #103 from yohanboniface/fix-configure
Fix syntax error in configure.ac (fix #96)
2017-08-26 16:56:50 +08:00
Yohan Boniface
234e64f584 Fix syntax error in configure.ac (fix #96) 2017-08-26 10:50:30 +02:00
Yo-An Lin
7e4c74d625 Merge pull request #98 from phynalle/2.0
Add include headers
2017-07-02 10:00:11 +08:00
phynalle
267ab34c36 Add include headers 2016-04-27 03:45:11 +09:00
c9s
cefefb239a Fix fprintf warning 2016-04-05 16:59:27 +08:00
c9s
341a29387b Merge branch 'krn_feature' of https://github.com/karantin2020/r3 into 2.0 2016-04-05 16:55:54 +08:00
karantin2020
9411039fce Router match logic corrected 2016-03-26 23:32:37 +05:00
karantin2020
ffc9e8571b Added memory files 2016-03-26 21:34:07 +05:00
karantin2020
75438ef3d3 Changed r3 memory model, made few optimizing 2016-03-22 07:23:37 +05:00
Yo-An Lin
0ba956159c Check is not running, fix check
Also update .travis.yml
2016-03-12 12:46:05 +08:00
Yo-An Lin
5a644ee046 Update README.md 2016-03-12 12:40:21 +08:00
karantin2020
ef910789c0 Added gitignored files 2016-03-08 22:30:44 +05:00
karantin2020
67fa8929f7 Clean heap after use in simple example 2016-03-08 19:52:47 +05:00
karantin2020
839001afec Changed routing example 2016-03-08 19:24:21 +05:00
karantin2020
40e0e1c2a0 Changed slug parsing 2016-03-08 14:51:42 +05:00
karantin2020
b7c0132fa9 Added routing example 2016-03-08 12:17:33 +05:00
karantin2020
400768394d Added slug parser and repaired few memory leaks 2016-03-08 11:19:54 +05:00
c9s
c609003c95 optimize c flags for development/production mode 2015-11-21 10:39:49 +08:00
c9s
44ee48724f update link 2015-11-18 14:57:01 +08:00
c9s
82611c93b5 Update readme 2015-11-18 14:56:16 +08:00
c9s
daee40756c Add -O2 flag as default 2015-11-18 14:46:18 +08:00
c9s
03031e02aa Fix r3 json writer 2015-11-18 14:27:28 +08:00
c9s
c3ef959539 Add route namespace
Summary:

    type route now becomes R3Route

Related issue #63
2015-11-18 14:16:26 +08:00
c9s
29a2a934d0 Update examples in README 2015-11-18 14:09:04 +08:00
c9s
ca5ad7497e Update changelog
Related: #63
2015-11-18 12:54:35 +08:00
c9s
49ffb454f7 Add namespace for node/edge structs
node => R3Node
edge => R3Edge

Related issue #63
2015-11-18 12:53:46 +08:00
c9s
ef55fb9cdb Another fix for stdbool types 2015-11-17 21:43:10 +08:00
c9s
1f1ab15cdd Fix for stdbool type 2015-11-17 21:40:21 +08:00
c9s
b0971e1553 Fix include 2015-11-17 21:35:18 +08:00
c9s
c128cdef13 Provide better feature checking for stdbool 2015-11-17 21:31:33 +08:00
c9s
3808cb37aa Move debug macros to private header files 2015-11-17 21:24:36 +08:00
c9s
a2f5205bc5 Update benchmark result 2015-11-17 21:18:41 +08:00
c9s
973a2cb85c Move private functions to private header files 2015-11-17 21:17:35 +08:00
c9s
6275e8724c Traivs: Add allow_failures section to matrix 2015-11-17 21:13:38 +08:00
c9s
e5ef80a200 Move private function to src/str.h 2015-11-17 21:13:38 +08:00
Yo-An Lin
5e6252d8a3 Merge pull request #91 from RickySu/2.0-fix
add include config.h
2015-11-17 21:08:25 +08:00
c9s
63577cfecc Remove unused str_repeat function 2015-11-17 21:06:59 +08:00
Ricky Su
88dc323f7f add include config.h 2015-11-17 20:59:17 +08:00
c9s
38651578f9 Update matrix 2015-11-17 18:41:58 +08:00
c9s
122d85d9e1 Remove unused str* function decl 2015-11-17 18:33:33 +08:00
c9s
520dba47a5 ASAN test to be allowed failure 2015-11-17 18:32:51 +08:00
c9s
dbc18a86c4 __attribute__ 2015-11-17 18:32:51 +08:00
c9s
2484b7328c Add clang compiler to the matrix 2015-11-17 18:32:51 +08:00
c9s
af8acd0286 use __attribute aligned to align struct 2015-11-17 18:32:51 +08:00
c9s
35b8bb1cf2 Adjust window size 2015-11-17 18:32:51 +08:00
c9s
84a7d83812 Add padding to node struct 2015-11-17 18:32:51 +08:00
c9s
0407c48884 Add 2.0 to changelog 2015-11-17 18:32:51 +08:00
c9s
b52af2acbd Add padding to edge structure 2015-11-17 18:32:51 +08:00
c9s
150f7953a2 Remove unneccessary comments from struct 2015-11-17 18:32:50 +08:00
c9s
acb2ff1767 Move variable positions 2015-11-17 18:32:50 +08:00
c9s
cd430968ef Add GNU likely/unlikely support 2015-11-17 18:32:50 +08:00
c9s
4ce9068963 Cache pointer of edge 2015-11-17 18:32:50 +08:00
c9s
d4850d3564 Make edges as an array
Fixes: #89
2015-11-17 18:32:50 +08:00
c9s
28726b27af Fix r3_str.h header protection 2015-11-17 18:27:25 +08:00
c9s
89e4517772 Fix zero-length path insertion
Fixes: #86, #85
2015-11-17 16:10:26 +08:00
c9s
f795785dce Implement faster edge branching 2015-11-17 16:08:01 +08:00
c9s
2f3bcb7116 refactoring testcase group 2015-11-17 15:37:10 +08:00
c9s
b70c239b2c Fix benchmark warning 2015-11-17 15:37:10 +08:00
c9s
12947c74d0 update benchmark 2015-11-10 19:59:21 +08:00
c9s
3376d1b771 Judy array is not used. 2015-11-10 19:58:28 +08:00
c9s
c18c466a9b use 64 * 3 bytes instead of 220 bytes for combined pattern 2015-11-10 19:56:07 +08:00
c9s
fbe3fb66b1 Update benchmark 2015-11-10 19:48:37 +08:00
c9s
a2994cfdac Reuse restlen 2015-11-10 19:48:22 +08:00
c9s
34269aa045 Change compare_type from char to int 2015-11-10 19:36:01 +08:00
c9s
0d9fbdbff7 update benchmark result 2015-11-10 19:35:09 +08:00
c9s
659dc19b1b Replace short integer with int to fit into cacheline (assume 64byte) 2015-11-10 19:29:18 +08:00
c9s
5d35d34ab3 Put pcre_extra back 2015-11-10 19:21:53 +08:00
c9s
3e57819bdf update benchmark result 2015-11-10 19:20:48 +08:00
c9s
6234166ffd Update benchmark csv 2015-11-10 19:18:02 +08:00
c9s
5002272a33 Move pointer fields to top of the structure 2015-11-10 18:43:25 +08:00
c9s
b6d76f75c1 Update benchmark result on Macbook Pro Retina 2015-11-10 18:29:10 +08:00
c9s
5f39b73211 Add test_root_match test case 2015-10-18 07:39:50 +08:00
Yo-An Lin
031a37514e Merge pull request #83 from sjones608/cplusplus_bool
Don't typedef bool if compiling with C++
2015-10-01 09:56:22 +08:00
Steve Jones
d12296af59 Don't typedef bool if compiling with C++
If using the C++ wrapper provided by r3.hpp, you get a compiler
error when r3_define.h tries to typedef bool as C++ already has
native bool type.  Modified the guard around this typedef
to include a check for C++ compilation (!defined(__cplusplus)).
2015-09-28 11:42:45 -05:00
Yo-An Lin
eb2ff77e5c Merge pull request #82 from RickySu/memoryleak-fix
fix memory leak
2015-09-11 18:24:07 +08:00
Ricky Su
292b082c78 fix memory leak 2015-09-11 15:14:07 +08:00
Yo-An Lin
d9a7deab9a Merge pull request #81 from RickySu/headerfile-fix
add extern c in c++
2015-08-28 14:00:08 +08:00
Ricky Su
d5296df683 add extern c in c++ 2015-08-27 13:58:36 +08:00
Yo-An Lin
5eb3e483b3 Merge pull request #80 from RickySu/slug_find_name
add r3_slug_find_name and rename slug_count to r3_slug_count
2015-08-26 22:06:46 +08:00
Ricky Su
f445c261c6 add r3_slug_find_name and rename slug_count to r3_slug_count 2015-08-26 22:00:23 +08:00
c9s
3a62cb6559 Fix cmake build 2015-03-05 11:45:39 +08:00
Gasol Wu
85f1b218a5 Make build sucessful 2015-02-12 13:21:50 +08:00
Gasol Wu
50643bb25f Add missing files from Pux 2015-02-12 13:21:50 +08:00
Gasol Wu
868ee4a27b Add workable config.m4 2015-02-12 13:21:50 +08:00
Brendan Ashworth
01239c96f6 Fixes typo 2015-02-12 12:44:41 +08:00
Brendan Ashworth
ea286b2c56 Adds clib package.json for dependency management 2015-02-12 12:44:41 +08:00
Yo-An Lin
17276ae8eb Update README.md 2015-01-27 17:35:26 +08:00
Yo-An Lin
c7fcba3ee5 Merge pull request #77 from pangyre/patch-1
Update README.md
2014-11-20 15:34:43 +08:00
Ashley Pond V
607a285426 Update README.md
Simple typo, s/Memroy/Memory/;
2014-11-19 19:46:30 -08:00
c9s
82143dfacb use am slient rule 2014-09-27 16:40:06 +08:00
Yo-An Lin
0f01283e78 Merge pull request #74 from PeterDaveHello/patch-1
make CI build faster
2014-08-14 07:06:39 +08:00
Peter Dave Hello
7a39929776 make CI build faster 2014-08-13 14:29:18 +08:00
c9s
bd22e7b35c free slug object 2014-08-12 18:03:26 +08:00
Yo-An Lin
21dcdd5807 Merge pull request #70 from tonytonyjan/patch-2
Add a new binding link.
2014-08-09 17:49:45 +08:00
Yo-An Lin
84a27bdf05 Merge pull request #69 from tonytonyjan/patch-1
Update README.md
2014-08-09 17:49:27 +08:00
Yo-An Lin
b80270afe9 Merge pull request #72 from kanru/fix-pcre-return-type
Fix pcre_exec's return type
2014-08-09 17:48:52 +08:00
Kan-Ru Chen (陳侃如)
3629452fe0 Fix pcre_exec's return type
According to PCRE_EXEC(3) pcre_exec returns int.

The original code uses char but char could be either signed or
unsigned. On armel and sparc this caused segment fault because
the (rc < 0) error checking was optimized out.
2014-08-04 01:07:37 +08:00
簡煒航 (Jian Weihang)
5d22dc54af Add a new binding link. 2014-07-30 05:28:33 +08:00
簡煒航 (Jian Weihang)
302763b783 Update README.md 2014-07-29 21:47:58 +08:00
Yo-An Lin
8c5b25949e Merge pull request #65 from czchen/feature/pcre
Use Requires for libpcre in r3.pc
2014-07-05 16:04:23 +08:00
Yo-An Lin
3963bddb1f Merge pull request #64 from czchen/feature/debug
Let --enable-debug define DEBUG instead of ENABLE_DEBUG in config.h
2014-07-05 16:03:46 +08:00
ChangZhuo Chen (陳昌倬)
b3482c7792 Use Requires for libpcre in r3.pc 2014-07-04 21:27:56 +08:00
ChangZhuo Chen (陳昌倬)
6eb509beb2 Use ##__VA_ARGS__ to avoid build fail 2014-07-04 18:26:32 +08:00
ChangZhuo Chen (陳昌倬)
21d9c486c0 Use DEBUG instead of ENABLE_DEBUG in config.h 2014-07-04 18:25:22 +08:00
c9s
270fd1dd7b Fix image path 2014-06-28 01:25:29 +08:00
c9s
1dca0fb453 update changelogs 2014-06-28 00:54:43 +08:00
c9s
13900d44ba bump version to 1.3.3 2014-06-28 00:53:32 +08:00
c9s
da026219be Fix gvc graph generator function 2014-06-28 00:53:04 +08:00
c9s
72f10015d5 Fix copyright email 2014-06-27 13:24:40 +08:00
c9s
1eeae6d26e bump version to 1.3.2 2014-06-27 00:18:22 +08:00
c9s
9a9572a0d5 Fix duplicated symbol define 2014-06-27 00:05:24 +08:00
c9s
04ac654440 Fix Makefile.am for C++ 2014-06-27 00:04:23 +08:00
c9s
d00ac51809 Fix cmakelist 2014-06-27 00:04:22 +08:00
Yo-An Lin
db35d03217 Merge pull request #62 from caasi/patch-2
Update README.md
2014-06-20 10:28:24 +08:00
caasi Huang
6e468fd5b5 Update README.md
* Try to fix the example: 'Routing with conditions'.
* Add two Node.js bindings.
2014-06-20 03:09:26 +08:00
Yo-An Lin
5e00b60f79 Merge pull request #61 from czchen/feature/ppa
Add ubuntu PPA link
2014-06-19 15:22:01 +08:00
ChangZhuo Chen (陳昌倬)
cc253e069a Add ubuntu PPA link 2014-06-18 23:48:17 +08:00
c9s
7754043252 Include 3rdparty CMakeLists.txt 2014-06-18 20:54:46 +08:00
c9s
a697301c44 strndup polyfill for cmake 2014-06-18 20:47:06 +08:00
c9s
8ebaac0536 Fix CMakeLists.txt 2014-06-18 20:28:13 +08:00
c9s
fb0cb84c3c Put CMakeLists.txt back 2014-06-18 20:28:13 +08:00
c9s
25e3644630 put cmake_modules files back, we can support two build systems 2014-06-18 20:28:13 +08:00
Yo-An Lin
9eab3619c1 Merge pull request #60 from caasi/patch-1
initialize node.data
2014-06-14 05:51:59 +08:00
caasi Huang
ce4095af35 initialize node.data 2014-06-14 00:55:24 +08:00
Yo-An Lin
3e03f8ed97 Merge pull request #58 from Ronmi/debian
Rename debian package directory
2014-06-13 10:29:08 +08:00
Ronmi Ren
c103f669a0 Rename debian package directory to save package maintainers' life.
See https://github.com/c9s/r3/pull/55#issuecomment-45902891
2014-06-13 09:35:49 +08:00
Yo-An Lin
34e779c84a fix route cmp function 2014-06-12 21:01:16 +08:00
Yo-An Lin
f74126e0a8 Merge pull request #57 from whitglint/master
Add C++ example.
2014-06-12 20:35:45 +08:00
whitglint
09cdbfaa60 Fix Travis CI compile error. 2014-06-12 20:05:24 +08:00
whitglint
fb65694934 Fix Travis CI compile error. 2014-06-12 19:37:08 +08:00
whitglint
360646e680 Add C++ example. 2014-06-12 16:55:44 +08:00
98 changed files with 12065 additions and 1583 deletions

70
.github/workflows/ci.yml vendored Normal file
View 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
View 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
View file

@ -51,3 +51,8 @@ autoscan.log
r3.pc r3.pc
stamp-h1 stamp-h1
tests/bench_str.csv tests/bench_str.csv
tests/check_host
config.h.in
examples/simple
examples/simple_cpp

5
.travis-ci/after_success.sh Executable file
View 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
View 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
View 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
View file

@ -0,0 +1,8 @@
#!/bin/sh
set -ev
mkdir build && cd build
cmake -GNinja ..
ninja -v
ctest

View file

@ -1,39 +1,111 @@
language: c language: c
sudo: required
compiler: services:
- gcc - docker
git:
depth: 1
matrix: matrix:
include: include:
- compiler: gcc - compiler: gcc
env: CONFIGURE_OPTION='--enable-debug --enable-gcov --with-malloc=jemalloc' COVERALLS=yes VALGRIND=no DEBUG=yes env:
- compiler: gcc - TYPE=autotools
env: CONFIGURE_OPTION='--enable-debug --enable-gcov' COVERALLS=yes VALGRIND=yes DEBUG=yes LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/ - CONFIGURE_OPTION='--enable-debug --enable-gcov --with-malloc=jemalloc'
# -D_BSD_SOURCE=1 is for asprintf - COVERALLS=yes
- compiler: clang - VALGRIND=no
env: ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/local/clang-3.4/bin/llvm-symbolizer CFLAGS='-fsanitize=address -g -O1 -D_BSD_SOURCE=1' - DEBUG=yes
- CC=gcc
- CXX=g++
- compiler: gcc
env:
- TYPE=autotools
- CONFIGURE_OPTION='--enable-debug --enable-gcov'
- COVERALLS=yes
- VALGRIND=yes
- DEBUG=yes
- CC=gcc
- CXX=g++
- compiler: clang
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: install:
- sudo apt-get update -qq - docker exec -t build bash -c "cd /travis && .travis-ci/install.sh"
- 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
before_script:
- sudo ldconfig
script: script:
- ./autogen.sh - docker exec -t build bash -c "cd /travis && .travis-ci/script-$TYPE.sh"
- ./configure --enable-check $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
# 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
after_success: after_success:
- if [ x$COVERALLS == xyes ]; then coveralls --exclude php --exclude 3rdparty; fi - docker exec -t build bash -c "cd /travis && .travis-ci/after_success.sh"
cache:
apt: true

14
3rdparty/Makefile.am vendored
View file

@ -1,14 +0,0 @@
AM_CFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/3rdparty -Wall -std=c99
AM_LDFLAGS=$(DEPS_LIBS) $(GVC_DEPS_LIBS)
noinst_LTLIBRARIES = libr3ext.la
libr3ext_la_SOURCES = zmalloc.c
libr3ext_la_LIBADD=$(DEPS_LIBS)
# noinst_LIBRARIES = libr3ext.la
libr3ext_la_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/3rdparty -Wall -std=c99
noinst_HEADERS = \
zmalloc.h \
$(NULL)

368
3rdparty/zmalloc.c vendored
View file

@ -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

86
3rdparty/zmalloc.h vendored
View file

@ -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

View file

@ -3,6 +3,20 @@
by Yo-An Lin <yoanlin93@gmail.com> 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 ### 1.3.0 - Tue Jun 3 18:47:14 2014
- Added Incorrect slug syntax warnings - Added Incorrect slug syntax warnings

49
CMakeLists.txt Normal file
View 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()

View file

@ -1,11 +1,15 @@
SUBDIRS=3rdparty src . tests examples SUBDIRS=src . examples
if ENABLE_CHECK
SUBDIRS += tests
endif
lib_LTLIBRARIES = libr3.la lib_LTLIBRARIES = libr3.la
libr3_la_SOURCES = libr3_la_SOURCES =
libr3_la_LIBADD = 3rdparty/libr3ext.la src/libr3core.la libr3_la_LIBADD = src/libr3core.la
libr3_la_LDFLAGS = libr3_la_LDFLAGS =
AM_CFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/3rdparty -Wall -std=c99 AM_CFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include -Wall -std=c99
AM_LDFLAGS=$(DEPS_LIBS) $(GVC_DEPS_LIBS) AM_LDFLAGS=$(DEPS_LIBS) $(GVC_DEPS_LIBS)
@ -13,6 +17,8 @@ ACLOCAL_AMFLAGS=-I m4
if ENABLE_DEBUG if ENABLE_DEBUG
AM_CFLAGS += -ggdb -fprofile-arcs -ftest-coverage AM_CFLAGS += -ggdb -fprofile-arcs -ftest-coverage
else
AM_CFLAGS += -O2
endif endif
if USE_JEMALLOC if USE_JEMALLOC
@ -21,10 +27,10 @@ endif
r3_includedir = $(includedir)/r3 r3_includedir = $(includedir)/r3
r3_include_HEADERS = \ r3_include_HEADERS = \
include/memory.h \
include/r3.h \ include/r3.h \
include/r3_define.h \
include/r3_list.h \ include/r3_list.h \
include/r3_str.h \ include/r3_slug.h \
include/r3_gvc.h \ include/r3_gvc.h \
include/r3_json.h \ include/r3_json.h \
include/str_array.h \ include/str_array.h \

View file

@ -1,12 +1,12 @@
R3 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. 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 By using the prefix tree constructed in the start-up time, you can dispatch
the path to the controller with high efficiency. the path to the controller with high efficiency.
@ -25,7 +25,7 @@ Requirement
### Runtime Requirement ### Runtime Requirement
* pcre * pcre2
* (optional) graphviz version 2.38.0 (20140413.2041) * (optional) graphviz version 2.38.0 (20140413.2041)
* (optional) libjson-c-dev * (optional) libjson-c-dev
@ -43,11 +43,11 @@ API
#include <r3/r3.h> #include <r3/r3.h>
// create a router tree with 10 children capacity (this capacity can grow dynamically) // 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; 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_path(n, "/bar", &route_data); // ignore the length of path
r3_tree_insert_pathl(n, "/zoo", strlen("/zoo"), &route_data ); r3_tree_insert_pathl(n, "/zoo", strlen("/zoo"), &route_data );
@ -61,7 +61,7 @@ r3_tree_insert_pathl(n, "/user/{id:\\d+}", strlen("/user/{id:\\d+}"), &route_dat
// if you want to catch error, you may call the extended path function for insertion // if you want to catch error, you may call the extended path function for insertion
int data = 10; int data = 10;
char *errstr = NULL; char *errstr = NULL;
node *ret = r3_tree_insert_pathl_ex(n, "/foo/{name:\\d{5}", strlen("/foo/{name:\\d{5}"), NULL, &data, &errstr); R3Node *ret = r3_tree_insert_pathl_ex(n, "/foo/{name:\\d{5}", strlen("/foo/{name:\\d{5}"), NULL, &data, &errstr);
if (ret == NULL) { if (ret == NULL) {
// failed insertion // failed insertion
printf("error: %s\n", errstr); printf("error: %s\n", errstr);
@ -83,9 +83,9 @@ if (err != 0) {
r3_tree_dump(n, 0); r3_tree_dump(n, 0);
// match a route // match a route
node *matched_node = r3_tree_matchl(n, "/foo/bar", strlen("/foo/bar"), NULL); R3Node *matched_node = r3_tree_matchl(n, "/foo/bar", strlen("/foo/bar"), NULL);
if (matched_node) { if (matched_node) {
int ret = *( (*int) matched_node->data ); int ret = *( (int*) matched_node->data );
} }
// release the tree // release the tree
@ -114,18 +114,18 @@ entry->request_method = METHOD_POST;
entry->request_method = METHOD_GET | 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 ```c
node *matched_node = r3_tree_match_entry(n, entry); R3Node * matched_node = r3_tree_match_entry(n, entry);
``` ```
**Release Memroy** **Release Memory**
To release the memory, you may call `r3_tree_free(node *tree)` to release the whole tree structure, 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. `node*`, `edge*`, `route*` objects that were inserted into the tree will be freed.
@ -141,7 +141,7 @@ n = r3_tree_create(10);
int route_data = 3; int route_data = 3;
// insert the route path into the router tree // 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 ); r3_tree_insert_routel(n, METHOD_GET | METHOD_POST, "/blog/post", sizeof("/blog/post") - 1, &route_data );
char *errstr = NULL; char *errstr = NULL;
@ -156,15 +156,15 @@ if (err != 0) {
// in your http server handler // in your http server handler
// create the match entry for capturing dynamic variables. // create the match entry for capturing dynamic variables.
match_entry * entry = match_entry_create("/foo/bar"); match_entry * entry = match_entry_create("/blog/post");
entry->request_method = METHOD_GET; entry->request_method = METHOD_GET;
route *matched_route = r3_tree_match_route(n, entry); R3Route *matched_R3Route = r3_tree_match_route(n, entry);
matched_route->data; // get the data from matched route matched_route->data; // get the data from matched route
// free the objects at the end // free the objects at the end
r3_route_free(r1); match_entry_free(entry);
r3_tree_free(n); r3_tree_free(n);
``` ```
@ -179,7 +179,7 @@ To specify the pattern of a slug, you may write a colon to separate the slug nam
"/user/{userId:\\d+}" "/user/{userId:\\d+}"
The above route will use `\d+` as its pattern. The above R3Route will use `\d+` as its pattern.
Optimization Optimization
@ -187,13 +187,13 @@ Optimization
Simple regular expressions are optimized through a regexp pattern to opcode Simple regular expressions are optimized through a regexp pattern to opcode
translator, 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 into the `[^/]+` 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 Performance
@ -206,7 +206,7 @@ And here is the result of the router journey:
omg 9932.9 (±4.8%) i/s - 49873 in 5.033452s 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 3 runs, 5000000 iterations each run, finished in 1.308894 seconds
11460057.83 i/sec 11460057.83 i/sec
@ -214,7 +214,7 @@ r3 uses the same route path data for benchmarking, and here is the benchmark:
### The Route Paths Of Benchmark ### 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 ```ruby
#!/usr/bin/env ruby #!/usr/bin/env ruby
@ -242,7 +242,7 @@ Function prefix mapping
Rendering Routes With Graphviz Rendering Routes With Graphviz
--------------------------------------- ---------------------------------------
The `r3_tree_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.
To use graphviz, you need to enable graphviz while you run `configure`: To use graphviz, you need to enable graphviz while you run `configure`:
@ -254,7 +254,7 @@ Here is the sample code of generating graph output:
```c ```c
node * n = r3_tree_create(1); R3Node * n = r3_tree_create(1);
r3_tree_insert_path(n, "/foo/bar/baz", NULL); 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/qux", NULL);
@ -280,7 +280,7 @@ r3_tree_free(n);
``` ```
![Imgur](http://i.imgur.com/hA8QXRi.png) ![Imgur](http://imgur.com/HrUoEbI.png)
Or you can even export it with dot format: Or you can even export it with dot format:
@ -300,13 +300,13 @@ digraph g {
### Graphviz Related Functions ### Graphviz Related Functions
```c ```c
int r3_tree_render_file(const node * tree, const char * format, const char * filename); int r3_tree_render_file(const R3Node * tree, const char * format, const char * filename);
int r3_tree_render(const node * tree, const char *layout, const char * format, FILE *fp); int r3_tree_render(const R3Node * 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_dot(const R3Node * tree, const char *layout, FILE *fp);
int r3_tree_render_file(const node * tree, const char * format, const char * filename); int r3_tree_render_file(const R3Node * tree, const char * format, const char * filename);
``` ```
@ -337,17 +337,17 @@ Use case in PHP
```php ```php
// Here is the paths data structure // Here is the paths data structure
$paths = [ $paths = [
'/blog/post/{id}' => [ 'controller' => 'PostController' , 'action' => 'item' , 'method' => 'GET' ] , '/blog/post/{id}' => [ 'controller' => 'PostController' , 'action' => 'item' , 'method' => 'GET' ] ,
'/blog/post' => [ 'controller' => 'PostController' , 'action' => 'list' , 'method' => 'GET' ] , '/blog/post' => [ 'controller' => 'PostController' , 'action' => 'list' , 'method' => 'GET' ] ,
'/blog/post' => [ 'controller' => 'PostController' , 'action' => 'create' , 'method' => 'POST' ] , '/blog/post' => [ 'controller' => 'PostController' , 'action' => 'create' , 'method' => 'POST' ] ,
'/blog' => [ 'controller' => 'BlogController' , 'action' => 'list' , 'method' => 'GET' ] , '/blog' => [ 'controller' => 'BlogController' , 'action' => 'list' , 'method' => 'GET' ] ,
]; ];
$rs = r3_compile($paths, 'persisten-table-id'); $rs = r3_compile($paths, 'persisten-table-id');
$ret = r3_dispatch($rs, '/blog/post/3' ); $ret = r3_dispatch($rs, '/blog/post/3' );
list($complete, $route, $variables) = $ret; list($complete, $route, $variables) = $ret;
// matched conditions aren't done yet // 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 ) { if ( $error ) {
echo $message; // "Method not allowed", "..."; echo $message; // "Method not allowed", "...";
} }
@ -356,7 +356,7 @@ if ( $error ) {
Install 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 sudo apt-get install graphviz-dev graphviz # if you want graphviz
./autogen.sh ./autogen.sh
./configure && make ./configure && make
@ -364,7 +364,8 @@ Install
And we support debian-based distro now! And we support debian-based distro now!
sudo apt-get install build-essential autoconf automake libpcre3-dev pkg-config debhelper libtool check sudo apt-get install build-essential autoconf automake libpcre2-dev pkg-config debhelper libtool check
mv dist-debian debian
dpkg-buildpackage -b -us -uc dpkg-buildpackage -b -us -uc
sudo gdebi ../libr3*.deb sudo gdebi ../libr3*.deb
@ -382,6 +383,10 @@ And we support debian-based distro now!
./configure --with-malloc=jemalloc ./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 Binding For Other Languages
--------------------------- ---------------------------
@ -392,6 +397,16 @@ Binding For Other Languages
* Haskell r3 by @MnO2 <https://github.com/MnO2/r3> * Haskell r3 by @MnO2 <https://github.com/MnO2/r3>
* Vala r3-vala by @Ronmi <https://github.com/Ronmi/r3-vala> * 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 License

View file

@ -11,6 +11,8 @@
<script> <script>
$.get('bench_str.csv', function(data) { $.get('bench_str.csv', function(data) {
var options = { var options = {
chart: { chart: {
zoomType: 'x', zoomType: 'x',
@ -147,8 +149,18 @@
}; };
var lines = data.split(/\n/); var lines = data.split(/\n/);
// keep window size to 60 records
lines = lines.splice(-60);
$(lines).each(function(i,line) { $(lines).each(function(i,line) {
if (line == "") {
return;
}
var columns = line.split(/,/); var columns = line.split(/,/);
if (columns.length == 0) {
return;
}
var a; var a;
a = parseInt(columns[1]); a = parseInt(columns[1]);
options.series[0].data.push(a || 0); options.series[0].data.push(a || 0);

View file

@ -584,3 +584,63 @@
1402402553,11294194.69,4189904.32,49932.19,2123185.29 1402402553,11294194.69,4189904.32,49932.19,2123185.29
1402402565,11164650.78,4006360.04,55924.05,2049352.49 1402402565,11164650.78,4006360.04,55924.05,2049352.49
1402402578,9943455.54,3781047.14,45590.26,2166878.84 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.

View 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 )

View 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
)

View 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)

View 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
View file

@ -0,0 +1,3 @@
#cmakedefine HAVE_STRDUP @HAVE_STRDUP@
#cmakedefine HAVE_STRNDUP @HAVE_STRNDUP@
#cmakedefine HAVE_STDBOOL_H @HAVE_STDBOOL_H@

View file

@ -1,13 +1,16 @@
AC_INIT([r3], 1.3.0) AC_INIT([r3], 2.0.0)
AC_PREREQ([2.64]) AC_PREREQ([2.64])
AC_USE_SYSTEM_EXTENSIONS AC_USE_SYSTEM_EXTENSIONS
AC_CONFIG_HEADERS(config.h) AC_CONFIG_HEADERS(config.h)
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AM_SILENT_RULES([yes])
AM_INIT_AUTOMAKE([foreign subdir-objects]) AM_INIT_AUTOMAKE([foreign subdir-objects])
LT_INIT LT_INIT
AC_PROG_CC AC_PROG_CC
AC_PROG_CC_STDC AC_PROG_CC_STDC
AC_PROG_CXX
AC_PROG_INSTALL AC_PROG_INSTALL
AC_HEADER_STDBOOL
# older debian # older debian
AC_PROG_LIBTOOL AC_PROG_LIBTOOL
@ -20,8 +23,6 @@ AC_C_INLINE
AC_TYPE_SIZE_T AC_TYPE_SIZE_T
# Checks for library functions. # Checks for library functions.
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([gettimeofday memset strchr strdup strndup strstr]) AC_CHECK_FUNCS([gettimeofday memset strchr strdup strndup strstr])
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
@ -48,7 +49,7 @@ if test "x$found_jemalloc" == "xyes" ; then
AC_MSG_CHECKING([Checking jemalloc version]) AC_MSG_CHECKING([Checking jemalloc version])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <jemalloc/jemalloc.h>]], AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <jemalloc/jemalloc.h>]],
[[ [[
#ifdef JEMALLOC_VERSION_MAJOR > 2 #ifdef JEMALLOC_VERSION_MAJOR > 2
return 0; return 0;
#endif #endif
@ -72,14 +73,14 @@ AM_CONDITIONAL(USE_JEMALLOC, test "x$have_jemalloc" = "xyes")
# AC_DEFINE(USE_JEMALLOC, test "x$found_jemalloc" = "xyes" , "use jemalloc") # AC_DEFINE(USE_JEMALLOC, test "x$found_jemalloc" = "xyes" , "use jemalloc")
PKG_CHECK_MODULES(DEPS, [libpcre]) PKG_CHECK_MODULES(DEPS, [libpcre2-8])
AC_SUBST(DEPS_CFLAGS) AC_SUBST(DEPS_CFLAGS)
AC_SUBST(DEPS_LIBS) AC_SUBST(DEPS_LIBS)
AC_ARG_ENABLE(debug,AS_HELP_STRING([--enable-debug],[enable debug])) AC_ARG_ENABLE(debug,AS_HELP_STRING([--enable-debug],[enable debug]))
if test "x$enable_debug" = "xyes"; then if test "x$enable_debug" = "xyes"; then
AC_DEFINE(ENABLE_DEBUG, 1, "debug") AC_DEFINE(DEBUG, 1, "debug")
fi fi
AM_CONDITIONAL(ENABLE_DEBUG, test "x$enable_debug" = "xyes") AM_CONDITIONAL(ENABLE_DEBUG, test "x$enable_debug" = "xyes")
@ -115,20 +116,21 @@ AC_ARG_ENABLE(check,
AS_HELP_STRING([--enable-check], AS_HELP_STRING([--enable-check],
[enable unit testing]), [enable unit testing]),
, enable_check=unset) , enable_check=unset)
if test "x$enable_check" != "xunset" ; then if test "x$enable_check" != "xunset" ; then
PKG_CHECK_MODULES(CHECK,[check >= 0.9.4],:,[ PKG_CHECK_MODULES(CHECK,[check >= 0.9.4],:,[
ifdef([AM_PATH_CHECK], ifdef([AM_PATH_CHECK],
[AM_PATH_CHECK(,[have_check="yes"])], [AM_PATH_CHECK(,[have_check="yes"])],
AC_MSG_WARN([Check not found; cannot run unit tests!]) AC_MSG_WARN([Check not found; cannot run unit tests!])
[have_check="no"])] [have_check="no"]
)]
]) ])
fi fi
AM_CONDITIONAL(HAVE_CHECK, test x"$have_check" = "xyes") AM_CONDITIONAL(ENABLE_CHECK, test "x$enable_check" = "xyes")
AC_CONFIG_FILES([ AC_CONFIG_FILES([
r3.pc r3.pc
Makefile Makefile
3rdparty/Makefile
src/Makefile src/Makefile
tests/Makefile tests/Makefile
examples/Makefile examples/Makefile

View file

@ -2,7 +2,7 @@ Source: libr3
Priority: optional Priority: optional
Maintainer: Ronmi Ren <ronmi.ren@gmail.com> Maintainer: Ronmi Ren <ronmi.ren@gmail.com>
Build-Depends: debhelper (>= 8.0.0), automake, autotools-dev, autoconf, Build-Depends: debhelper (>= 8.0.0), automake, autotools-dev, autoconf,
libtool, libpcre3-dev, pkg-config, check libtool, libpcre2-dev, pkg-config, check
Standards-Version: 3.9.4 Standards-Version: 3.9.4
Section: libs Section: libs
Homepage: https://github.com/c9s/r3 Homepage: https://github.com/c9s/r3
@ -13,7 +13,7 @@ Architecture: any
Depends: libr3 (= ${binary:Version}) Depends: libr3 (= ${binary:Version})
Description: Development files for libr3 Description: Development files for libr3
libr3 (https://github.com/c9s/r3) is an URL router library with high libr3 (https://github.com/c9s/r3) is an URL router library with high
performance, thus, it's implemented in C. It compiles your route paths into performance, thus, it's implemented in C. It compiles your R3Route paths into
a prefix trie. a prefix trie.
. .
This package contains header files for libr3. This package contains header files for libr3.
@ -24,5 +24,5 @@ Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends} Depends: ${shlibs:Depends}, ${misc:Depends}
Description: High performance URL routing library written in C. Description: High performance URL routing library written in C.
libr3 (https://github.com/c9s/r3) is an URL router library with high libr3 (https://github.com/c9s/r3) is an URL router library with high
performance, thus, it's implemented in C. It compiles your route paths into performance, thus, it's implemented in C. It compiles your R3Route paths into
a prefix trie. a prefix trie.

View file

@ -1,5 +1,7 @@
AM_CFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) -I$(top_builddir)/include -Wall -std=c99 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 AM_LDFLAGS=$(DEPS_LIBS) $(GVC_DEPS_LIBS) $(top_builddir)/libr3.la
noinst_PROGRAMS = simple noinst_PROGRAMS = simple simple_cpp
simple_SOURCES = simple.c simple_SOURCES = simple.c
simple_cpp_SOURCES = simple_cpp.cpp

95
examples/routing.c Normal file
View 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();
}

View file

@ -1,6 +1,6 @@
/* /*
* bench.c * bench.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -10,7 +10,7 @@
int main() int main()
{ {
node * n = r3_tree_create(3); R3Node * n = r3_tree_create(3);
r3_tree_insert_path(n, "/foo/bar/baz", NULL); 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/qux", NULL);
@ -44,15 +44,16 @@ int main()
return 1; return 1;
} }
node *m; R3Node *m;
m = r3_tree_match(n , "/qux/bar/corge", NULL); m = r3_tree_match(n , "/qux/bar/corge", NULL);
match_entry * e = match_entry_createl("/garply/baz/grault", strlen("/garply/baz/grault") ); match_entry * e = match_entry_createl("/garply/baz/grault", strlen("/garply/baz/grault") );
m = r3_tree_match_entry(n , e); m = r3_tree_match_entry(n , e);
if (m) { if (m) {
printf("Matched! %s\n", e->path); printf("Matched! %s\n", e->path.base);
} }
match_entry_free(e); match_entry_free(e);
r3_tree_free(n);
return 0; return 0;
} }

97
examples/simple_cpp.cpp Normal file
View 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;
}

View file

@ -10,7 +10,7 @@ puts <<END
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include "r3.h" #include "r3.h"
#include "r3_str.h" #include "r3_slug.h"
#include "zmalloc.h" #include "zmalloc.h"
START_TEST (test_routes) START_TEST (test_routes)

141
include/memory.h Normal file
View 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

View file

@ -1,6 +1,6 @@
/* /*
* r3.h * r3.h
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -10,10 +10,24 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <pcre.h> #define PCRE2_CODE_UNIT_WIDTH 8
#include <stdbool.h> #include <pcre2.h>
#include "r3_define.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
#include "str_array.h" #include "str_array.h"
#include "r3_slug.h"
#include "memory.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -22,126 +36,99 @@ extern "C" {
struct _edge; struct _edge;
struct _node; struct _node;
struct _route; struct _route;
typedef struct _edge edge; typedef struct _edge R3Edge;
typedef struct _node node; typedef struct _node R3Node;
typedef struct _route route; typedef struct _R3Route R3Route;
struct _node { struct _node {
edge ** edges; R3_VECTOR(R3Edge) edges;
// edge ** edge_table; R3_VECTOR(R3Route) routes;
char * combined_pattern;
pcre2_code * pcre_pattern;
pcre2_match_data * match_data;
// edges are mostly less than 255 // edges are mostly less than 255
unsigned char compare_type; // compare_type: pcre, opcode, string unsigned int compare_type; // compare_type: pcre, opcode, string
unsigned char edge_len; unsigned int endpoint; // endpoint, should be zero for non-endpoint nodes
unsigned char endpoint; // endpoint, should be zero for non-endpoint nodes
unsigned char ov_cnt; // capture vector array size for pcre
// almost less than 255 // the pointer of R3Route data
unsigned char edge_cap;
unsigned char route_len;
unsigned char route_cap;
// <-- here comes a char[1] struct padding for alignment since we have 4 char above.
/** compile-time variables here.... **/
/* the combined regexp pattern string from pattern_tokens */
pcre * pcre_pattern;
pcre_extra * pcre_extra;
route ** routes;
char * combined_pattern;
/**
* the pointer of route data
*/
void * data; void * data;
}; };
#define r3_node_edge_pattern(node,i) node->edges[i]->pattern #define r3_node_edge_pattern(node,i) node->edges.entries[i].pattern.base
#define r3_node_edge_pattern_len(node,i) node->edges[i]->pattern_len #define r3_node_edge_pattern_len(node,i) node->edges.entries[i].pattern.len
struct _edge { struct _edge {
char * pattern; // 8 bytes r3_iovec_t pattern; // 8 bytes
node * child; // 8 bytes R3Node * child; // 8 bytes
unsigned char pattern_len; // 1 byte unsigned int opcode; // 4byte
unsigned char opcode:4; // 4 bit unsigned int has_slug; // 4byte
unsigned char has_slug:1; // 1 bit
}; };
struct _route { struct _R3Route {
char * path; r3_iovec_t path;
int path_len; R3_VECTOR(r3_iovec_t) slugs;
int request_method; // can be (GET || POST)
int request_method; // can be (GET || POST) r3_iovec_t host; // required host name
char * host; // required host name
int host_len;
void * data; void * data;
char * ip_addr; r3_iovec_t remote_addr_pattern;
char * ip_mask;
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)
// todo: take of this
char * remote_addr_pattern;
int remote_addr_pattern_len;
}; };
typedef struct { typedef struct _R3Entry match_entry;
str_array * vars; struct _R3Entry {
const char * path; // current path to dispatch str_array vars;
int path_len; // the length of the current path r3_iovec_t path; // current path to dispatch
int request_method; // current request method int request_method; // current request method
void * data; // route ptr void * data; // R3Route ptr
char * host; // the request host r3_iovec_t host; // the request host
int host_len; r3_iovec_t remote_addr;
char * remote_addr; int http_scheme;
int remote_addr_len; };
} match_entry;
R3Node * r3_tree_create(int cap);
// R3Node * r3_node_create();
void r3_tree_free(R3Node * tree);
R3Edge * r3_node_connectl(R3Node * n, const char * pat, int len, int strdup, R3Node *child);
node * r3_tree_create(int cap);
node * r3_node_create();
void r3_tree_free(node * tree);
edge * r3_node_connectl(node * n, const char * pat, int len, int strdup, node *child);
#define r3_node_connect(n, pat, child) r3_node_connectl(n, pat, strlen(pat), 0, child) #define r3_node_connect(n, pat, child) r3_node_connectl(n, pat, strlen(pat), 0, child)
edge * r3_node_find_edge(const node * n, const char * pat, int pat_len); 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);
edge * r3_node_find_common_prefix(node *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);
node * r3_tree_insert_pathl(node *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)
#define r3_tree_insert_pathl(tree, path, path_len, data) r3_tree_insert_pathl_ex(tree, path, path_len, NULL , data, NULL)
route * r3_tree_insert_routel(node *tree, int method, const char *path, int path_len, void *data); R3Route * r3_tree_insert_routel(R3Node * tree, int method, const char *path, int path_len, void *data);
route * r3_tree_insert_routel_ex(node *tree, int method, const char *path, int path_len, void *data, char **errstr); 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_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), NULL, d, 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) #define r3_tree_insert_route(n,method,path,data) r3_tree_insert_routel(n, method, path, strlen(path), data)
@ -149,49 +136,51 @@ route * r3_tree_insert_routel_ex(node *tree, int method, const char *path, int p
/** /**
* The private API to insert a path * The private API to insert a path
*/ */
node * r3_tree_insert_pathl_ex(node *tree, const char *path, int path_len, route * route, void * data, char ** errstr); 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(const node * n, int level); void r3_tree_dump(const R3Node * n, int level);
edge * r3_node_find_edge_str(const node * n, const char * str, int str_len); R3Edge * r3_node_find_edge_str(const R3Node * n, const char * str, int str_len);
int r3_tree_compile(node *n, char** errstr); int r3_tree_compile(R3Node *n, char** errstr);
int r3_tree_compile_patterns(node * n, char** errstr); int r3_tree_compile_patterns(R3Node * n, char** errstr);
node * r3_tree_matchl(const node * n, const char * path, int path_len, match_entry * entry); 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) #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); // R3Node * r3_tree_match_entry(R3Node * n, match_entry * entry);
#define r3_tree_match_entry(n, entry) r3_tree_matchl(n, entry->path, entry->path_len, 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(const node *n); bool r3_node_has_slug_edges(const R3Node *n);
edge * r3_edge_createl(const 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);
route * r3_route_create(const char * path); R3Route * r3_route_create(const char * path);
route * r3_route_createl(const char * path, int path_len); // R3Route * r3_route_createl(const char * path, int path_len);
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);
int r3_route_cmp(const route *r1, const match_entry *r2); int r3_route_cmp(const R3Route *r1, const match_entry *r2);
route * r3_tree_match_route(const node *n, match_entry * entry); R3Route * r3_tree_match_route(const R3Node *n, match_entry * entry);
#define r3_route_create(p) r3_route_createl(p, strlen(p)) #define r3_route_create(p) r3_route_createl(p, strlen(p))
@ -204,13 +193,16 @@ route * r3_tree_match_route(const node *n, match_entry * entry);
#define METHOD_HEAD 2<<5 #define METHOD_HEAD 2<<5
#define METHOD_OPTIONS 2<<6 #define METHOD_OPTIONS 2<<6
#define SCHEME_HTTP 2
#define SCHEME_HTTPS 2<<1
int r3_pattern_to_opcode(const 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 { 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};
@ -221,8 +213,7 @@ match_entry * match_entry_createl(const char * path, int path_len);
void match_entry_free(match_entry * entry); void match_entry_free(match_entry * entry);
int r3_ip_cmp(const char* a, const char* b);
int r3_ip_mask_cmp(const char *a, const char* mask_str, const char* b);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -8,7 +8,7 @@
#define R3_HPP #define R3_HPP
#include <cstring> #include <cstring>
#include <r3.h> #include "r3.h"
namespace r3 { namespace r3 {
template <typename T> template <typename T>
@ -37,8 +37,8 @@ namespace r3 {
private: private:
T* p_; T* p_;
}; };
typedef Base<node> Node; typedef Base<R3Node> Node;
typedef Base<route> Route; typedef Base<R3Route> Route;
class MatchEntry : public Base<match_entry> { class MatchEntry : public Base<match_entry> {
public: public:
@ -69,7 +69,7 @@ namespace r3 {
MatchEntry& operator =(const MatchEntry&); MatchEntry& operator =(const MatchEntry&);
}; };
class Tree : public Base<node> { class Tree : public Base<R3Node> {
public: public:
explicit Tree(int cap) explicit Tree(int cap)
: Base(r3_tree_create(cap)) { : Base(r3_tree_create(cap)) {
@ -90,13 +90,13 @@ namespace r3 {
} }
Node insert_path(const char* path, void* data, char** errstr = NULL) { Node insert_path(const char* path, void* data, char** errstr = NULL) {
return r3_tree_insert_pathl_ex(get(), path, std::strlen(path), NULL, return r3_tree_insert_pathl_ex(get(), path, std::strlen(path), 0, 0,
data, errstr); data, errstr);
} }
Node insert_pathl(const char* path, int path_len, void* data, Node insert_pathl(const char* path, int path_len, void* data,
char** errstr = NULL) { char** errstr = NULL) {
return r3_tree_insert_pathl_ex(get(), path, path_len, NULL, data, return r3_tree_insert_pathl_ex(get(), path, path_len, 0, 0, data,
errstr); errstr);
} }

View file

@ -1,37 +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
#include <stdbool.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 */

View file

@ -1,6 +1,6 @@
/* /*
* r3_gvc.h * r3_gvc.h
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -11,7 +11,7 @@
#include <gvc.h> #include <gvc.h>
#include "r3.h" #include "r3.h"
void r3_tree_build_ag_nodes(Agraph_t * g, Agnode_t * ag_parent_node, const node * n, int node_cnt); 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(const node * tree, const char *layout, const char * format, FILE *fp);

View file

@ -1,6 +1,6 @@
/* /*
* r3_json.h * r3_json.h
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -11,13 +11,13 @@
#include <json-c/json.h> #include <json-c/json.h>
#include "r3.h" #include "r3.h"
json_object * r3_edge_to_json_object(const edge * e); json_object * r3_edge_to_json_object(const R3Edge * e);
json_object * r3_node_to_json_object(const node * n); json_object * r3_node_to_json_object(const R3Node * n);
json_object * r3_route_to_json_object(const route * r); json_object * r3_route_to_json_object(const R3Route * r);
const char * r3_node_to_json_string_ext(const node * n, int options); const char * r3_node_to_json_string_ext(const R3Node * n, int options);
const char * r3_node_to_json_pretty_string(const node * n); const char * r3_node_to_json_pretty_string(const R3Node * n);
const char * r3_node_to_json_string(const node * n); const char * r3_node_to_json_string(const R3Node * n);

View file

@ -1,6 +1,6 @@
/* /*
* r3_list.h * r3_list.h
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */

30
include/r3_slug.h Normal file
View 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 */

View file

@ -1,35 +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"
char * r3_slug_compile(const char * str, int len);
char * r3_slug_find_pattern(const char *s1, int *len);
char * r3_slug_find_placeholder(const char *s1, int *len);
char * r3_inside_slug(const char * needle, int needle_len, char *offset, char **errstr);
void str_repeat(char *s, const 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 */

View file

@ -1,6 +1,6 @@
/* /*
* str_array.h * str_array.h
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -8,28 +8,34 @@
#ifndef STR_ARRAY_H #ifndef STR_ARRAY_H
#define STR_ARRAY_H #define STR_ARRAY_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 { typedef struct _str_array {
char **tokens; R3_VECTOR(r3_iovec_t) slugs;
int len; R3_VECTOR(r3_iovec_t) tokens;
int cap;
} str_array; } str_array;
str_array * str_array_create(int cap); bool str_array_append(str_array * l, const char * token, unsigned int len);
bool str_array_is_full(const str_array * l);
bool str_array_resize(str_array *l, int new_cap);
bool str_array_append(str_array * list, char * token);
void str_array_free(str_array *l); void str_array_free(str_array *l);
void str_array_dump_slugs(const str_array *l);
void str_array_dump(const str_array *l); void str_array_dump(const str_array *l);
str_array * split_route_pattern(char *pattern, int pattern_len); #define str_array_fetch(t,i) t->tokens.entries[i]
#define str_array_len(t) t->tokens.size
#define str_array_fetch(t,i) t->tokens[i] #define str_array_cap(t) t->tokens.capacity
#define str_array_len(t) t->len
#define str_array_cap(t) t->cap
#endif /* !STR_ARRAY_H */ #endif /* !STR_ARRAY_H */

9
package.json Normal file
View 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
View 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
View 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

Binary file not shown.

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
View 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

File diff suppressed because it is too large Load diff

View 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

View 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);
}

View 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
View 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;
}

View 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[];

View 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
View 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

View file

@ -14,7 +14,7 @@
#include "php_r3.h" #include "php_r3.h"
// #include "ct_helper.h" // #include "ct_helper.h"
// #include "r3_functions.h" #include "r3_functions.h"
// #include "r3_mux.h" // #include "r3_mux.h"
// #include "php_expandable_mux.h" // #include "php_expandable_mux.h"
// #include "r3_controller.h" // #include "r3_controller.h"
@ -104,33 +104,3 @@ PHP_MSHUTDOWN_FUNCTION(r3) {
PHP_RINIT_FUNCTION(r3) { PHP_RINIT_FUNCTION(r3) {
return SUCCESS; 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();
}

View file

@ -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); 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; 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); 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 #define phpext_r3_ptr &r3_module_entry

View file

@ -20,7 +20,7 @@ vim:fdm=marker:et:sw=4:ts=4:sts=4:
// #include "r3_persistent.h" // #include "r3_persistent.h"
// #include "php_expandable_mux.h" // #include "php_expandable_mux.h"
// #include "hash.h" #include "hash.h"
/** /**
* new_dst = ht_copy_fun_t(NULL, src); * new_dst = ht_copy_fun_t(NULL, src);
@ -407,8 +407,8 @@ PHP_FUNCTION(r3_match)
} }
zval *z_route; zval *z_route;
z_route = php_r3_match(z_routes, path, path_len TSRMLS_CC); z_R3Route = php_r3_match(z_routes, path, path_len TSRMLS_CC);
if ( z_route != NULL ) { if ( z_R3Route != NULL ) {
*return_value = *z_route; *return_value = *z_route;
zval_copy_ctor(return_value); zval_copy_ctor(return_value);
return; return;
@ -438,7 +438,7 @@ PHP_FUNCTION(r3_persistent_dispatch)
char *ns, *filename, *path; char *ns, *filename, *path;
int ns_len, filename_len, path_len; int ns_len, filename_len, path_len;
zval *mux = NULL; zval *mux = NULL;
zval *route = NULL; zval *R3Route = NULL;
zval *z_path = NULL; zval *z_path = NULL;
/* parse parameters */ /* parse parameters */
@ -463,15 +463,15 @@ PHP_FUNCTION(r3_persistent_dispatch)
ZVAL_STRINGL(z_path, path ,path_len, 1); // no copy 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 // 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); zval_ptr_dtor(&z_path);
if ( route ) { if ( R3Route ) {
*return_value = *route; *return_value = *route;
zval_copy_ctor(return_value); zval_copy_ctor(return_value);
return; return;
} }
// route not found // R3Route not found
RETURN_FALSE; 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. // 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_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 ( 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) ) { if ( 0 == validate_request_method( z_route_options_pp, current_request_method TSRMLS_CC) ) {

View file

@ -81,7 +81,7 @@ zend_class_entry ** get_pattern_compiler_ce(TSRMLS_D) {
return ce_pattern_compiler; 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) zval * compile_route_pattern(zval *z_pattern, zval *z_options, zend_class_entry **ce_pattern_compiler TSRMLS_DC)
{ {
// zend_class_entry **ce_pattern_compiler; // 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 ); 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; return NULL;
} else if ( Z_TYPE_P(z_compiled_route) == IS_NULL ) { } else if ( Z_TYPE_P(z_compiled_route) == IS_NULL ) {
zval_ptr_dtor(&z_compiled_route); zval_ptr_dtor(&z_compiled_route);
@ -467,7 +467,7 @@ PHP_METHOD(Mux, mount) {
// zval for new route // zval for new route
zval *z_new_routes; zval *z_new_routes;
// zval for route item // zval for R3Route item
zval **z_is_pcre; // route[0] zval **z_is_pcre; // route[0]
zval **z_route_pattern; zval **z_route_pattern;
zval **z_route_callback; zval **z_route_callback;
@ -514,10 +514,10 @@ PHP_METHOD(Mux, mount) {
// $routeArgs = PatternCompiler::compile($newPattern, // $routeArgs = PatternCompiler::compile($newPattern,
// array_merge_recursive($route[3], $options) ); // 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); 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_compiled_route);
Z_ADDREF_P(z_new_routes); 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_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, 1, *z_compiled_route_pattern);
add_index_zval(z_new_routes, 2 , *z_route_callback); 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); 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; zval *z_new_route_options;
MAKE_STD_ZVAL(z_new_route_options); MAKE_STD_ZVAL(z_new_route_options);
array_init(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_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); 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); // 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; *return_value = **z_route;
zval_copy_ctor(return_value); zval_copy_ctor(return_value);
return; return;
@ -788,7 +788,7 @@ PHP_METHOD(Mux, compile) {
zend_call_method( NULL, NULL, NULL, "usort", strlen("usort"), &rv, 2, zend_call_method( NULL, NULL, NULL, "usort", strlen("usort"), &rv, 2,
z_routes, z_sort_callback TSRMLS_CC ); z_routes, z_sort_callback TSRMLS_CC );
zval_ptr_dtor(&z_sort_callback); // recycle sort callback zval 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); // 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; int path_len;
zval *z_path; 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) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &path, &path_len) == FAILURE) {
RETURN_FALSE; 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_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 ); 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); zval_ptr_dtor(&z_path);
RETURN_NULL(); RETURN_NULL();
} }
@ -908,11 +908,11 @@ PHP_METHOD(Mux, dispatch) {
zval *z_substr; 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 ) { 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; RETURN_FALSE;
} }
if ( zend_hash_index_find( Z_ARRVAL_PP(z_options) , 0 , (void**) &z_route_vars_0 ) == FAILURE ) { 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; RETURN_FALSE;
} }
@ -955,7 +955,7 @@ PHP_METHOD(Mux, dispatch) {
} }
} }
if ( z_return_route ) { if ( z_return_R3Route ) {
*return_value = *z_return_route; *return_value = *z_return_route;
zval_copy_ctor(return_value); zval_copy_ctor(return_value);
} }
@ -972,7 +972,7 @@ PHP_METHOD(Mux, match) {
} }
zval **z_route_pp = NULL; 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 ( 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 ) { if ( Z_TYPE_PP(z_route_pp) != IS_NULL ) {
*return_value = **z_route_pp; *return_value = **z_route_pp;
@ -981,8 +981,8 @@ PHP_METHOD(Mux, match) {
return; 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); 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_route != NULL ) { if ( z_R3Route != NULL ) {
*return_value = *z_route; *return_value = *z_route;
zval_copy_ctor(return_value); zval_copy_ctor(return_value);
Z_ADDREF_P(z_route); 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 ); 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 ) { 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; RETURN_FALSE;
} }
add_next_index_zval(z_routes, rv); add_next_index_zval(z_routes, rv);
@ -1115,9 +1115,9 @@ inline void mux_add_route(INTERNAL_FUNCTION_PARAMETERS)
MAKE_STD_ZVAL(z_pattern); MAKE_STD_ZVAL(z_pattern);
ZVAL_STRINGL(z_pattern, pattern, pattern_len, 1); ZVAL_STRINGL(z_pattern, pattern, pattern_len, 1);
zval *z_compiled_route = compile_route_pattern(z_pattern, z_options, NULL TSRMLS_CC); zval *z_compiled_R3Route = compile_route_pattern(z_pattern, z_options, NULL TSRMLS_CC);
if ( z_compiled_route == NULL ) { if ( z_compiled_R3Route == NULL ) {
zend_throw_exception(ce_r3_exception, "Unable to compile route pattern.", 0 TSRMLS_CC); zend_throw_exception(ce_r3_exception, "Unable to compile R3Route pattern.", 0 TSRMLS_CC);
RETURN_FALSE; RETURN_FALSE;
} }
zval_ptr_dtor(&z_pattern); 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_index_zval( z_new_route, 3 , z_options);
add_next_index_zval(z_routes, z_new_route); 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)) ) { 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); zval * z_static_routes = zend_read_property(ce_r3_mux, this_ptr, "staticRoutes", sizeof("staticRoutes")-1, 1 TSRMLS_CC);
if ( z_static_routes ) { 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 * 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); ALLOC_ZVAL(id_route);
ZVAL_COPY_VALUE(id_route, z_new_route); ZVAL_COPY_VALUE(id_route, z_new_route);
zval_copy_ctor(id_route); zval_copy_ctor(id_route);

View file

@ -6,6 +6,6 @@ libdir=@libdir@
Name: r3 Name: r3
Description: High-performance URL router library Description: High-performance URL router library
Version: @PACKAGE_VERSION@ Version: @PACKAGE_VERSION@
Requires: Requires: libpcre2-8
Libs: -L${libdir} -lr3 -lpcre Libs: -L${libdir} -lr3
CFlags: -I${includedir} CFlags: -I${includedir}

27
src/CMakeLists.txt Normal file
View 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)

View file

@ -1,10 +1,10 @@
AM_CFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) $(JSONC_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/3rdparty -Wall -std=c99 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) AM_LDFLAGS=$(DEPS_LIBS) $(GVC_DEPS_LIBS) $(JSONC_LIBS)
MAYBE_COVERAGE=--coverage MAYBE_COVERAGE=--coverage
noinst_LTLIBRARIES = libr3core.la noinst_LTLIBRARIES = libr3core.la
# lib_LIBRARIES = libr3.a # lib_LIBRARIES = libr3.a
libr3core_la_SOURCES = node.c edge.c str.c token.c match_entry.c slug.c ip.c libr3core_la_SOURCES = node.c edge.c str.c token.c match_entry.c slug.c memory.c
if ENABLE_JSON if ENABLE_JSON
libr3core_la_SOURCES += json.c libr3core_la_SOURCES += json.c
@ -23,7 +23,7 @@ MOSTLYCLEANFILES = *.gcov *.gcda *.gcno
# libr3_la_LIBADD=$(DEPS_LIBS) $(LIBOBJS) $(ALLOCA) # libr3_la_LIBADD=$(DEPS_LIBS) $(LIBOBJS) $(ALLOCA)
# libr3core_la_LIBADD=$(DEPS_LIBS) # libr3core_la_LIBADD=$(DEPS_LIBS)
# libr3core_la_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/3rdparty -Wall -std=c99 # libr3core_la_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include -Wall -std=c99
if ENABLE_GRAPHVIZ if ENABLE_GRAPHVIZ
libr3core_la_SOURCES += gvc.c libr3core_la_SOURCES += gvc.c

View file

@ -1,6 +1,6 @@
/* /*
* edge.c * edge.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -13,95 +13,76 @@
// Jemalloc memory management // Jemalloc memory management
// #include <jemalloc/jemalloc.h> // #include <jemalloc/jemalloc.h>
// PCRE
#include <pcre.h>
// Judy array
// #include <Judy.h>
#include "r3.h" #include "r3.h"
#include "r3_str.h" #include "r3_slug.h"
#include "slug.h" #include "slug.h"
#include "zmalloc.h"
#define CHECK_PTR(ptr) if (ptr == NULL) return NULL; #define CHECK_PTR(ptr) if (ptr == NULL) return NULL;
edge * r3_edge_createl(const char * pattern, int pattern_len, node * child) {
edge * e = (edge*) zmalloc( sizeof(edge) );
CHECK_PTR(e); void r3_edge_initl(R3Edge *e, const char * pattern, int pattern_len, R3Node * child)
{
e->pattern = (char*) pattern; e->pattern.base = (char*) pattern;
e->pattern_len = pattern_len; e->pattern.len = (unsigned int)pattern_len;
e->opcode = 0; // e->opcode = 0;
e->child = child; e->child = child;
e->has_slug = r3_path_contains_slug_char(e->pattern); e->has_slug = r3_path_contains_slug_char(e->pattern.base, e->pattern.len);
return e;
} }
// 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, * branch the edge pattern at "dl" offset,
* insert a dummy child between the edges. * and insert a dummy child between the edges.
* *
* * A -> [EDGE: abcdefg] -> B -> [EDGE:branch1], [EDGE:branch2]
* A -> [prefix..suffix] -> B * A -> [EDGE: abcd] -> B1 -> [efg] -> B2 (new child with copied data from B)
* A -> [prefix] -> B -> [suffix] -> New Child (Copy Data, Edges from B)
* *
*/ */
node * r3_edge_branch(edge *e, int dl) { R3Node * r3_edge_branch(R3Edge *e, int dl) {
node *new_child; R3Node * new_child;
edge *e1; R3Edge * new_edge;
char * s1 = e->pattern + dl;
int s1_len = 0; // the rest string
const char * s1 = e->pattern.base + dl;
int s1_len = e->pattern.len - dl;
// the suffix edge of the leaf // the suffix edge of the leaf
new_child = r3_tree_create(3); new_child = r3_tree_create(3);
s1_len = e->pattern_len - dl;
e1 = r3_edge_createl(zstrndup(s1, s1_len), s1_len, new_child);
// Migrate the child edges to the new edge we just created.
for ( int i = 0 ; i < e->child->edge_len ; i++ ) {
r3_node_append_edge(new_child, e->child->edges[i]);
e->child->edges[i] = NULL;
}
e->child->edge_len = 0;
// Migrate the child routes
for ( int i = 0 ; i < e->child->route_len ; i++ ) {
r3_node_append_route(new_child, e->child->routes[i]);
e->child->routes[i] = NULL;
}
e->child->route_len = 0;
// Migrate the endpoint
new_child->endpoint = e->child->endpoint;
e->child->endpoint = 0; // reset endpoint
// Migrate the data
new_child->data = e->child->data; // copy data pointer
e->child->data = NULL;
r3_node_append_edge(e->child, e1);
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 // truncate the original edge pattern
char *oldpattern = e->pattern; e->pattern.len = dl;
e->pattern = zstrndup(e->pattern, dl);
e->pattern_len = dl;
zfree(oldpattern);
return new_child; return new_child;
} }
void r3_edge_free(edge * e) { void r3_edge_free(R3Edge * e) {
zfree(e->pattern); if (e) {
if ( e->child ) { if ( e->child ) {
r3_tree_free(e->child); r3_tree_free(e->child);
}
// free itself
// free(e);
} }
// free itself
zfree(e);
} }

View file

@ -1,6 +1,6 @@
/* /*
* gvz.c * gvz.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -10,24 +10,25 @@
#include <stdlib.h> #include <stdlib.h>
#include "r3.h" #include "r3.h"
#include "r3_gvc.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) { void r3_tree_build_ag_nodes(Agraph_t * g, Agnode_t * ag_parent_node, const node * n, int * node_cnt) {
edge * e; if (!n)
return;
for ( int i = 0 ; i < n->edge_len ; i++ ) { for ( int i = 0 ; i < n->edges.size ; i++ ) {
e = n->edges[i]; edge * e = n->edges.entries + i;
(*node_cnt)++;
node_cnt++;
Agnode_t *agn_child = NULL; Agnode_t *agn_child = NULL;
Agedge_t *agn_edge = NULL; Agedge_t *agn_edge = NULL;
char *nodename = NULL; char *nodename = NULL;
if ( e && e->child && e->child->combined_pattern ) { if ( e && e->child && e->child->combined_pattern ) {
asprintf(&nodename,"%s", e->child->combined_pattern); int r = asprintf(&nodename,"%s", e->child->combined_pattern);
if (r) {};
} else { } else {
asprintf(&nodename,"#%d", node_cnt); int r = asprintf(&nodename,"#%d", *node_cnt);
if (r) {};
} }
agn_child = agnode(g, nodename, 1); agn_child = agnode(g, nodename, 1);
@ -52,11 +53,13 @@ int r3_tree_render(const node * tree, const char *layout, const char * format, F
GVC_t *gvc = NULL; GVC_t *gvc = NULL;
gvc = gvContext(); gvc = gvContext();
/* Create a simple digraph */ /* Create a simple digraph */
g = agopen("g", Agdirected, 0); // g = agopen("g", Agdirected, 0);
g = agopen("g", Agundirected, 0);
// create self node // create self node
Agnode_t *ag_root = agnode(g, "{root}", 1); 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, layout); gvLayout(gvc, g, layout);
gvRender(gvc, g, format, fp); gvRender(gvc, g, format, fp);
gvFreeLayout(gvc, g); gvFreeLayout(gvc, g);
@ -96,11 +99,13 @@ int r3_tree_render_file(const node * tree, const char * format, const char * fil
*/ */
/* Create a simple digraph */ /* Create a simple digraph */
g = agopen("g", Agdirected, 0); // g = agopen("g", Agdirected, 0);
g = agopen("g", Agundirected, 0);
// create self node // create self node
Agnode_t *ag_root = agnode(g, "{root}", 1); 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"); gvLayout(gvc, g, "dot");
gvRenderFilename(gvc, g, format, filename); gvRenderFilename(gvc, g, format, filename);

View file

@ -1,48 +0,0 @@
/*
* ip.c
* Copyright (C) 2014 c9s <c9s@c9smba.local>
*
* Distributed under terms of the MIT license.
*/
// #include "ip.h"
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h>
int r3_ip_cmp(const char* a, const char* b) {
long al = inet_addr(a);
long bl = inet_addr(b);
return al == bl;
}
int r3_ip_mask_cmp(const char *a, const char* b, const char* mask_str) {
long m = inet_addr(mask_str);
long al = inet_addr(a);
long bl = inet_addr(b);
return !((al ^ bl) & m);
}
/*
long in;
in = inet_addr("192.168.1.1");
printf("My unreadable addres is %ld\n", in);
in = inet_addr("8.8.8.8");
printf("My unreadable addres is %ld\n", in);
printf("My unreadable addres is %u %d\n", inet_addr("255.255.255.255"), 2 << 16 );
printf("My unreadable addres is %u %d\n", inet_addr("255.0.0.0") , (2 << 7) - 1 );
printf("My unreadable addres is %u %d\n", inet_addr("255.255.0.0") , (2 << 15) - 1 );
printf("My unreadable addres is %u %d\n", inet_addr("255.255.255.0") , (2 << 23) - 1 );
printf("My unreadable addres is %u\n", inet_addr("255.255.255.0") & inet_addr("192.168.0.1") );
struct in_addr in2;
in2.s_addr = inet_addr("192.168.0.1");
char *dot_ip = inet_ntoa(in2);
printf("%s\n", dot_ip);
*/

View file

@ -1,6 +1,6 @@
/* /*
* json.c * json.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -9,29 +9,29 @@
#include "r3.h" #include "r3.h"
#include "r3_json.h" #include "r3_json.h"
json_object * r3_route_to_json_object(const route * r) { json_object * r3_route_to_json_object(const R3Route * r) {
json_object *obj; json_object *obj;
obj = json_object_new_object(); obj = json_object_new_object();
json_object_object_add(obj, "path", json_object_new_string(r->path)); 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)); json_object_object_add(obj, "allowed_methods", json_object_new_int(r->request_method));
if (r->host) { if (r->host) {
json_object_object_add(obj, "host", json_object_new_string(r->host)); json_object_object_add(obj, "host", json_object_new_string(r->host.base));
} }
if (r->remote_addr_pattern) { if (r->remote_addr_pattern) {
json_object_object_add(obj, "remote_addr_pattern", json_object_new_string(r->remote_addr_pattern)); json_object_object_add(obj, "remote_addr_pattern", json_object_new_string(r->remote_addr_pattern.base));
} }
return obj; return obj;
} }
json_object * r3_edge_to_json_object(const edge * e) { json_object * r3_edge_to_json_object(const R3Edge * e) {
json_object *obj; json_object *obj;
obj = json_object_new_object(); obj = json_object_new_object();
json_object_object_add(obj, "pattern", json_object_new_string(e->pattern)); 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, "opcode", json_object_new_int(e->opcode));
json_object_object_add(obj, "slug", json_object_new_boolean(e->has_slug)); json_object_object_add(obj, "slug", json_object_new_boolean(e->has_slug));
@ -42,7 +42,7 @@ json_object * r3_edge_to_json_object(const edge * e) {
return obj; return obj;
} }
json_object * r3_node_to_json_object(const node * n) { json_object * r3_node_to_json_object(const R3Node * n) {
json_object *obj; json_object *obj;
obj = json_object_new_object(); obj = json_object_new_object();
@ -60,7 +60,7 @@ json_object * r3_node_to_json_object(const node * n) {
json_object *edges_array = json_object_new_array(); json_object *edges_array = json_object_new_array();
json_object_object_add(obj, "edges", edges_array); json_object_object_add(obj, "edges", edges_array);
for (i = 0 ; i < n->edge_len ; i++ ) { for (i = 0 ; i < n->edge_len ; i++ ) {
json_object *edge_json_obj = r3_edge_to_json_object(n->edges[i]); json_object *edge_json_obj = r3_edge_to_json_object(n->edges.entries + i);
json_object_array_add(edges_array, edge_json_obj); json_object_array_add(edges_array, edge_json_obj);
} }
} }
@ -69,7 +69,7 @@ json_object * r3_node_to_json_object(const node * n) {
json_object *routes_array = json_object_new_array(); json_object *routes_array = json_object_new_array();
json_object_object_add(obj, "routes", routes_array); json_object_object_add(obj, "routes", routes_array);
for (i = 0; i < n->route_len; i++ ) { for (i = 0; i < n->route_len; i++ ) {
json_object *route_json_obj = r3_route_to_json_object(n->routes[i]); json_object *route_json_obj = r3_route_to_json_object(n->routes.entries + i);
json_object_array_add(routes_array, route_json_obj); json_object_array_add(routes_array, route_json_obj);
} }
} }
@ -79,17 +79,17 @@ json_object * r3_node_to_json_object(const node * n) {
} }
const char * r3_node_to_json_string_ext(const node * n, int options) { const char * r3_node_to_json_string_ext(const R3Node * n, int options) {
json_object *obj = r3_node_to_json_object(n); json_object *obj = r3_node_to_json_object(n);
return json_object_to_json_string_ext(obj, options); return json_object_to_json_string_ext(obj, options);
} }
const char * r3_node_to_json_pretty_string(const node * n) { const char * r3_node_to_json_pretty_string(const R3Node * n) {
json_object *obj = r3_node_to_json_object(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); 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 node * n) { const char * r3_node_to_json_string(const R3Node * n) {
json_object *obj = r3_node_to_json_object(n); json_object *obj = r3_node_to_json_object(n);
return json_object_to_json_string(obj); return json_object_to_json_string(obj);
} }

View file

@ -1,18 +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. * Distributed under terms of the MIT license.
*/ */
#include <stdlib.h> #include <stdlib.h>
#include "r3_list.h" #include "r3_list.h"
#include "zmalloc.h"
/* Naive linked list implementation */ /* Naive linked list implementation */
list * list *
list_create() list_create()
{ {
list *l = (list *) zmalloc(sizeof(list)); list *l = (list *) malloc(sizeof(list));
l->count = 0; l->count = 0;
l->head = NULL; l->head = NULL;
l->tail = NULL; l->tail = NULL;
@ -38,7 +37,7 @@ list_free(l)
} }
pthread_mutex_unlock(&(l->mutex)); pthread_mutex_unlock(&(l->mutex));
pthread_mutex_destroy(&(l->mutex)); pthread_mutex_destroy(&(l->mutex));
zfree(l); free(l);
} }
} }
@ -48,7 +47,7 @@ list_item * list_add_element(list * l, void * ptr)
pthread_mutex_lock(&(l->mutex)); pthread_mutex_lock(&(l->mutex));
li = (list_item *) zmalloc(sizeof(list_item)); li = (list_item *) malloc(sizeof(list_item));
li->value = ptr; li->value = ptr;
li->next = NULL; li->next = NULL;
li->prev = l->tail; li->prev = l->tail;
@ -89,7 +88,7 @@ list_remove_element(l, ptr)
li->next->prev = li->prev; li->next->prev = li->prev;
} }
l->count--; l->count--;
zfree(li); free(li);
result = 1; result = 1;
break; break;
} }

View file

@ -1,34 +1,28 @@
/* /*
* match_entry.c * match_entry.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
#include "config.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <pcre.h>
#include <assert.h> #include <assert.h>
#include <stdbool.h>
#include "r3.h" #include "r3.h"
#include "zmalloc.h"
match_entry * match_entry_createl(const char * path, int path_len) { match_entry * match_entry_createl(const char * path, int path_len) {
match_entry * entry = zmalloc(sizeof(match_entry)); match_entry * entry = r3_mem_alloc( sizeof(match_entry) );
if(!entry) memset(entry, 0, sizeof(*entry));
return NULL; r3_vector_reserve(&entry->vars.tokens, 3);
entry->vars = str_array_create(3); entry->path.base = path;
entry->path = path; entry->path.len = path_len;
entry->path_len = path_len;
entry->data = NULL;
return entry; return entry;
} }
void match_entry_free(match_entry * entry) { void match_entry_free(match_entry * entry) {
assert(entry); assert(entry);
if (entry->vars) { free(entry->vars.tokens.entries);
str_array_free(entry->vars); free(entry);
}
zfree(entry);
} }

45
src/memory.c Normal file
View 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;
}

File diff suppressed because it is too large Load diff

19
src/r3_debug.h Normal file
View 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 */

View file

@ -1,6 +1,6 @@
/* /*
* slug.c * slug.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -9,14 +9,14 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "r3.h" #include "r3.h"
#include "r3_str.h" #include "r3_slug.h"
#include "slug.h" #include "slug.h"
#include "zmalloc.h" #include "r3_debug.h"
r3_slug_t * r3_slug_new(const char * path, int path_len) { r3_slug_t * r3_slug_new(const char * path, int path_len) {
r3_slug_t * s = zmalloc(sizeof(r3_slug_t)); r3_slug_t * s = malloc(sizeof(r3_slug_t));
if (!s) if (!s)
return NULL; return NULL;
s->path = (char*) path; s->path = (char*) path;
@ -32,7 +32,7 @@ r3_slug_t * r3_slug_new(const char * path, int path_len) {
} }
void r3_slug_free(r3_slug_t * s) { void r3_slug_free(r3_slug_t * s) {
zfree(s); free(s);
} }
@ -60,7 +60,8 @@ int r3_slug_check(r3_slug_t *s) {
char * r3_slug_to_str(const r3_slug_t *s) { char * r3_slug_to_str(const r3_slug_t *s) {
char *str = NULL; char *str = NULL;
asprintf(&str, "slug: '%.*s', pattern: '%.*s', path: '%.*s'", s->len, s->begin, s->pattern_len, s->pattern, s->path_len, s->path); 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; return str;
} }
@ -85,7 +86,7 @@ int r3_slug_parse(r3_slug_t *s, const char *needle, int needle_len, const char *
} }
// there is no slug // there is no slug
if (!r3_path_contains_slug_char(offset)) { if (!r3_path_contains_slug_char(offset, needle_len - (offset-needle))) {
return 0; return 0;
} }
@ -139,7 +140,8 @@ int r3_slug_parse(r3_slug_t *s, const char *needle, int needle_len, const char *
if (state != 0) { if (state != 0) {
if (errstr) { if (errstr) {
char *err = NULL; char *err = NULL;
asprintf(&err, "Incomplete slug pattern. PATH (%d): '%s', OFFSET: %ld, STATE: %d", needle_len, needle, p - needle, state); int r = asprintf(&err, "Incomplete slug pattern. PATH (%d): '%s', OFFSET: %ld, STATE: %d", needle_len, needle, p - needle, state);
if (r) {};
*errstr = err; *errstr = err;
} }
return -1; return -1;
@ -152,7 +154,7 @@ int r3_slug_parse(r3_slug_t *s, const char *needle, int needle_len, const char *
/** /**
* provide a quick way to count slugs, simply search for '{' * provide a quick way to count slugs, simply search for '{'
*/ */
int slug_count(const char * needle, int len, char **errstr) { int r3_slug_count(const char * needle, int len, char **errstr) {
int cnt = 0; int cnt = 0;
int state = 0; int state = 0;
char * p = (char*) needle; char * p = (char*) needle;
@ -177,7 +179,8 @@ int slug_count(const char * needle, int len, char **errstr) {
if (state != 0) { if (state != 0) {
if (errstr) { if (errstr) {
char *err = NULL; char *err = NULL;
asprintf(&err, "Incomplete slug pattern. PATTERN (%d): '%s', OFFSET: %ld, STATE: %d", len, needle, p - needle, state); int r = asprintf(&err, "Incomplete slug pattern. PATTERN (%d): '%s', OFFSET: %ld, STATE: %d", len, needle, p - needle, state);
if (r) {};
*errstr = err; *errstr = err;
} }
return -1; return -1;

View file

@ -1,11 +1,11 @@
/* /*
* slug.h * slug.h
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
#ifndef R3_SLUG_H #ifndef SLUG_H
#define R3_SLUG_H #define SLUG_H
typedef struct { typedef struct {
/** /**
@ -49,10 +49,11 @@ char * r3_slug_to_str(const r3_slug_t *s);
void r3_slug_free(r3_slug_t * s); void r3_slug_free(r3_slug_t * s);
int slug_count(const char * needle, int len, char **errstr); static inline int r3_path_contains_slug_char(const char *str, unsigned int len) {
for (unsigned int i = 0; i < len; i++) {
static inline int r3_path_contains_slug_char(const char * str) { if (str[i] == '{') return 1;
return strchr(str, '{') != NULL ? 1 : 0; }
return 0;
} }
#endif /* !SLUG_H */ #endif /* !SLUG_H */

113
src/str.c
View file

@ -1,6 +1,6 @@
/* /*
* str.c * str.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -10,11 +10,18 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "r3.h" #include "r3.h"
#include "r3_str.h" #include "r3_slug.h"
#include "str.h"
#include "slug.h" #include "slug.h"
#include "zmalloc.h"
int r3_pattern_to_opcode(const 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 ) { if ( strncmp(pattern, "\\w+",len) == 0 ) {
return OP_EXPECT_MORE_WORDS; return OP_EXPECT_MORE_WORDS;
} }
@ -36,6 +43,9 @@ int r3_pattern_to_opcode(const char * pattern, int len) {
if ( strncmp(pattern, "[^-]+", len) == 0 ) { if ( strncmp(pattern, "[^-]+", len) == 0 ) {
return OP_EXPECT_NODASH; return OP_EXPECT_NODASH;
} }
if ( strncmp(pattern, ".*", len) == 0 ) {
return OP_GREEDY_ANY;
}
return 0; return 0;
} }
@ -71,21 +81,23 @@ char * r3_inside_slug(const char * needle, int needle_len, char *offset, char **
if (found_s1 || found_s2) { if (found_s1 || found_s2) {
// wrong slug pattern // wrong slug pattern
if(errstr) { if(errstr) {
asprintf(errstr, "Incomplete slug pattern"); int r = asprintf(errstr, "Incomplete slug pattern");
if (r) {};
} }
return NULL; return NULL;
} }
return NULL; return NULL;
} }
char * r3_slug_find_placeholder(const char *s1, int *len) { const char * r3_slug_find_placeholder(const char *s1, unsigned int str_len, unsigned int *len) {
char *c; const char *c;
char *s2; const char *s2;
int cnt = 0; int cnt = 0;
if ( NULL != (c = strchr(s1, '{')) ) { if ((c = strnchr(s1, str_len, '{'))) {
// find closing '}' // find closing '}'
s2 = c; s2 = c;
while(*s2) { unsigned int j = str_len - (c - s1);
for (unsigned int i = 0; i < j; i++) {
if (*s2 == '{' ) if (*s2 == '{' )
cnt++; cnt++;
else if (*s2 == '}' ) else if (*s2 == '}' )
@ -110,15 +122,16 @@ char * r3_slug_find_placeholder(const char *s1, int *len) {
/** /**
* given a slug string, duplicate the pattern string of the slug * given a slug string, duplicate the pattern string of the slug
*/ */
char * r3_slug_find_pattern(const char *s1, int *len) { const char * r3_slug_find_pattern(const char *s1, unsigned int str_len, unsigned int *len) {
char *c; const char *c;
char *s2; const char *s2;
int cnt = 1; unsigned int cnt = 1;
if ( NULL != (c = strchr(s1, ':')) ) { if ( (c = strnchr(s1, str_len, ':')) ) {
c++; c++;
// find closing '}' // find closing '}'
s2 = c; s2 = c;
while(s2) { unsigned int j = str_len - (c - s1);
for (unsigned int i = 0; i < j; i++) {
if (*s2 == '{' ) if (*s2 == '{' )
cnt++; cnt++;
else if (*s2 == '}' ) else if (*s2 == '}' )
@ -131,31 +144,57 @@ char * r3_slug_find_pattern(const char *s1, int *len) {
} else { } else {
return NULL; return NULL;
} }
if (cnt!=0) {
return NULL;
}
*len = s2 - c; *len = s2 - c;
return 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 * @param char * sep separator
*/ */
char * r3_slug_compile(const char * str, int len) char * r3_slug_compile(const char * str, unsigned int len)
{ {
char *s1 = NULL, *o = NULL; const char *s1 = NULL;
char *pat = NULL; char *o = NULL;
const char *pat = NULL;
char sep = '/'; char sep = '/';
// append prefix // append prefix
int s1_len; unsigned int s1_len;
s1 = r3_slug_find_placeholder(str, &s1_len); s1 = r3_slug_find_placeholder(str, len, &s1_len);
if ( s1 == NULL ) { if ( !s1 ) {
return zstrdup(str); return strndup(str,len);
} }
char * out = NULL; char * out = NULL;
if ((out = zcalloc(sizeof(char) * 200)) == NULL) { if (!(out = calloc(1, sizeof(char) * 200))) {
return (NULL); return (NULL);
} }
@ -167,8 +206,8 @@ char * r3_slug_compile(const char * str, int len)
o += (s1 - str); o += (s1 - str);
int pat_len; unsigned int pat_len;
pat = r3_slug_find_pattern(s1, &pat_len); pat = r3_slug_find_pattern(s1, s1_len, &pat_len);
if (pat) { if (pat) {
*o = '('; *o = '(';
@ -182,7 +221,7 @@ char * r3_slug_compile(const char * str, int len)
o+= strlen("([^*]+)"); o+= strlen("([^*]+)");
} }
s1 += s1_len; s1 += s1_len;
strncat(o, s1, strlen(s1)); strncat(o, s1, len - (s1 - str)); // string after slug
return out; return out;
} }
@ -191,13 +230,7 @@ char * ltrim_slash(char* str)
{ {
char * p = str; char * p = str;
while (*p == '/') p++; while (*p == '/') p++;
return zstrdup(p); return strdup(p);
}
void str_repeat(char *s, const char *c, int len) {
while(len--) {
s[len - 1] = *c;
}
} }
void print_indent(int level) { void print_indent(int level) {
@ -207,14 +240,16 @@ void print_indent(int level) {
} }
} }
#ifndef HAVE_STRDUP #ifndef HAVE_STRDUP
char *zstrdup(const char *s) { char *strdup(const char *s) {
char *out; char *out;
int count = 0; int count = 0;
while( s[count] ) while( s[count] )
++count; ++count;
++count; ++count;
out = zmalloc(sizeof(char) * count); out = malloc(sizeof(char) * count);
out[--count] = 0; out[--count] = 0;
while( --count >= 0 ) while( --count >= 0 )
out[count] = s[count]; out[count] = s[count];
@ -222,14 +257,16 @@ char *zstrdup(const char *s) {
} }
#endif #endif
#ifndef HAVE_STRNDUP #ifndef HAVE_STRNDUP
char *zstrndup(const char *s, int n) { char *strndup(const char *s, int n) {
char *out; char *out;
int count = 0; int count = 0;
while( count < n && s[count] ) while( count < n && s[count] )
++count; ++count;
++count; ++count;
out = zmalloc(sizeof(char) * count); out = malloc(sizeof(char) * count);
out[--count] = 0; out[--count] = 0;
while( --count >= 0 ) while( --count >= 0 )
out[count] = s[count]; out[count] = s[count];

14
src/str.h Normal file
View 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

View file

@ -1,64 +1,54 @@
/* /*
* token.c * token.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
#include "config.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "r3.h" #include "r3.h"
#include "r3_str.h" #include "r3_slug.h"
#include "str_array.h" #include "str_array.h"
#include "zmalloc.h" #include "memory.h"
str_array * str_array_create(int cap) {
str_array * list = (str_array*) zmalloc( sizeof(str_array) );
if (!list)
return NULL;
list->len = 0;
list->cap = cap;
list->tokens = (char**) zmalloc( sizeof(char*) * cap);
return list;
}
void str_array_free(str_array *l) { void str_array_free(str_array *l) {
assert(l); assert(l);
for ( int i = 0; i < l->len ; i++ ) { free(l->tokens.entries);
if (l->tokens[ i ]) { }
zfree(l->tokens[i]);
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;
}
void str_array_dump_slugs(const str_array *l) {
if (l->tokens.size) {
printf("[");
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");
} }
zfree(l);
}
bool str_array_is_full(const str_array * l) {
return l->len >= l->cap;
}
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(const str_array *l) { void str_array_dump(const str_array *l) {
printf("["); printf("[");
for ( int i = 0; i < l->len ; i++ ) { for ( int i = 0; i < l->tokens.size ; i++ ) {
printf("\"%s\"", l->tokens[i] ); printf("\"%*.*s\"", l->tokens.entries[i].len,l->tokens.entries[i].len,l->tokens.entries[i].base );
if ( i + 1 != l->len ) { // printf("\"%s\"", l->tokens.entries[i] );
if ( i + 1 != l->tokens.size ) {
printf(", "); printf(", ");
} }
} }

27
tests/CMakeLists.txt Normal file
View 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)

View file

@ -1,6 +1,6 @@
TESTS = TESTS =
AM_CFLAGS=$(DEPS_CFLAGS) $(GVC_DEPS_CFLAGS) $(JSONC_CFLAGS) @CHECK_CFLAGS@ -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/src -I$(top_builddir)/3rdparty -Wall -std=c99 -ggdb -Wall 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 AM_LDFLAGS=$(DEPS_LIBS) $(GVC_DEPS_LIBS) $(JSONC_LIBS) @CHECK_LIBS@ $(top_builddir)/libr3.la
if USE_JEMALLOC if USE_JEMALLOC
@ -27,6 +27,18 @@ check_routes_SOURCES = check_routes.c
TESTS += check_str_array TESTS += check_str_array
check_str_array_SOURCES = check_str_array.c 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 if ENABLE_JSON
TESTS += check_json TESTS += check_json
@ -49,4 +61,3 @@ bench_SOURCES = bench.c
# AM_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir)/include # AM_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir)/include
# AM_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include # AM_CFLAGS=$(DEPS_CFLAGS) -I$(top_builddir) -I$(top_builddir)/include
CLEANFILES = check_tree.log CLEANFILES = check_tree.log

View file

@ -1,6 +1,6 @@
/* /*
* bench.c * bench.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -12,15 +12,14 @@
#include <stdarg.h> /* va_list, va_start, va_arg, va_end */ #include <stdarg.h> /* va_list, va_start, va_arg, va_end */
#include "r3.h" #include "r3.h"
#include "r3_str.h" #include "r3_slug.h"
#include "zmalloc.h"
#include "bench.h" #include "bench.h"
unsigned long unixtime() { unsigned long unixtime() {
struct timeval tp; struct timeval tp;
if (gettimeofday((struct timeval *) &tp, (NUL)) == 0) { if (gettimeofday((struct timeval *) &tp, (NULL)) == 0) {
return tp.tv_sec; return tp.tv_sec;
} }
return 0; return 0;
@ -30,7 +29,7 @@ double microtime() {
struct timeval tp; struct timeval tp;
long sec = 0L; long sec = 0L;
double msec = 0.0; double msec = 0.0;
if (gettimeofday((struct timeval *) &tp, (NUL)) == 0) { if (gettimeofday((struct timeval *) &tp, (NULL)) == 0) {
msec = (double) (tp.tv_usec / MICRO_IN_SEC); msec = (double) (tp.tv_usec / MICRO_IN_SEC);
sec = tp.tv_sec; sec = tp.tv_sec;
if (msec >= 1.0) if (msec >= 1.0)
@ -96,7 +95,7 @@ void bench_append_csv(char *filename, int countOfB, ...) {
int main() int main()
{ {
node * n = r3_tree_create(1); R3Node * n = r3_tree_create(1);
int route_data = 999; int route_data = 999;
@ -441,7 +440,7 @@ r3_tree_insert_path(n, "/garply/grault/corge", NULL);
r3_tree_compile(n, NULL); r3_tree_compile(n, NULL);
END_MEASURE(tree_compile) END_MEASURE(tree_compile)
node *m; R3Node * m;
m = r3_tree_match(n , "/qux/bar/corge", NULL); m = r3_tree_match(n , "/qux/bar/corge", NULL);
assert(m != NULL); assert(m != NULL);
assert( *((int*) m->data) == 999 ); assert( *((int*) m->data) == 999 );
@ -461,10 +460,11 @@ r3_tree_insert_path(n, "/garply/grault/corge", NULL);
BENCHMARK_SUMMARY(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_insert_path(tree2, "/post/{year}/{month}", NULL);
r3_tree_compile(tree2, NULL); r3_tree_compile(tree2, NULL);
// r3_tree_dump(tree2,0);
BENCHMARK(pcre_dispatch) BENCHMARK(pcre_dispatch)
r3_tree_matchl(tree2, "/post/2014/12", strlen("/post/2014/12"), NULL); r3_tree_matchl(tree2, "/post/2014/12", strlen("/post/2014/12"), NULL);
END_BENCHMARK(pcre_dispatch) END_BENCHMARK(pcre_dispatch)

View file

@ -1,6 +1,6 @@
/* /*
* bench.h * bench.h
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* check_gvc.c * check_gvc.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -10,7 +10,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "r3.h" #include "r3.h"
#include "r3_gvc.h" #include "r3_gvc.h"
#include "r3_str.h" #include "r3_slug.h"
#include "bench.h" #include "bench.h"
START_TEST (test_gvc_render_dot) START_TEST (test_gvc_render_dot)

92
tests/check_host.c Normal file
View 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
View 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;
}

View file

@ -1,6 +1,6 @@
/* /*
* check_json.c * check_json.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -10,13 +10,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <json-c/json.h> #include <json-c/json.h>
#include "r3.h" #include "r3.h"
#include "r3_str.h" #include "r3_slug.h"
#include "r3_json.h" #include "r3_json.h"
#include "zmalloc.h" #include "zmalloc.h"
START_TEST (test_json_encode) START_TEST (test_json_encode)
{ {
node * n; R3Node * n;
n = r3_tree_create(10); n = r3_tree_create(10);
ck_assert(n); ck_assert(n);

216
tests/check_remote_addr.c Normal file
View 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;
}

View file

@ -7,13 +7,12 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include "r3.h" #include "r3.h"
#include "r3_str.h" #include "r3_slug.h"
#include "zmalloc.h"
START_TEST (test_routes) START_TEST (test_routes)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
node * m = NULL; R3Node * m = NULL;
char *data0 = "/foo/bar/baz"; char *data0 = "/foo/bar/baz";
r3_tree_insert_path(n, "/foo/bar/baz", (void*) data0); r3_tree_insert_path(n, "/foo/bar/baz", (void*) data0);

117
tests/check_routes2.c Normal file
View 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;
}

View file

@ -1,6 +1,6 @@
/* /*
* check_slug.c * check_slug.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -9,9 +9,9 @@
#include <check.h> #include <check.h>
#include <stdlib.h> #include <stdlib.h>
#include "r3.h" #include "r3.h"
#include "r3_str.h" #include "r3_slug.h"
#include "zmalloc.h"
#include "slug.h" #include "slug.h"
#include "r3_debug.h"
START_TEST (test_pattern_to_opcode) START_TEST (test_pattern_to_opcode)
{ {
@ -27,46 +27,75 @@ START_TEST (test_r3_slug_compile)
char * path = "/user/{id}"; char * path = "/user/{id}";
char * c = NULL; char * c = NULL;
ck_assert_str_eq( c = r3_slug_compile(path, strlen(path) ) , "^/user/([^/]+)" ); ck_assert_str_eq( c = r3_slug_compile(path, strlen(path) ) , "^/user/([^/]+)" );
zfree(c); free(c);
char * path2 = "/what/{id}-foo"; char * path2 = "/what/{id}-foo";
ck_assert_str_eq( c = r3_slug_compile(path2, strlen(path2) ) , "^/what/([^/]+)-foo" ); ck_assert_str_eq( c = r3_slug_compile(path2, strlen(path2) ) , "^/what/([^/]+)-foo" );
zfree(c); free(c);
char * path3 = "-{id}"; char * path3 = "-{id}";
ck_assert_str_eq( c = r3_slug_compile(path3, strlen(path3)), "^-([^/]+)" ); ck_assert_str_eq( c = r3_slug_compile(path3, strlen(path3)), "^-([^/]+)" );
zfree(c); free(c);
char * path4 = "-{idx:\\d{3}}"; char * path4 = "-{idx:\\d{3}}";
ck_assert_str_eq( c = r3_slug_compile(path4, strlen(path4)), "^-(\\d{3})" ); ck_assert_str_eq( c = r3_slug_compile(path4, strlen(path4)), "^-(\\d{3})" );
zfree(c); free(c);
} }
END_TEST END_TEST
START_TEST (test_contains_slug) START_TEST (test_contains_slug)
{ {
ck_assert( r3_path_contains_slug_char("/user/{id}/{name}") ); char *test_str = "/user/{id}/{name}";
ck_assert( r3_path_contains_slug_char(test_str, strlen(test_str)) );
} }
END_TEST END_TEST
START_TEST (test_r3_slug_find_pattern) START_TEST (test_r3_slug_find_pattern)
{ {
int len; unsigned int len;
char * namerex = r3_slug_find_pattern("{name:\\s+}", &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 ); ck_assert( strncmp(namerex, "\\s+", len) == 0 );
} }
END_TEST END_TEST
START_TEST (test_r3_slug_find_name)
{
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) START_TEST (test_r3_slug_find_placeholder)
{ {
int slug_len = 0; unsigned int slug_len = 0;
char * slug; const char * slug;
slug = r3_slug_find_placeholder("/user/{name:\\s+}/to/{id}", &slug_len); 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 ); ck_assert( strncmp(slug, "{name:\\s+}", slug_len) == 0 );
test_str = "/user/{idx:\\d{3}}/to/{idy:\\d{3}}";
slug = r3_slug_find_placeholder("/user/{idx:\\d{3}}/to/{idy:\\d{3}}", &slug_len); slug = r3_slug_find_placeholder(test_str, strlen(test_str), &slug_len);
ck_assert( slug_len == strlen("{idx:\\d{3}}") ); ck_assert( slug_len == strlen("{idx:\\d{3}}") );
ck_assert( strncmp(slug, "{idx:\\d{3}}", slug_len) == 0 ); ck_assert( strncmp(slug, "{idx:\\d{3}}", slug_len) == 0 );
} }
@ -76,7 +105,7 @@ START_TEST (test_r3_inside_slug)
{ {
char * pattern = "/user/{name:\\s+}/to/{id}"; char * pattern = "/user/{name:\\s+}/to/{id}";
char * offset = strchr(pattern, '{') + 2; char * offset = strchr(pattern, '{') + 2;
ck_assert( (int)r3_inside_slug(pattern, strlen(pattern), offset, NULL) ); 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), offset, NULL)) == '{' );
ck_assert( ! r3_inside_slug(pattern, strlen(pattern), pattern, NULL) ); ck_assert( ! r3_inside_slug(pattern, strlen(pattern), pattern, NULL) );
} }
@ -87,9 +116,9 @@ START_TEST (test_incomplete_slug)
int cnt = 0; int cnt = 0;
char * errstr = NULL; char * errstr = NULL;
char * pattern = "/user/{name:\\d{3}}/to/{id"; char * pattern = "/user/{name:\\d{3}}/to/{id";
cnt = slug_count(pattern, strlen(pattern), &errstr); cnt = r3_slug_count(pattern, strlen(pattern), &errstr);
ck_assert_int_eq(cnt, -1); ck_assert_int_eq(cnt, -1);
ck_assert(errstr); ck_assert(errstr != NULL);
printf("%s\n",errstr); printf("%s\n",errstr);
free(errstr); free(errstr);
} }
@ -138,22 +167,22 @@ END_TEST
START_TEST (test_slug_count) START_TEST (test_r3_slug_count)
{ {
int cnt = 0; int cnt = 0;
char * pattern = "/user/{name:\\s+}/to/{id}"; char * pattern = "/user/{name:\\s+}/to/{id}";
char * errstr = NULL; char * errstr = NULL;
cnt = slug_count(pattern, strlen(pattern), &errstr); cnt = r3_slug_count(pattern, strlen(pattern), &errstr);
ck_assert_int_eq(cnt, 2); ck_assert_int_eq(cnt, 2);
if(errstr) free(errstr); if(errstr) free(errstr);
char * pattern2 = "/user/{name:\\d{3}}/to/{id}"; char * pattern2 = "/user/{name:\\d{3}}/to/{id}";
cnt = slug_count(pattern2, strlen(pattern2), &errstr); cnt = r3_slug_count(pattern2, strlen(pattern2), &errstr);
ck_assert_int_eq(cnt, 2); ck_assert_int_eq(cnt, 2);
if(errstr) free(errstr); if(errstr) free(errstr);
char * pattern3 = "/user/{name:\\d{3}}/to/{id}"; char * pattern3 = "/user/{name:\\d{3}}/to/{id}";
cnt = slug_count(pattern3, strlen(pattern3), &errstr); cnt = r3_slug_count(pattern3, strlen(pattern3), &errstr);
ck_assert_int_eq(cnt, 2); ck_assert_int_eq(cnt, 2);
if(errstr) free(errstr); if(errstr) free(errstr);
} }
@ -161,8 +190,9 @@ END_TEST
START_TEST (test_r3_slug_find_placeholder_with_broken_slug) START_TEST (test_r3_slug_find_placeholder_with_broken_slug)
{ {
int slug_len = 0; unsigned int slug_len = 0;
char * slug = r3_slug_find_placeholder("/user/{name:\\s+/to/{id", &slug_len); 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); ck_assert(slug == 0);
} }
END_TEST END_TEST
@ -177,10 +207,14 @@ Suite* r3_suite (void) {
tcase_add_test(tcase, test_r3_slug_find_pattern); 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);
tcase_add_test(tcase, test_r3_slug_find_placeholder_with_broken_slug); tcase_add_test(tcase, test_r3_slug_find_placeholder_with_broken_slug);
tcase_add_test(tcase, test_slug_count); tcase_add_test(tcase, test_r3_slug_count);
tcase_add_test(tcase, test_r3_slug_compile); tcase_add_test(tcase, test_r3_slug_compile);
tcase_add_test(tcase, test_pattern_to_opcode); tcase_add_test(tcase, test_pattern_to_opcode);
tcase_add_test(tcase, test_incomplete_slug); 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_with_pattern);
// tcase_add_test(tcase, test_slug_parse_without_pattern); // tcase_add_test(tcase, test_slug_parse_without_pattern);

View file

@ -1,6 +1,6 @@
/* /*
* check_str_array.c * check_str_array.c
* Copyright (C) 2014 c9s <c9s@c9smba.local> * Copyright (C) 2014 c9s <yoanlin93@gmail.com>
* *
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
@ -8,29 +8,59 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <check.h> #include <check.h>
#include "r3.h" #include "str_array.h"
#include "r3_str.h"
#include "zmalloc.h"
START_TEST (test_str_array) START_TEST (test_str_array)
{ {
str_array * l = str_array_create(3); str_array *vars = r3_mem_alloc(sizeof(str_array));
ck_assert(l); memset(vars, 0, sizeof(*vars));
ck_assert(str_array_append(l, zstrdup("abc"))); char *test_str = "abc";
ck_assert( l->len == 1 ); ck_assert( str_array_append(vars, test_str, strlen(test_str)));
ck_assert( vars->tokens.size == 1 );
ck_assert(str_array_append(l, zstrdup("foo") )); char *test_str1 = "foo";
ck_assert( l->len == 2 ); ck_assert( str_array_append(vars, test_str1, strlen(test_str1)));
ck_assert( vars->tokens.size == 2 );
ck_assert( str_array_append(l, zstrdup("bar") ) ); char *test_str2 = "bar";
ck_assert( l->len == 3 ); ck_assert( str_array_append(vars, test_str2, strlen(test_str2)));
ck_assert( vars->tokens.size == 3 );
ck_assert( str_array_append(l, zstrdup("zoo") ) ); char *test_str3 = "zoo";
ck_assert( l->len == 4 ); ck_assert( str_array_append(vars, test_str3, strlen(test_str3)));
ck_assert( vars->tokens.size == 4 );
ck_assert( str_array_resize(l, l->cap * 2) ); str_array_free(vars);
str_array_free(l); 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 END_TEST
@ -38,6 +68,7 @@ Suite* r3_suite (void) {
Suite *suite = suite_create("str_array test"); Suite *suite = suite_create("str_array test");
TCase *tcase = tcase_create("testcase"); TCase *tcase = tcase_create("testcase");
tcase_add_test(tcase, test_str_array); tcase_add_test(tcase, test_str_array);
tcase_add_test(tcase, test_access_macros);
suite_add_tcase(suite, tcase); suite_add_tcase(suite, tcase);
return suite; return suite;
} }

View file

@ -4,32 +4,37 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include "r3.h" #include "r3.h"
#include "r3_str.h" #include "r3_slug.h"
#include "zmalloc.h"
#include "bench.h" #include "bench.h"
#include "../src/r3_debug.h"
#define SAFE_FREE(ptr) if(ptr) free(ptr); #define SAFE_FREE(ptr) if(ptr) free(ptr);
#define STR(str) str, sizeof(str)-1
START_TEST (test_find_common_prefix) START_TEST (test_find_common_prefix)
{ {
node * n = r3_tree_create(10); char *test_str = "/foo/{slug}";
edge * e = r3_edge_createl(zstrdup("/foo/{slug}"), sizeof("/foo/{slug}")-1, NULL); R3Node * n = r3_tree_create(10);
r3_node_append_edge(n,e); R3Edge * e = r3_node_append_edge(n);
r3_edge_initl(e, test_str, strlen(test_str), NULL);
char *errstr = NULL; char *errstr = NULL;
int prefix_len = 0; int prefix_len = 0;
edge *ret_edge = NULL; R3Edge *ret_edge = NULL;
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "/foo", sizeof("/foo")-1, &prefix_len, &errstr); char *test_pref1 = "/foo";
ret_edge = r3_node_find_common_prefix(n, test_pref1, strlen(test_pref1), &prefix_len, &errstr);
ck_assert(ret_edge != NULL); ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 4); ck_assert_int_eq(prefix_len, 4);
SAFE_FREE(errstr); SAFE_FREE(errstr);
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "/foo/", sizeof("/foo/")-1, &prefix_len, &errstr); char *test_pref2 = "/foo/";
ret_edge = r3_node_find_common_prefix(n, test_pref2, strlen(test_pref2), &prefix_len, &errstr);
ck_assert(ret_edge != NULL); ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 5); ck_assert_int_eq(prefix_len, 5);
SAFE_FREE(errstr); SAFE_FREE(errstr);
@ -37,35 +42,40 @@ START_TEST (test_find_common_prefix)
errstr = NULL; errstr = NULL;
prefix_len = 0; prefix_len = 0;
ret_edge = r3_node_find_common_prefix(n, "/foo/{slog}", sizeof("/foo/{slog}")-1, &prefix_len, &errstr); char *test_pref3 = "/foo/{slog}";
ret_edge = r3_node_find_common_prefix(n, test_pref3, strlen(test_pref3), &prefix_len, &errstr);
ck_assert(ret_edge != NULL); ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 5); ck_assert_int_eq(prefix_len, 5);
SAFE_FREE(errstr); SAFE_FREE(errstr);
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "/foo/{bar}", sizeof("/foo/{bar}")-1, &prefix_len, &errstr); char *test_pref4 = "/foo/{bar}";
ret_edge = r3_node_find_common_prefix(n, test_pref4, strlen(test_pref4), &prefix_len, &errstr);
ck_assert(ret_edge != NULL); ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 5); ck_assert_int_eq(prefix_len, 5);
SAFE_FREE(errstr); SAFE_FREE(errstr);
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "/foo/bar", sizeof("/foo/bar")-1, &prefix_len, &errstr); char *test_pref5 = "/foo/bar";
ret_edge = r3_node_find_common_prefix(n, test_pref5, strlen(test_pref5), &prefix_len, &errstr);
ck_assert(ret_edge != NULL); ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 5); ck_assert_int_eq(prefix_len, 5);
SAFE_FREE(errstr); SAFE_FREE(errstr);
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "/bar/", sizeof("/bar/")-1, &prefix_len, &errstr); char *test_pref6 = "/bar/";
ret_edge = r3_node_find_common_prefix(n, test_pref6, strlen(test_pref6), &prefix_len, &errstr);
ck_assert(ret_edge != NULL); ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 1); ck_assert_int_eq(prefix_len, 1);
SAFE_FREE(errstr); SAFE_FREE(errstr);
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "{bar}", sizeof("{bar}")-1, &prefix_len, &errstr); char *test_pref7 = "{bar}";
ret_edge = r3_node_find_common_prefix(n, test_pref7, strlen(test_pref7), &prefix_len, &errstr);
ck_assert(ret_edge == NULL); ck_assert(ret_edge == NULL);
ck_assert_int_eq(prefix_len, 0); ck_assert_int_eq(prefix_len, 0);
SAFE_FREE(errstr); SAFE_FREE(errstr);
@ -81,30 +91,34 @@ END_TEST
START_TEST (test_find_common_prefix_after) START_TEST (test_find_common_prefix_after)
{ {
node * n = r3_tree_create(10); char *test_str = "{slug}/foo";
edge * e = r3_edge_createl(zstrdup("{slug}/foo"), sizeof("{slug}/foo")-1, NULL); R3Node * n = r3_tree_create(10);
r3_node_append_edge(n,e); R3Edge * e = r3_node_append_edge(n);
r3_edge_initl(e, test_str, strlen(test_str), NULL);
int prefix_len = 0; int prefix_len = 0;
edge *ret_edge = NULL; R3Edge *ret_edge = NULL;
char *errstr = NULL; char *errstr = NULL;
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "/foo", sizeof("/foo")-1, &prefix_len, &errstr); char *test_pref1 = "/foo";
ret_edge = r3_node_find_common_prefix(n, test_pref1, strlen(test_pref1), &prefix_len, &errstr);
ck_assert(ret_edge == NULL); ck_assert(ret_edge == NULL);
ck_assert_int_eq(prefix_len, 0); ck_assert_int_eq(prefix_len, 0);
SAFE_FREE(errstr); SAFE_FREE(errstr);
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "{slug}/bar", sizeof("{slug}/bar")-1, &prefix_len, &errstr); char *test_pref2 = "{slug}/bar";
ret_edge = r3_node_find_common_prefix(n, test_pref2, strlen(test_pref2), &prefix_len, &errstr);
ck_assert(ret_edge != NULL); ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 7); ck_assert_int_eq(prefix_len, 7);
SAFE_FREE(errstr); SAFE_FREE(errstr);
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "{slug}/foo", sizeof("{slug}/foo")-1, &prefix_len, &errstr); char *test_pref3 = "{slug}/foo";
ret_edge = r3_node_find_common_prefix(n, test_pref3, strlen(test_pref3), &prefix_len, &errstr);
ck_assert(ret_edge != NULL); ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 10); ck_assert_int_eq(prefix_len, 10);
SAFE_FREE(errstr); SAFE_FREE(errstr);
@ -118,16 +132,18 @@ END_TEST
START_TEST (test_find_common_prefix_double_middle) START_TEST (test_find_common_prefix_double_middle)
{ {
node * n = r3_tree_create(10); char *test_str = "{slug}/foo/{name}";
edge * e = r3_edge_createl(zstrdup("{slug}/foo/{name}"), sizeof("{slug}/foo/{name}")-1, NULL); R3Node * n = r3_tree_create(10);
r3_node_append_edge(n,e); R3Edge * e = r3_node_append_edge(n);
r3_edge_initl(e, test_str, strlen(test_str), NULL);
int prefix_len; int prefix_len;
edge *ret_edge = NULL; R3Edge *ret_edge = NULL;
char *errstr; char *errstr;
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "{slug}/foo/{number}", sizeof("{slug}/foo/{number}")-1, &prefix_len, &errstr); char *test_pref1 = "{slug}/foo/{number}";
ret_edge = r3_node_find_common_prefix(n, test_pref1, strlen(test_pref1), &prefix_len, &errstr);
ck_assert(ret_edge); ck_assert(ret_edge);
ck_assert_int_eq(prefix_len, 11); ck_assert_int_eq(prefix_len, 11);
SAFE_FREE(errstr); SAFE_FREE(errstr);
@ -140,22 +156,25 @@ END_TEST
START_TEST (test_find_common_prefix_middle) START_TEST (test_find_common_prefix_middle)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
edge * e = r3_edge_createl(zstrdup("/foo/{slug}/hate"), sizeof("/foo/{slug}/hate")-1, NULL); char *test_str = "/foo/{slug}/hate";
r3_node_append_edge(n,e); R3Edge * e = r3_node_append_edge(n);
r3_edge_initl(e, test_str, strlen(test_str), NULL);
int prefix_len; int prefix_len;
edge *ret_edge = NULL; R3Edge *ret_edge = NULL;
char *errstr = NULL; char *errstr = NULL;
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "/foo/{slug}/bar", sizeof("/foo/{slug}/bar")-1, &prefix_len, &errstr); char *test_str1 = "/foo/{slug}/bar";
ret_edge = r3_node_find_common_prefix(n, test_str1, strlen(test_str1), &prefix_len, &errstr);
ck_assert(ret_edge); ck_assert(ret_edge);
ck_assert_int_eq(prefix_len, 12); ck_assert_int_eq(prefix_len, 12);
SAFE_FREE(errstr); SAFE_FREE(errstr);
errstr = NULL; errstr = NULL;
ret_edge = r3_node_find_common_prefix(n, "/fo/{slug}/bar", sizeof("/fo/{slug}/bar")-1, &prefix_len, &errstr); char *test_str2 = "/fo/{slug}/bar";
ret_edge = r3_node_find_common_prefix(n, test_str2, strlen(test_str2), &prefix_len, &errstr);
ck_assert(ret_edge); ck_assert(ret_edge);
ck_assert_int_eq(prefix_len, 3); ck_assert_int_eq(prefix_len, 3);
SAFE_FREE(errstr); SAFE_FREE(errstr);
@ -166,12 +185,13 @@ END_TEST
START_TEST (test_find_common_prefix_same_pattern) START_TEST (test_find_common_prefix_same_pattern)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
edge * e = r3_edge_createl(zstrdup("/foo/{slug:xxx}/hate"), sizeof("/foo/{slug:xxx}/hate")-1, NULL); char *test_str = "/foo/{slug:xxx}/hate";
r3_node_append_edge(n,e); R3Edge * e = r3_node_append_edge(n);
r3_edge_initl(e, test_str, strlen(test_str), NULL);
int prefix_len; int prefix_len;
edge *ret_edge = NULL; R3Edge *ret_edge = NULL;
prefix_len = 0; prefix_len = 0;
ret_edge = r3_node_find_common_prefix(n, "/foo/{slug:yyy}/hate", sizeof("/foo/{slug:yyy}/hate")-1, &prefix_len, NULL); ret_edge = r3_node_find_common_prefix(n, "/foo/{slug:yyy}/hate", sizeof("/foo/{slug:yyy}/hate")-1, &prefix_len, NULL);
@ -190,12 +210,13 @@ END_TEST
START_TEST (test_find_common_prefix_same_pattern2) START_TEST (test_find_common_prefix_same_pattern2)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
edge * e = r3_edge_createl(zstrdup("{slug:xxx}/hate"), sizeof("{slug:xxx}/hate")-1, NULL); char *test_str = "{slug:xxx}/hate";
r3_node_append_edge(n,e); R3Edge * e = r3_node_append_edge(n);
r3_edge_initl(e, test_str, strlen(test_str), NULL);
int prefix_len; int prefix_len;
edge *ret_edge = NULL; R3Edge *ret_edge = NULL;
prefix_len = 0; prefix_len = 0;
ret_edge = r3_node_find_common_prefix(n, "{slug:yyy}/hate", sizeof("{slug:yyy}/hate")-1, &prefix_len, NULL); ret_edge = r3_node_find_common_prefix(n, "{slug:yyy}/hate", sizeof("{slug:yyy}/hate")-1, &prefix_len, NULL);
@ -206,7 +227,59 @@ START_TEST (test_find_common_prefix_same_pattern2)
} }
END_TEST END_TEST
START_TEST (test_find_common_prefix_multi_edge)
{
R3Node * n = r3_tree_create(10);
char *test_str1 = "{id}/foo";
R3Edge * e1 = r3_node_append_edge(n);
r3_edge_initl(e1, test_str1, strlen(test_str1), NULL);
char *test_str2 = "{id2}/bar";
R3Edge * e2 = r3_node_append_edge(n);
r3_edge_initl(e2, test_str2, strlen(test_str2), NULL);
R3Edge *ret_edge = NULL;
int prefix_len = 0;
char *errstr = NULL;
char *test_pref1 = "{id}";
ret_edge = r3_node_find_common_prefix(n, test_pref1, strlen(test_pref1), &prefix_len, &errstr);
ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 4);
ck_assert(ret_edge == e1);
SAFE_FREE(errstr);
prefix_len = 0;
errstr = NULL;
char *test_pref2 = "{id}/foo";
ret_edge = r3_node_find_common_prefix(n, test_pref2, strlen(test_pref2), &prefix_len, &errstr);
ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 8);
ck_assert(ret_edge == e1);
SAFE_FREE(errstr);
prefix_len = 0;
errstr = NULL;
char *test_pref3 = "{id2}";
ret_edge = r3_node_find_common_prefix(n, test_pref3, strlen(test_pref3), &prefix_len, &errstr);
ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 5);
ck_assert(ret_edge == e2);
SAFE_FREE(errstr);
prefix_len = 0;
errstr = NULL;
char *test_pref4 = "{id2}/bar";
ret_edge = r3_node_find_common_prefix(n, test_pref4, strlen(test_pref4), &prefix_len, &errstr);
ck_assert(ret_edge != NULL);
ck_assert_int_eq(prefix_len, 9);
ck_assert(ret_edge == e2);
SAFE_FREE(errstr);
r3_tree_free(n);
}
END_TEST
@ -224,15 +297,15 @@ END_TEST
START_TEST (test_node_construct_and_free) START_TEST (test_node_construct_and_free)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
node * another_tree = r3_tree_create(3); R3Node * another_tree = r3_tree_create(3);
r3_tree_free(n); r3_tree_free(n);
r3_tree_free(another_tree); r3_tree_free(another_tree);
} }
END_TEST END_TEST
static node * create_simple_str_tree() { static R3Node * create_simple_str_tree() {
node * n; R3Node * n;
n = r3_tree_create(10); n = r3_tree_create(10);
r3_tree_insert_path(n, "/zoo", NULL); r3_tree_insert_path(n, "/zoo", NULL);
r3_tree_insert_path(n, "/foo", NULL); r3_tree_insert_path(n, "/foo", NULL);
@ -245,8 +318,8 @@ static node * create_simple_str_tree() {
START_TEST (test_compile) START_TEST (test_compile)
{ {
node *n; R3Node *n;
node *m; R3Node *m;
n = create_simple_str_tree(); n = create_simple_str_tree();
@ -258,41 +331,49 @@ START_TEST (test_compile)
r3_tree_insert_path(n, "/{id}", NULL); r3_tree_insert_path(n, "/{id}", NULL);
r3_tree_compile(n, NULL); r3_tree_compile(n, NULL);
r3_tree_compile(n, NULL); // test double compile r3_tree_compile(n, NULL); // test double compile
r3_tree_dump(n, 0); // r3_tree_dump(n, 0);
match_entry * entry; match_entry * entry;
entry = match_entry_createl( "foo" , strlen("/foo") ); entry = match_entry_createl( "foo" , strlen("/foo") );
m = r3_tree_matchl( n , "/foo", strlen("/foo"), entry); m = r3_tree_matchl( n , "/foo", strlen("/foo"), entry);
ck_assert( m ); ck_assert( m );
match_entry_free(entry);
entry = match_entry_createl( "/zoo" , strlen("/zoo") ); entry = match_entry_createl( "/zoo" , strlen("/zoo") );
m = r3_tree_matchl( n , "/zoo", strlen("/zoo"), entry); m = r3_tree_matchl( n , "/zoo", strlen("/zoo"), entry);
ck_assert( m ); ck_assert( m );
match_entry_free(entry);
entry = match_entry_createl( "/bar" , strlen("/bar") ); entry = match_entry_createl( "/bar" , strlen("/bar") );
m = r3_tree_matchl( n , "/bar", strlen("/bar"), entry); m = r3_tree_matchl( n , "/bar", strlen("/bar"), entry);
ck_assert( m ); ck_assert( m );
match_entry_free(entry);
entry = match_entry_createl( "/xxx" , strlen("/xxx") ); entry = match_entry_createl( "/xxx" , strlen("/xxx") );
m = r3_tree_matchl( n , "/xxx", strlen("/xxx"), entry); m = r3_tree_matchl( n , "/xxx", strlen("/xxx"), entry);
ck_assert( m ); ck_assert( m );
match_entry_free(entry);
entry = match_entry_createl( "/foo/xxx" , strlen("/foo/xxx") ); entry = match_entry_createl( "/foo/xxx" , strlen("/foo/xxx") );
m = r3_tree_matchl( n , "/foo/xxx", strlen("/foo/xxx"), entry); m = r3_tree_matchl( n , "/foo/xxx", strlen("/foo/xxx"), entry);
ck_assert( m ); ck_assert( m );
match_entry_free(entry);
entry = match_entry_createl( "/some_id" , strlen("/some_id") ); entry = match_entry_createl( "/some_id" , strlen("/some_id") );
m = r3_tree_matchl( n , "/some_id", strlen("/some_id"), entry); m = r3_tree_matchl( n , "/some_id", strlen("/some_id"), entry);
ck_assert( m ); ck_assert( m );
match_entry_free(entry);
r3_tree_free(n);
} }
END_TEST END_TEST
START_TEST (test_incomplete_slug_path) START_TEST (test_incomplete_slug_path)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
node * ret_node; R3Node * ret_node;
// r3_tree_insert_path(n, "/foo-{user}-{id}", NULL, NULL); // r3_tree_insert_path(n, "/foo-{user}-{id}", NULL, NULL);
ret_node = r3_tree_insert_path(n, "/post/{handle", NULL); ret_node = r3_tree_insert_path(n, "/post/{handle", NULL);
@ -319,7 +400,7 @@ START_TEST (test_incomplete_slug_path)
ck_assert(ret_node); ck_assert(ret_node);
// OK to insert, but should return error when compiling patterns // OK to insert, but should return error when compiling patterns
node * ret_node2 = r3_tree_insert_path(n, "/users/{idx:\\d{3}}/{idy:aaa}", NULL); R3Node * ret_node2 = r3_tree_insert_path(n, "/users/{idx:\\d{3}}/{idy:aaa}", NULL);
ck_assert(ret_node2); ck_assert(ret_node2);
ck_assert(ret_node2 != ret_node); // make sure it's another node ck_assert(ret_node2 != ret_node); // make sure it's another node
@ -328,7 +409,7 @@ START_TEST (test_incomplete_slug_path)
r3_tree_compile(n, &errstr); r3_tree_compile(n, &errstr);
ck_assert(errstr == NULL); // no error ck_assert(errstr == NULL); // no error
r3_tree_dump(n, 0); // r3_tree_dump(n, 0);
r3_tree_free(n); r3_tree_free(n);
} }
@ -337,7 +418,7 @@ END_TEST
START_TEST (test_pcre_patterns_insert) START_TEST (test_pcre_patterns_insert)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
// r3_tree_insert_path(n, "/foo-{user}-{id}", NULL, NULL); // r3_tree_insert_path(n, "/foo-{user}-{id}", NULL, NULL);
r3_tree_insert_path(n, "/post/{handle:\\d+}-{id:\\d+}", NULL); r3_tree_insert_path(n, "/post/{handle:\\d+}-{id:\\d+}", NULL);
@ -352,7 +433,7 @@ START_TEST (test_pcre_patterns_insert)
// r3_tree_dump(n, 0); // r3_tree_dump(n, 0);
node *matched; R3Node *matched;
matched = r3_tree_match(n, "/post/foo", NULL); matched = r3_tree_match(n, "/post/foo", NULL);
@ -384,12 +465,53 @@ START_TEST (test_pcre_patterns_insert)
END_TEST END_TEST
/**
* Test for root '/'
*/
START_TEST (test_root_match)
{
R3Node * n = r3_tree_create(10);
int a = 10;
int b = 20;
int c = 30;
r3_tree_insert_path(n, "/", &a);
r3_tree_insert_path(n, "/foo", &b);
r3_tree_insert_path(n, "/bar", &c);
char *errstr = NULL;
r3_tree_compile(n, &errstr);
// r3_tree_dump(n, 0);
R3Node *matched;
matched = r3_tree_match(n, "/", NULL);
ck_assert(matched);
ck_assert(matched->data == &a);
ck_assert(matched->endpoint > 0);
matched = r3_tree_match(n, "/foo", NULL);
ck_assert(matched);
ck_assert(matched->data == &b);
ck_assert(matched->endpoint > 0);
matched = r3_tree_match(n, "/bar", NULL);
ck_assert(matched);
ck_assert(matched->data == &c);
ck_assert(matched->endpoint > 0);
r3_tree_free(n);
}
END_TEST
/** /**
* Test for \d{2}/\d{2} * Test for \d{2}/\d{2}
*/ */
START_TEST (test_pcre_patterns_insert_2) START_TEST (test_pcre_patterns_insert_2)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
r3_tree_insert_path(n, "/post/{idx:\\d{2}}/{idy:\\d{2}}", NULL); r3_tree_insert_path(n, "/post/{idx:\\d{2}}/{idy:\\d{2}}", NULL);
r3_tree_insert_path(n, "/zoo", NULL); r3_tree_insert_path(n, "/zoo", NULL);
r3_tree_insert_path(n, "/foo", NULL); r3_tree_insert_path(n, "/foo", NULL);
@ -398,11 +520,13 @@ START_TEST (test_pcre_patterns_insert_2)
char *errstr = NULL; char *errstr = NULL;
r3_tree_compile(n, &errstr); r3_tree_compile(n, &errstr);
r3_tree_dump(n, 0); // r3_tree_dump(n, 0);
node *matched; R3Node *matched;
matched = r3_tree_match(n, "/post/11/22", NULL); matched = r3_tree_match(n, "/post/11/22", NULL);
ck_assert((int)matched); ck_assert(matched);
ck_assert(matched->endpoint > 0); ck_assert(matched->endpoint > 0);
r3_tree_free(n);
} }
END_TEST END_TEST
@ -411,14 +535,14 @@ END_TEST
*/ */
START_TEST (test_pcre_patterns_insert_3) START_TEST (test_pcre_patterns_insert_3)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
printf("Inserting /post/{idx:\\d{2}}/{idy}\n"); printf("Inserting /post/{idx:\\d{2}}/{idy}\n");
r3_tree_insert_path(n, "/post/{idx:\\d{2}}/{idy}", NULL); r3_tree_insert_path(n, "/post/{idx:\\d{2}}/{idy}", NULL);
r3_tree_dump(n, 0); // r3_tree_dump(n, 0);
printf("Inserting /zoo\n"); printf("Inserting /zoo\n");
r3_tree_insert_path(n, "/zoo", NULL); r3_tree_insert_path(n, "/zoo", NULL);
r3_tree_dump(n, 0); // r3_tree_dump(n, 0);
r3_tree_insert_path(n, "/foo", NULL); r3_tree_insert_path(n, "/foo", NULL);
r3_tree_insert_path(n, "/bar", NULL); r3_tree_insert_path(n, "/bar", NULL);
@ -426,12 +550,12 @@ START_TEST (test_pcre_patterns_insert_3)
char *errstr = NULL; char *errstr = NULL;
r3_tree_compile(n, &errstr); r3_tree_compile(n, &errstr);
r3_tree_dump(n, 0); // r3_tree_dump(n, 0);
node *matched; R3Node *matched;
matched = r3_tree_match(n, "/post/11/22", NULL); matched = r3_tree_match(n, "/post/11/22", NULL);
ck_assert((int)matched); ck_assert(matched);
matched = r3_tree_match(n, "/post/11", NULL); matched = r3_tree_match(n, "/post/11", NULL);
ck_assert(!matched); ck_assert(!matched);
@ -443,6 +567,8 @@ START_TEST (test_pcre_patterns_insert_3)
matched = r3_tree_match(n, "/post/113", NULL); matched = r3_tree_match(n, "/post/113", NULL);
ck_assert(!matched); ck_assert(!matched);
*/ */
r3_tree_free(n);
} }
END_TEST END_TEST
@ -450,12 +576,12 @@ END_TEST
START_TEST (test_insert_pathl_fail) START_TEST (test_insert_pathl_fail)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
node * ret; R3Node * ret;
char *errstr = NULL; char *errstr = NULL;
ret = r3_tree_insert_pathl_ex(n, "/foo/{name:\\d{5}", strlen("/foo/{name:\\d{5}"), NULL, NULL, &errstr); ret = r3_tree_insert_pathl_ex(n, "/foo/{name:\\d{5}", strlen("/foo/{name:\\d{5}"), 0, 0, 0, &errstr);
ck_assert(ret == NULL); ck_assert(ret == NULL);
ck_assert(errstr != NULL); ck_assert(errstr != NULL);
printf("%s\n", errstr); // Returns Incomplete slug pattern. PATTERN (16): '/foo/{name:\d{5}', OFFSET: 16, STATE: 1 printf("%s\n", errstr); // Returns Incomplete slug pattern. PATTERN (16): '/foo/{name:\d{5}', OFFSET: 16, STATE: 1
@ -469,14 +595,31 @@ START_TEST (test_insert_pathl_fail)
} }
END_TEST END_TEST
START_TEST (test_insert_pathl_fail2)
{
R3Node * n = r3_tree_create(10);
char *errstr = NULL;
R3Node * ret;
ret = r3_tree_insert_pathl_ex(n, "/foo", strlen("/foo"), 0, 0, 0, NULL);
ck_assert(ret);
/* Insert an incomplete pattern without requesting an error string */
ret = r3_tree_insert_pathl_ex(n, "/foo/{name:\\d{5}", strlen("/foo/{name:\\d{5}"), 0, 0, 0, NULL);
ck_assert(ret == NULL);
r3_tree_compile(n, &errstr);
ck_assert(errstr == NULL);
r3_tree_free(n);
}
END_TEST
START_TEST (test_insert_pathl) START_TEST (test_insert_pathl)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
node * ret; R3Node * ret;
ret = r3_tree_insert_path(n, "/foo/bar", NULL); ret = r3_tree_insert_path(n, "/foo/bar", NULL);
ck_assert(ret); ck_assert(ret);
@ -512,8 +655,7 @@ START_TEST (test_insert_pathl)
char * errstr = NULL; char * errstr = NULL;
r3_tree_compile(n, &errstr); r3_tree_compile(n, &errstr);
ck_assert(errstr == NULL); ck_assert(errstr == NULL);
// r3_tree_dump(n, 0);
r3_tree_dump(n, 0);
r3_tree_free(n); r3_tree_free(n);
} }
END_TEST END_TEST
@ -522,9 +664,9 @@ END_TEST
START_TEST (test_compile_fail) START_TEST (test_compile_fail)
{ {
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
node * ret; R3Node * ret;
ret = r3_tree_insert_path(n, "/foo/{idx}/{idy:)}", NULL); ret = r3_tree_insert_path(n, "/foo/{idx}/{idy:)}", NULL);
ck_assert(ret); ck_assert(ret);
@ -538,7 +680,7 @@ START_TEST (test_compile_fail)
fprintf(stderr, "Compile failed: %s\n", errstr); fprintf(stderr, "Compile failed: %s\n", errstr);
free(errstr); free(errstr);
r3_tree_dump(n, 0); // r3_tree_dump(n, 0);
r3_tree_free(n); r3_tree_free(n);
} }
END_TEST END_TEST
@ -567,7 +709,9 @@ END_TEST
START_TEST(test_route_cmp) START_TEST(test_route_cmp)
{ {
route *r1 = r3_route_create("/blog/post"); R3Node * n = r3_tree_create(10);
char *test_str = "/blog/post";
R3Route *r1 = r3_node_append_route(n,test_str, strlen(test_str),0,0);
match_entry * m = match_entry_create("/blog/post"); match_entry * m = match_entry_create("/blog/post");
fail_if( r3_route_cmp(r1, m) == -1, "should match"); fail_if( r3_route_cmp(r1, m) == -1, "should match");
@ -584,8 +728,8 @@ START_TEST(test_route_cmp)
m->request_method = METHOD_POST | METHOD_GET; m->request_method = METHOD_POST | METHOD_GET;
fail_if( r3_route_cmp(r1, m) == -1, "should match"); fail_if( r3_route_cmp(r1, m) == -1, "should match");
r3_route_free(r1);
match_entry_free(m); match_entry_free(m);
r3_tree_free(n);
} }
END_TEST END_TEST
@ -594,16 +738,17 @@ START_TEST(test_pcre_pattern_simple)
{ {
match_entry * entry; match_entry * entry;
entry = match_entry_createl( "/user/123" , strlen("/user/123") ); entry = match_entry_createl( "/user/123" , strlen("/user/123") );
node * n = r3_tree_create(10); R3Node * n = r3_tree_create(10);
r3_tree_insert_path(n, "/user/{id:\\d+}", NULL); r3_tree_insert_path(n, "/user/{id:\\d+}", NULL);
r3_tree_insert_path(n, "/user", NULL); r3_tree_insert_path(n, "/user", NULL);
r3_tree_compile(n, NULL); r3_tree_compile(n, NULL);
// r3_tree_dump(n, 0); // r3_tree_dump(n, 0);
node *matched; R3Node *matched;
matched = r3_tree_matchl(n, "/user/123", strlen("/user/123"), entry); matched = r3_tree_matchl(n, "/user/123", strlen("/user/123"), entry);
ck_assert(matched); ck_assert(matched);
ck_assert(entry->vars->len > 0); ck_assert(entry->vars.tokens.size > 0);
ck_assert_str_eq(entry->vars->tokens[0],"123"); ck_assert_str_eq(entry->vars.tokens.entries[0].base,"123");
match_entry_free(entry);
r3_tree_free(n); r3_tree_free(n);
} }
END_TEST END_TEST
@ -612,8 +757,9 @@ END_TEST
START_TEST(test_pcre_pattern_more) START_TEST(test_pcre_pattern_more)
{ {
match_entry * entry; match_entry * entry;
entry = match_entry_createl( "/user/123" , strlen("/user/123") ); entry = match_entry_create( "/user/123" );
node * n = r3_tree_create(10); entry->request_method = 0;
R3Node * n = r3_tree_create(10);
int var0 = 5; int var0 = 5;
int var1 = 100; int var1 = 100;
@ -630,53 +776,54 @@ START_TEST(test_pcre_pattern_more)
r3_tree_insert_path(n, "/user3/{id:\\d{3}}", &var3); r3_tree_insert_path(n, "/user3/{id:\\d{3}}", &var3);
r3_tree_insert_path(n, "/user", &var0); r3_tree_insert_path(n, "/user", &var0);
r3_tree_compile(n, NULL); r3_tree_compile(n, NULL);
r3_tree_dump(n, 0); // r3_tree_dump(n, 0);
node *matched; R3Node *matched;
matched = r3_tree_matchl(n, "/user/123", strlen("/user/123"), entry); matched = r3_tree_match(n, "/user/123", entry);
ck_assert(matched); ck_assert(matched);
ck_assert(entry->vars->len > 0); ck_assert(entry->vars.tokens.size > 0);
ck_assert_str_eq(entry->vars->tokens[0],"123"); ck_assert_str_eq(entry->vars.tokens.entries[0].base,"123");
info("matched %p\n", matched->data);
info("matched %p\n", matched->data); info("matched %p\n", matched->data);
ck_assert_int_eq( *((int*) matched->data), var1); ck_assert_int_eq( *((int*) matched->data), var1);
matched = r3_tree_matchl(n, "/user2/123", strlen("/user2/123"), entry); matched = r3_tree_matchl(n, "/user2/123", strlen("/user2/123"), entry);
ck_assert(matched); ck_assert(matched);
ck_assert(entry->vars->len > 0); ck_assert(entry->vars.tokens.size > 0);
ck_assert_str_eq(entry->vars->tokens[0],"123"); ck_assert_str_eq(entry->vars.tokens.entries[0].base,"123");
info("matched %p\n", matched->data);
ck_assert_int_eq( *((int*)matched->data), var2); ck_assert_int_eq( *((int*)matched->data), var2);
matched = r3_tree_matchl(n, "/user3/123", strlen("/user3/123"), entry); matched = r3_tree_matchl(n, "/user3/123", strlen("/user3/123"), entry);
ck_assert(matched); ck_assert(matched);
ck_assert(entry->vars->len > 0); ck_assert(entry->vars.tokens.size > 0);
ck_assert_str_eq(entry->vars->tokens[0],"123"); ck_assert_str_eq(entry->vars.tokens.entries[0].base,"123");
info("matched %p\n", matched->data);
ck_assert_int_eq( *((int*)matched->data), var3); ck_assert_int_eq( *((int*)matched->data), var3);
match_entry_free(entry);
r3_tree_free(n); r3_tree_free(n);
} }
END_TEST END_TEST
START_TEST(test_ip_cmp) START_TEST(test_insert_pathl_before_root)
{ {
ck_assert(r3_ip_cmp("127.0.0.1","127.0.0.1")); char *errstr = NULL;
ck_assert(!r3_ip_cmp("127.0.0.1","220.12.12.12")); int var1 = 22;
} int var2 = 33;
END_TEST int var3 = 44;
R3Node * n = r3_tree_create(3);
r3_tree_insert_pathl_ex(n, STR("/blog/post"), 0, 0, &var1, NULL);
r3_tree_insert_pathl_ex(n, STR("/blog"), 0, 0, &var2, NULL);
r3_tree_insert_pathl_ex(n, STR("/"), 0, 0, &var3, NULL);
errstr = NULL;
r3_tree_compile(n, &errstr);
r3_tree_dump(n, 0);
START_TEST(test_ip_mask_cmp) r3_tree_free(n);
{
ck_assert( r3_ip_mask_cmp("127.123.123.123" , "127.0.0.1" , "255.0.0.0" ));
ck_assert( r3_ip_mask_cmp("192.168.123.123" , "192.168.0.1" , "255.0.0.0" ));
ck_assert( r3_ip_mask_cmp("192.168.123.123" , "192.168.0.1" , "255.255.0.0" ));
ck_assert( r3_ip_mask_cmp("192.168.123.123" , "192.168.123.1" , "255.255.255.0" ));
ck_assert(!r3_ip_mask_cmp("127.123.123.123" , "127.0.0.1", "255.255.0.0" ));
ck_assert(!r3_ip_mask_cmp("127.123.123.123" , "127.0.0.1", "255.255.255.0" ));
} }
END_TEST END_TEST
@ -687,12 +834,12 @@ START_TEST(test_insert_route)
int var2 = 33; int var2 = 33;
node * n = r3_tree_create(2); R3Node * n = r3_tree_create(2);
r3_tree_insert_route(n, METHOD_GET, "/blog/post", &var1); r3_tree_insert_route(n, METHOD_GET, "/blog/post", &var1);
r3_tree_insert_route(n, METHOD_POST, "/blog/post", &var2); r3_tree_insert_route(n, METHOD_POST, "/blog/post", &var2);
match_entry * entry; match_entry * entry;
route *r; R3Route *r;
entry = match_entry_create("/blog/post"); entry = match_entry_create("/blog/post");
entry->request_method = METHOD_GET; entry->request_method = METHOD_GET;
@ -717,33 +864,41 @@ START_TEST(test_insert_route)
} }
END_TEST END_TEST
Suite* r3_suite (void) { Suite* r3_suite (void) {
Suite *suite = suite_create("r3 core tests"); Suite *suite = suite_create("r3 core tests");
TCase *tcase = tcase_create("common_prefix_testcase");
TCase *tcase = tcase_create("testcase");
tcase_add_test(tcase, test_find_common_prefix); tcase_add_test(tcase, test_find_common_prefix);
tcase_add_test(tcase, test_find_common_prefix_after); tcase_add_test(tcase, test_find_common_prefix_after);
tcase_add_test(tcase, test_find_common_prefix_double_middle); tcase_add_test(tcase, test_find_common_prefix_double_middle);
tcase_add_test(tcase, test_find_common_prefix_middle); tcase_add_test(tcase, test_find_common_prefix_middle);
tcase_add_test(tcase, test_find_common_prefix_same_pattern); tcase_add_test(tcase, test_find_common_prefix_same_pattern);
tcase_add_test(tcase, test_find_common_prefix_same_pattern2); tcase_add_test(tcase, test_find_common_prefix_same_pattern2);
tcase_add_test(tcase, test_find_common_prefix_multi_edge);
suite_add_tcase(suite, tcase);
tcase = tcase_create("insert_testcase");
tcase_add_test(tcase, test_insert_pathl); tcase_add_test(tcase, test_insert_pathl);
tcase_add_test(tcase, test_insert_pathl_fail); tcase_add_test(tcase, test_insert_pathl_fail);
tcase_add_test(tcase, test_insert_pathl_fail2);
tcase_add_test(tcase, test_node_construct_and_free); tcase_add_test(tcase, test_node_construct_and_free);
suite_add_tcase(suite, tcase);
tcase = tcase_create("compile_testcase");
tcase_add_test(tcase, test_compile); tcase_add_test(tcase, test_compile);
tcase_add_test(tcase, test_compile_fail); tcase_add_test(tcase, test_compile_fail);
tcase_add_test(tcase, test_route_cmp); tcase_add_test(tcase, test_route_cmp);
tcase_add_test(tcase, test_insert_route); tcase_add_test(tcase, test_insert_route);
tcase_add_test(tcase, test_insert_pathl_before_root);
suite_add_tcase(suite, tcase);
tcase = tcase_create("pcre_pattern_testcase");
tcase_add_test(tcase, test_pcre_pattern_simple); tcase_add_test(tcase, test_pcre_pattern_simple);
tcase_add_test(tcase, test_pcre_pattern_more); tcase_add_test(tcase, test_pcre_pattern_more);
tcase_add_test(tcase, test_pcre_patterns_insert); tcase_add_test(tcase, test_pcre_patterns_insert);
tcase_add_test(tcase, test_pcre_patterns_insert_2); tcase_add_test(tcase, test_pcre_patterns_insert_2);
tcase_add_test(tcase, test_root_match);
tcase_add_test(tcase, test_pcre_patterns_insert_3); tcase_add_test(tcase, test_pcre_patterns_insert_3);
tcase_add_test(tcase, test_incomplete_slug_path); tcase_add_test(tcase, test_incomplete_slug_path);
tcase_add_test(tcase, test_ip_cmp);
tcase_add_test(tcase, test_ip_mask_cmp);
suite_add_tcase(suite, tcase); suite_add_tcase(suite, tcase);
return suite; return suite;