{"id":13407356,"url":"https://github.com/c9s/r3","last_synced_at":"2025-05-15T01:07:49.316Z","repository":{"id":16918271,"uuid":"19679590","full_name":"c9s/r3","owner":"c9s","description":"libr3 is a high-performance path dispatching library. It compiles your route paths into a prefix tree (trie). By using the constructed prefix trie in the start-up time, you may dispatch your routes with efficiency","archived":false,"fork":false,"pushed_at":"2025-01-27T14:59:16.000Z","size":1523,"stargazers_count":812,"open_issues_count":24,"forks_count":83,"subscribers_count":31,"default_branch":"2.0","last_synced_at":"2025-05-10T17:39:31.239Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://c9s.github.io/r3/bench.html","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/c9s.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-05-11T22:52:44.000Z","updated_at":"2025-03-07T15:46:42.000Z","dependencies_parsed_at":"2022-07-21T04:49:48.789Z","dependency_job_id":"d3956ab0-1602-441e-85de-3f673bc153b6","html_url":"https://github.com/c9s/r3","commit_stats":{"total_commits":778,"total_committers":31,"mean_commits":"25.096774193548388","dds":"0.24550128534704374","last_synced_commit":"48e93c6388c42eb2acafa8bf0d252cb617945956"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/c9s%2Fr3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/c9s%2Fr3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/c9s%2Fr3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/c9s%2Fr3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/c9s","download_url":"https://codeload.github.com/c9s/r3/tar.gz/refs/heads/2.0","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254010791,"owners_count":21998993,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:00:38.367Z","updated_at":"2025-05-15T01:07:49.264Z","avatar_url":"https://github.com/c9s.png","language":"C","funding_links":[],"categories":["Lib/Framework/API/Dev Tool","C"],"sub_categories":[],"readme":"R3\n================\n\n[![Build Status](https://travis-ci.org/c9s/r3.svg?branch=2.0)](https://travis-ci.org/c9s/r3)\n\n[![Coverage Status](https://coveralls.io/repos/c9s/r3/badge.svg)](https://coveralls.io/r/c9s/r3)\n\nR3 is an URL router library with high performance, thus, it's implemented in C.\nIt compiles your R3Route paths into a prefix trie.\n\nBy using the prefix tree constructed in the start-up time, you can dispatch\nthe path to the controller with high efficiency.\n\n\n\nRequirement\n-----------------------\n\n### Build Requirement\n\n* autoconf\n* automake\n* check\n* pkg-config\n\n### Runtime Requirement\n\n* pcre2\n* (optional) graphviz version 2.38.0 (20140413.2041)\n* (optional) libjson-c-dev\n\nPattern Syntax\n-----------------------\n\n    /blog/post/{id}      use [^/]+ regular expression by default.\n    /blog/post/{id:\\d+}  use `\\d+` regular expression instead of default.\n\n\nAPI\n------------------------\n\n```c\n#include \u003cr3/r3.h\u003e\n\n// create a router tree with 10 children capacity (this capacity can grow dynamically)\nR3Node *n = r3_tree_create(10);\n\nint route_data = 3;\n\n// insert the R3Route path into the router tree\nr3_tree_insert_path(n, \"/bar\", \u0026route_data); // ignore the length of path\n\nr3_tree_insert_pathl(n, \"/zoo\", strlen(\"/zoo\"), \u0026route_data );\nr3_tree_insert_pathl(n, \"/foo/bar\", strlen(\"/foo/bar\"), \u0026route_data );\n\nr3_tree_insert_pathl(n ,\"/post/{id}\", strlen(\"/post/{id}\") , \u0026route_data );\n\nr3_tree_insert_pathl(n, \"/user/{id:\\\\d+}\", strlen(\"/user/{id:\\\\d+}\"), \u0026route_data );\n\n\n// if you want to catch error, you may call the extended path function for insertion\nint data = 10;\nchar *errstr = NULL;\nR3Node *ret = r3_tree_insert_pathl_ex(n, \"/foo/{name:\\\\d{5}\", strlen(\"/foo/{name:\\\\d{5}\"), NULL, \u0026data, \u0026errstr);\nif (ret == NULL) {\n    // failed insertion\n    printf(\"error: %s\\n\", errstr);\n    free(errstr); // errstr is created from `asprintf`, so you have to free it manually.\n}\n\n\n// let's compile the tree!\nchar *errstr = NULL;\nint err = r3_tree_compile(n, \u0026errstr);\nif (err != 0) {\n    // fail\n    printf(\"error: %s\\n\", errstr);\n    free(errstr); // errstr is created from `asprintf`, so you have to free it manually.\n}\n\n\n// dump the compiled tree\nr3_tree_dump(n, 0);\n\n// match a route\nR3Node *matched_node = r3_tree_matchl(n, \"/foo/bar\", strlen(\"/foo/bar\"), NULL);\nif (matched_node) {\n    int ret = *( (int*) matched_node-\u003edata );\n}\n\n// release the tree\nr3_tree_free(n);\n```\n\n\n**Capture Dynamic Variables**\n\nIf you want to capture the variables from regular expression, you will need to\ncreate a `match_entry` object and pass the object to `r3_tree_matchl` function,\nthe catched variables will be pushed into the match entry structure:\n\n```c\nmatch_entry * entry = match_entry_create(\"/foo/bar\");\n\n// free the match entry\nmatch_entry_free(entry);\n```\n\nAnd you can even specify the request method restriction:\n\n```c\nentry-\u003erequest_method = METHOD_GET;\nentry-\u003erequest_method = METHOD_POST;\nentry-\u003erequest_method = METHOD_GET | METHOD_POST;\n```\n\nWhen using `match_entry`, you may match the R3Route with `r3_tree_match_entry` function:\n\n```c\nR3Node * matched_node = r3_tree_match_entry(n, entry);\n```\n\n\n\n\n**Release Memory**\n\nTo release the memory, you may call `r3_tree_free(R3Node *tree)` to release the whole tree structure,\n`node*`, `edge*`, `route*` objects that were inserted into the tree will be freed.\n\n\n\n\n\n\n### Routing with conditions\n\n```c\n// create a router tree with 10 children capacity (this capacity can grow dynamically)\nn = r3_tree_create(10);\n\nint route_data = 3;\n\n// insert the R3Route path into the router tree\nr3_tree_insert_routel(n, METHOD_GET | METHOD_POST, \"/blog/post\", sizeof(\"/blog/post\") - 1, \u0026route_data );\n\nchar *errstr = NULL;\nint err = r3_tree_compile(n, \u0026errstr);\nif (err != 0) {\n    // fail\n    printf(\"error: %s\\n\", errstr);\n    free(errstr); // errstr is created from `asprintf`, so you have to free it manually.\n}\n\n\n// in your http server handler\n\n// create the match entry for capturing dynamic variables.\nmatch_entry * entry = match_entry_create(\"/blog/post\");\nentry-\u003erequest_method = METHOD_GET;\n\n\nR3Route *matched_R3Route = r3_tree_match_route(n, entry);\nmatched_route-\u003edata; // get the data from matched route\n\n// free the objects at the end\nmatch_entry_free(entry);\nr3_tree_free(n);\n```\n\nSlug\n-----------------------\nA slug is a placeholder, which captures the string from the URL as a variable.\nSlugs will be compiled into regular expression patterns.\n\nSlugs without patterns (like `/user/{userId}`) will be compiled into the `[^/]+` pattern.\n\nTo specify the pattern of a slug, you may write a colon to separate the slug name and the pattern:\n\n    \"/user/{userId:\\\\d+}\"\n\nThe above R3Route will use `\\d+` as its pattern.\n\n\nOptimization\n-----------------------\nSimple regular expressions are optimized through a regexp pattern to opcode\ntranslator, which translates simple patterns into small \u0026 fast scanners.\n\nBy using this method, r3 reduces the matching overhead of pcre2 library.\n\nOptimized patterns are: `[a-z]+`, `[0-9]+`, `\\d+`, `\\w+`, `[^/]+`, `[^-]+` or `.*`.\n\nSlugs without specified regular expression will be compiled into the `[^/]+` pattern. therefore, it's optimized too.\n\nComplex regular expressions will still use libpcre2 to match URL (partially).\n\n\nPerformance\n-----------------------\nThe routing benchmark from stevegraham/rails' PR \u003chttps://github.com/stevegraham/rails/pull/1\u003e:\n\n                 omg    10462.0 (±6.7%) i/s -      52417 in   5.030416s\n\nAnd here is the result of the router journey:\n\n                 omg     9932.9 (±4.8%) i/s -      49873 in   5.033452s\n\nr3 uses the same R3Route path data for benchmarking, and here is the benchmark:\n\n                3 runs, 5000000 iterations each run, finished in 1.308894 seconds\n                11460057.83 i/sec\n\n\n### The Route Paths Of Benchmark\n\nThe R3Route path generator is from \u003chttps://github.com/stevegraham/rails/pull/1\u003e:\n\n```ruby\n#!/usr/bin/env ruby\narr    = [\"foo\", \"bar\", \"baz\", \"qux\", \"quux\", \"corge\", \"grault\", \"garply\"]\npaths  = arr.permutation(3).map { |a| \"/#{a.join '/'}\" }\npaths.each do |path|\n    puts \"r3_tree_insert_path(n, \\\"#{path}\\\", NULL);\"\nend\n```\n\nFunction prefix mapping\n-----------------------\n\n|Function Prefix   |Description                                                                         |\n|------------------|------------------------------------------------------------------------------------|\n|`r3_tree_*`       |Tree related operations, which require a node to operate a whole tree               |\n|`r3_node_*`       |Single node related operations, which do not go through its own children or parent. |\n|`r3_edge_*`       |Edge related operations                                                             |\n|`r3_route_*`      |Route related operations, which are needed only when the tree is defined by routes  |\n|`match_entry_*`   |Match entry related operations, a `match_entry` is just like the request parameters |\n\n\n\n\nRendering Routes With Graphviz\n---------------------------------------\n\nThe `r3_tree_render_file` API let you render the whole R3Route trie into a image.\n\nTo use graphviz, you need to enable graphviz while you run `configure`:\n\n\n    ./configure --enable-graphviz\n\n\nHere is the sample code of generating graph output:\n\n\n```c\nR3Node * n = r3_tree_create(1);\n\nr3_tree_insert_path(n, \"/foo/bar/baz\",  NULL);\nr3_tree_insert_path(n, \"/foo/bar/qux\",  NULL);\nr3_tree_insert_path(n, \"/foo/bar/quux\",  NULL);\nr3_tree_insert_path(n, \"/foo/bar/corge\",  NULL);\nr3_tree_insert_path(n, \"/foo/bar/grault\",  NULL);\nr3_tree_insert_path(n, \"/garply/grault/foo\",  NULL);\nr3_tree_insert_path(n, \"/garply/grault/bar\",  NULL);\nr3_tree_insert_path(n, \"/user/{id}\",  NULL);\nr3_tree_insert_path(n, \"/post/{title:\\\\w+}\",  NULL);\n\nchar *errstr = NULL;\nint err;\nerr = r3_tree_compile(n, \u0026errstr);\nif (err != 0) {\n    // fail\n    printf(\"error: %s\\n\", errstr);\n    free(errstr); // errstr is created from `asprintf`, so you have to free it manually.\n}\n\nr3_tree_render_file(n, \"png\", \"check_gvc.png\");\nr3_tree_free(n);\n```\n\n\n![Imgur](http://imgur.com/HrUoEbI.png)\n\nOr you can even export it with dot format:\n\n```dot\ndigraph g {\n\tgraph [bb=\"0,0,205.1,471\"];\n\tnode [label=\"\\N\"];\n\t\"{root}\"\t [height=0.5,\n\t\tpos=\"35.097,453\",\n\t\twidth=0.97491];\n\t\"#1\"\t [height=0.5,\n\t\tpos=\"35.097,366\",\n\t\twidth=0.75];\n        ....\n```\n\n### Graphviz Related Functions\n\n```c\nint r3_tree_render_file(const R3Node * tree, const char * format, const char * filename);\n\nint r3_tree_render(const R3Node * tree, const char *layout, const char * format, FILE *fp);\n\nint r3_tree_render_dot(const R3Node * tree, const char *layout, FILE *fp);\n\nint r3_tree_render_file(const R3Node * tree, const char * format, const char * filename);\n```\n\n\nJSON Output\n----------------------------------------\n\nYou can render the whole tree structure into json format output.\n\nPlease run `configure` with the `--enable-json` option.\n\nHere is the sample code to generate JSON string:\n\n```c\njson_object * obj = r3_node_to_json_object(n);\n\nconst char *json = r3_node_to_json_pretty_string(n);\nprintf(\"Pretty JSON: %s\\n\",json);\n\nconst char *json = r3_node_to_json_string(n);\nprintf(\"JSON: %s\\n\",json);\n```\n\n\nUse case in PHP\n-----------------------\n**not implemented yet**\n\n```php\n// Here is the paths data structure\n$paths = [\n    '/blog/post/{id}' =\u003e [ 'controller' =\u003e 'PostController' , 'action' =\u003e 'item'   , 'method'   =\u003e 'GET' ] ,\n    '/blog/post'      =\u003e [ 'controller' =\u003e 'PostController' , 'action' =\u003e 'list'   , 'method'   =\u003e 'GET' ] ,\n    '/blog/post'      =\u003e [ 'controller' =\u003e 'PostController' , 'action' =\u003e 'create' , 'method' =\u003e 'POST' ]  ,\n    '/blog'           =\u003e [ 'controller' =\u003e 'BlogController' , 'action' =\u003e 'list'   , 'method'   =\u003e 'GET' ] ,\n];\n$rs = r3_compile($paths, 'persisten-table-id');\n$ret = r3_dispatch($rs, '/blog/post/3' );\nlist($complete, $route, $variables) = $ret;\n\n// matched conditions aren't done yet\nlist($error, $message) = r3_validate($route); // validate R3Route conditions\nif ( $error ) {\n    echo $message; // \"Method not allowed\", \"...\";\n}\n```\n\nInstall\n----------------------\n\n    sudo apt-get install check libpcre2 libpcre2-dev libjemalloc-dev libjemalloc1 build-essential libtool automake autoconf pkg-config\n    sudo apt-get install graphviz-dev graphviz  # if you want graphviz\n    ./autogen.sh\n    ./configure \u0026\u0026 make\n    sudo make install\n\nAnd we support debian-based distro now!\n\n    sudo apt-get install build-essential autoconf automake libpcre2-dev pkg-config debhelper libtool check\n    mv dist-debian debian\n    dpkg-buildpackage -b -us -uc\n    sudo gdebi ../libr3*.deb\n\n\n#### Run Unit Tests\n\n    ./configure --enable-check\n    make check\n\n#### Enable Graphviz\n\n    ./configure --enable-graphviz\n\n#### With jemalloc\n\n    ./configure --with-malloc=jemalloc\n\nubuntu PPA\n----------------------\n\nThe PPA for libr3 can be found in \u003chttps://launchpad.net/~r3-team/+archive/libr3-daily\u003e.\n\nBinding For Other Languages\n---------------------------\n\n* Perl Router::R3 by @CindyLinz \u003chttps://metacpan.org/pod/Router::R3\u003e\n* Python pyr3 by @lucemia \u003chttps://github.com/lucemia/pyr3\u003e\n* Python pyr3 by @thedrow \u003chttps://github.com/thedrow/pyr3\u003e\n* Haskell r3 by @MnO2 \u003chttps://github.com/MnO2/r3\u003e\n* Vala r3-vala by @Ronmi \u003chttps://github.com/Ronmi/r3-vala\u003e\n\nNode.js\n\n* node-r3 by @othree \u003chttps://github.com/othree/node-r3\u003e\n* node-libr3 by @caasi \u003chttps://github.com/caasi/node-r3\u003e\n\nRuby\n\n* Ruby rr3 by @tonytonyjan \u003chttps://github.com/tonytonyjan/rr3\u003e\n* mruby r3 \u003chttps://github.com/rail44/mruby-r3\u003e\n* mruby rake r3 \u003chttps://github.com/rail44/mruby-rack-r3\u003e\n\n\nLicense\n--------------------\nThis software is released under MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fc9s%2Fr3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fc9s%2Fr3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fc9s%2Fr3/lists"}