{"id":22220593,"url":"https://github.com/fix8mt/uri","last_synced_at":"2026-02-11T21:05:30.105Z","repository":{"id":215362038,"uuid":"738749183","full_name":"fix8mt/uri","owner":"fix8mt","description":"Lightweight header-only C++20 URI parser","archived":false,"fork":false,"pushed_at":"2024-12-09T19:26:16.000Z","size":3461,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-27T15:55:01.855Z","etag":null,"topics":["c-plus-plus-20","cplusplus-20","cpp20","fix8-market","header-only","no-dependencies","normalization","rfc-3986","single-file","uniform-resource-identifiers","uri","uri-parse","uri-parser","uri-query","uri-query-parser","url-parser","url-parsing","url-query"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fix8mt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2024-01-04T00:40:33.000Z","updated_at":"2025-06-28T20:33:20.000Z","dependencies_parsed_at":null,"dependency_job_id":"1e595bc3-e6cc-4598-aaa0-882b70285e11","html_url":"https://github.com/fix8mt/uri","commit_stats":null,"previous_names":["dakka/uri"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/fix8mt/uri","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Furi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Furi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Furi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Furi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fix8mt","download_url":"https://codeload.github.com/fix8mt/uri/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Furi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29345433,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-11T20:11:40.865Z","status":"ssl_error","status_checked_at":"2026-02-11T20:10:41.637Z","response_time":97,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["c-plus-plus-20","cplusplus-20","cpp20","fix8-market","header-only","no-dependencies","normalization","rfc-3986","single-file","uniform-resource-identifiers","uri","uri-parse","uri-parser","uri-query","uri-query-parser","url-parser","url-parsing","url-query"],"created_at":"2024-12-02T23:09:17.579Z","updated_at":"2026-02-11T21:05:30.089Z","avatar_url":"https://github.com/fix8mt.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.fix8mt.com\"\u003e\u003cimg src=\"https://github.com/fix8mt/uri/blob/master/assets/fix8mt_Master_Logo_Green_Trans.png\" width=\"200\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# uri\n\n### A lightweight C++20 URI parser\n\n------------------------------------------------------------------------\n[![Ubuntu](https://github.com/fix8mt/uri/actions/workflows/cmake-single-platform.yml/badge.svg)](https://github.com/fix8mt/uri/actions/workflows/cmake-single-platform.yml)\n\u003ca href=\"https://en.wikipedia.org/wiki/C%2B%2B20\"\u003e\u003cimg src=\"https://github.com/fix8mt/uri/blob/master/assets/badgecpprequired.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://http://www.boost.org/LICENSE_1_0.txt\"\u003e\u003cimg src=\"https://github.com/fix8mt/uri/blob/master/assets/badgeboostlic.svg\"\u003e\u003c/a\u003e\n\n# Quick links\n|**Link**|**Description**|\n--|--\n|[Here](https://github.com/fix8mt/uri/blob/master/include/fix8/uri.hpp)| For implementation|\n|[Examples](#2-examples)| For examples|\n|[Building](#3-building)| How to build or include|\n|[API](#4-api)| API details |\n|[Testing](#testing)| Testing apps|\n|[Benchmark](#benchmarks)| Benchmark info|\n|[Discussion](#6-discussion)| Discussion |\n\n\u003e [!TIP]\n\u003e Use the built-in [table of contents](https://github.blog/changelog/2021-04-13-table-of-contents-support-in-markdown-files/) to navigate this guide.\n\n------------------------------------------------------------------------\n# 1. Introduction\nThis is a lightweight URI parser implementation featuring zero-copy, minimal storage and high performance.\n\n## Motivation\n- header-only\n- zero-copy where possible (base class uses views only)\n- no external dependencies\n- simplicity, lightweight\n- make use of C++20 features\n- entirely `constexpr`\n- high performance\n\n## Features\n- single _header-only_\n- fast, very lightweight, predictive non brute force parser: avg 52ns[^1] to decode a URI with `basic_uri`\n- base class is zero-copy, using `std::string_view`\n- derived class moves (or copies) source string once\n- all methods `constexpr`; no virtual methods\n- extracts all components `scheme`, `authority`, `userinfo`, `user`, `password`, `host`, `port`, `path`, `query`, `fragment`\n- query components returned as `std::string_view`\n- query decode and search; segment decode; no copying, all results point to uri source\n- small memory footprint - base class object is only 64 bytes\n- support for dynamic or static uri storage\n- built-in unit test cases with exhaustive test URI cases; simple test case addition\n- normalization ([**RFC 3986**](https://datatracker.ietf.org/doc/html/rfc3986))\n- cmake integration with FetchContent\n\n# 2. Examples\n## i. Use `basic_uri` as a view\nThis example parses a list of URI strings and prints out `host` component. `basic_uri` creates a no-copy view of the source. `get_component()`\nreturns a `std::string_view`.\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003esource\u003c/i\u003e\u003c/summary\u003e\n\u003cp\u003e\n\n```c++\n#include \u003ciostream\u003e\n#include \u003carray\u003e\n#include \u003cfix8/uri.hpp\u003e\nusing namespace FIX8;\n\nint main(int argc, char *argv[])\n{\n   using namespace std::literals;\n   static constexpr std::array uris\n   {\n      \"https://www.blah.com:3000/test\"sv,\n      \"https://dakka@www.staylate.net:3000/\"sv,\n      \"https://www.buyexample.com/over/there?name=ferret\u0026time=any#afrag\"sv,\n   };\n   for (const auto\u0026 pp : uris)\n      std::cout \u003c\u003c basic_uri(pp).get_component\u003curi::host\u003e() \u003c\u003c '\\n';\n   return 0;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003eoutput\u003c/i\u003e\u003c/summary\u003e\n\u003c/p\u003e\n\n```CSV\n$ ./example1\nwww.blah.com\nwww.staylate.net\nwww.buyexample.com\n$\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## ii. Using a `uri`\nThis example parses a URI string and prints out all the contained elements. Then individual components are queried and printed if present.\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003esource\u003c/i\u003e\u003c/summary\u003e\n\u003cp\u003e\n\n```c++\n#include \u003ciostream\u003e\n#include \u003cfix8/uri.hpp\u003e\nusing namespace FIX8;\n\nint main(int argc, char *argv[])\n{\n   const uri u1 {\"http://nodejs.org:89/docs/latest/api/foo/bar/qua/13949281/0f28b/5d49/b3020/url.html\"\n      \"?payload1=true\u0026payload2=false\u0026test=1\u0026benchmark=3\u0026foo=38.38.011.293\"\n      \"\u0026bar=1234834910480\u0026test=19299\u00263992\u0026key=f5c65e1e98fe07e648249ad41e1cfdb0#test\"};\n\n   std::cout \u003c\u003c u1 \u003c\u003c '\\n';\n\n   std::cout \u003c\u003c u1.get_authority() \u003c\u003c '\\n'\n      \u003c\u003c u1.get_host() \u003c\u003c '\\n'\n      \u003c\u003c u1.get_port() \u003c\u003c '\\n'\n      \u003c\u003c u1.get_query() \u003c\u003c '\\n'\n      \u003c\u003c u1.get_fragment() \u003c\u003c '\\n';\n   if (u1.has_user()) // should be no user\n      std::cout \u003c\u003c u1.get_user() \u003c\u003c '\\n';\n   auto result{u1.decode_query(true)}; // sort result\n   std::cout \u003c\u003c \"key = \" \u003c\u003c uri::find_query(\"key\", result) \u003c\u003c '\\n';\n   return 0;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003eoutput\u003c/i\u003e\u003c/summary\u003e\n\u003c/p\u003e\n\n```CSV\n$ ./example2\nuri         http://nodejs.org:89/docs/latest/api/foo/bar/qua/13949281/0f28b/5d49/b3020/url.html?payload1=true\u0026payload2=false\u0026test=1\u0026benchmark=3\u0026foo=38.38.011.293\u0026bar=1234834910480\u0026test=19299\u00263992\u0026key=f5c65e1e98fe07e648249ad41e1cfdb0#test\nscheme      http\nauthority   nodejs.org:89\nhost        nodejs.org\nport        89\npath        /docs/latest/api/foo/bar/qua/13949281/0f28b/5d49/b3020/url.html\n   docs\n   latest\n   api\n   foo\n   bar\n   qua\n   13949281\n   0f28b\n   5d49\n   b3020\n   url.html\nquery       payload1=true\u0026payload2=false\u0026test=1\u0026benchmark=3\u0026foo=38.38.011.293\u0026bar=1234834910480\u0026test=19299\u00263992\u0026key=f5c65e1e98fe07e648249ad41e1cfdb0\n   payload1    true\n   payload2    false\n   test        1\n   benchmark   3\n   foo         38.38.011.293\n   bar         1234834910480\n   test        19299\n   3992        (empty)\n   key         f5c65e1e98fe07e648249ad41e1cfdb0\nfragment    test\n\nnodejs.org:89\nnodejs.org\n89\npayload1=true\u0026payload2=false\u0026test=1\u0026benchmark=3\u0026foo=38.38.011.293\u0026bar=1234834910480\u0026test=19299\u00263992\u0026key=f5c65e1e98fe07e648249ad41e1cfdb0\ntest\nkey = f5c65e1e98fe07e648249ad41e1cfdb0\n$\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## iii. Using a `uri_static`\nCreate a static URI with a maximum storage of 256 bytes, from the supplied string. Print out the result and max storage.\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003esource\u003c/i\u003e\u003c/summary\u003e\n\u003cp\u003e\n\n```c++\n#include \u003ciostream\u003e\n#include \u003cfix8/uri.hpp\u003e\nusing namespace FIX8;\n\nint main(int argc, char *argv[])\n{\n   uri_static\u003c256\u003e u1{\"mailto:John.Smith@example.com\"};\n   std::cout \u003c\u003c u1 \u003c\u003c '\\n';\n   std::cout \u003c\u003c \"max storage \" \u003c\u003c u1.max_storage() \u003c\u003c '\\n';\n   return 0;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003eoutput\u003c/i\u003e\u003c/summary\u003e\n\u003c/p\u003e\n\n```CSV\n$ ./example3\nuri         mailto:John.Smith@example.com\nscheme      mailto\npath        John.Smith@example.com\nmax storage 256\n$\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## iv. Use factory or format\nCreate a URI from an initializer list or using format specification. Note we chose to percent-encode a part of the path. Print out the results.\nBoth the factory and format versions produce the same uri.\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003esource (factory)\u003c/i\u003e\u003c/summary\u003e\n\u003cp\u003e\n\n```c++\n#include \u003ciostream\u003e\n#include \u003cfix8/uri.hpp\u003e\nusing namespace FIX8;\nusing enum uri::component;\n\nint main(int argc, char *argv[])\n{\n   const auto u1 { uri::factory({{scheme, \"https\"}, {user, \"dakka\"}, {host, \"www.blah.com\"}, {port, \"3000\"},\n      {path, \"/foo/\" + basic_uri::encode_hex(\"this path has embedded spaces\") + \"/test\"}}) };\n   std::cout \u003c\u003c u1 \u003c\u003c '\\n';\n   return 0;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003eoutput (factory)\u003c/i\u003e\u003c/summary\u003e\n\u003c/p\u003e\n\n```CSV\n$ ./example4\nuri         https://dakka@www.blah.com:3000/foo/this%20path%20has%20embedded%20spaces/test\nscheme      https\nauthority   dakka@www.blah.com:3000\nuserinfo    dakka\nuser        dakka\nhost        www.blah.com\nport        3000\npath        /foo/this%20path%20has%20embedded%20spaces/test\n   foo\n   this%20path%20has%20embedded%20spaces\n   test\n$\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003esource (format)\u003c/i\u003e\u003c/summary\u003e\n\u003cp\u003e\n\n```c++\n#include \u003ciostream\u003e\n#include \u003cfix8/uri.hpp\u003e\nusing namespace FIX8;\n\nint main(int argc, char *argv[])\n{\n   const auto u1 { uri::format(\"{}://{}@{}:{}/{}/{}/{}\", \"https\", \"dakka\", \"www.blah.com\", \"3000\", \"foo\",\n      basic_uri::encode_hex(\"this path has embedded spaces\"), \"test\") };\n   std::cout \u003c\u003c u1 \u003c\u003c '\\n';\n   return 0;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003eoutput (format)\u003c/i\u003e\u003c/summary\u003e\n\u003c/p\u003e\n\n```CSV\n$ ./example4\nuri         https://dakka@www.blah.com:3000/foo/this%20path%20has%20embedded%20spaces/test\nscheme      https\nauthority   dakka@www.blah.com:3000\nuserinfo    dakka\nuser        dakka\nhost        www.blah.com\nport        3000\npath        /foo/this%20path%20has%20embedded%20spaces/test\n   foo\n   this%20path%20has%20embedded%20spaces\n   test\n$\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## v. Using edit\nCreate a URI and then edit it.\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003esource\u003c/i\u003e\u003c/summary\u003e\n\u003cp\u003e\n\n```c++\n#include \u003ciostream\u003e\n#include \u003cfix8/uri.hpp\u003e\nusing namespace FIX8;\nusing enum uri::component;\n\nint main(int argc, char *argv[])\n{\n   uri u1 { \"https://dakka@www.blah.com:3000\" };\n   std::cout \u003c\u003c u1 \u003c\u003c '\\n';\n   u1.edit({{port, \"80\"}, {user, \"\"}, {path, \"/newpath\"}});\n   std::cout \u003c\u003c '\\n' \u003c\u003c u1 \u003c\u003c '\\n';\n   return 0;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003eoutput\u003c/i\u003e\u003c/summary\u003e\n\u003c/p\u003e\n\n```CSV\n$ ./example5\nuri         https://dakka@www.blah.com:3000\nscheme      https\nauthority   dakka@www.blah.com:3000\nuserinfo    dakka\nuser        dakka\nhost        www.blah.com\nport        3000\npath        (empty)\n\nuri         https://www.blah.com:80/newpath\nscheme      https\nauthority   www.blah.com:80\nhost        www.blah.com\nport        80\npath        /newpath\n$\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n# 3. Building\nThis implementation is header only. Apart from standard C++20 includes there are no external dependencies needed in your application.\n[Catch2](https://github.com/catchorg/Catch2.git) is used for the built-in unit tests. [Criterion](https://github.com/p-ranav/criterion) is used for benchmarking.\n\n## i. Obtaining the source, building the examples\nTo clone and default build the test app, unit tests and the benchmark:\n```bash\ngit clone https://github.com/fix8mt/uri.git\ncd uri\nmkdir build\ncd build\ncmake ..\nmake -j4\nmake test (or ctest)\n```\nTo disable building the benchmarks, pass the following switch to cmake:\n```bash\n# cmake -DBUILD_BENCHMARKS=false ..\n```\n\n## ii. Using in your application with cmake\nIn `CMakeLists.txt` set your include path to:\n```cmake\ninclude_directories([uri directory]/include)\n# e.g.\nset(uridir /home/dd/prog/uri)\ninclude_directories(${uridir}/include)\n```\nand just include:\n```c++\n#include \u003cfix8/uri.hpp\u003e\n```\nin your application. Everything in this class is within the namespace `FIX8`, so you can add:\n```c++\nusing namespace FIX8;\n```\nThis is C++20, so you can also add in your local code:\n```c++\nusing enum uri::component;\n```\n\n## iii. Integrating uri in your project with cmake FetchContent\nYou can use cmake [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) to integrate uri with your project.\nIf your project was called `myproj` with the sourcefile `myproj.cpp` then...\n```cmake\nproject(myproj)\nadd_executable (myproj myproj.cpp)\nset_target_properties(myproj PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED true)\nmessage(STATUS \"Downloading uri...\")\ninclude(FetchContent)\nFetchContent_Declare(uri GIT_REPOSITORY https://github.com/fix8mt/uri.git)\nFetchContent_MakeAvailable(uri)\ntarget_include_directories(myproj PRIVATE ${uri_SOURCE_DIR}/include)\n```\n\n# 4. API\n## i. Class hierarchy\n### `basic_uri`\nThe base class `basic_uri` performs the bulk of the work, holding a `std::string_view` of the source uri string. If you wish to manage the scope of the source uri yourself then\nthis class is the most efficient way to use uri functionality.\n\n```c++\nbasic_uri u1{\"https://www.example.com:8080/path1\"};\n```\n\n***\n### `uri_base`\nThis class is aliased by `uri` and `uri_static`. You can inherit from class if you wish to specialise further.\n\n***\n### `uri`\nThe derived class `uri` stores the source string and then builds a `basic_uri` using that string as its reference. `uri` derives from\n`basic_uri` and a private **dynamic** storage class `uri_storage`. The supplied string is moved or copied and stored by the object. If your application needs the uri\nto hold and persist the source uri, this class is suitable.\nThe storage class used is a specialisation of `uri_storage` which specifies `0` as the non-type parameter `sz`, selecting dynamic storage.\n\n```c++\nstd::string myuri;\n.\n.\n.\nuri u1{myuri};\n```\n\n![class diagram](https://github.com/fix8mt/uri/blob/master/assets/classdynamic.png)\n\n***\n### `uri_static`\nThe derived class `uri_static` stores the source string and then builds a `basic_uri` using that string as its reference. `uri_static` derives from\n`basic_uri` and a private **static** storage class `uri_storage`. The supplied string is moved or copied and stored by the object. The class is templated by the non-type parameter\n`sz` which sets the static size and maximum storage capacity for the uri. `sz` defaults to `1024`. Storage is allocated once with the object in a `std::array`. No dynamic memory is used.\nIf your application needs the uri to hold and persist the source uri statically (for example in another container), this class is suitable.\n\n```c++\nstd::string myuri;\n.\n.\n.\nuri_static\u003c256\u003e u1{myuri};\n```\n\n![class diagram (static)](https://github.com/fix8mt/uri/blob/master/assets/classstatic.png)\n\n## ii. Types\n### component\n```c++\nenum component { scheme, authority, userinfo, user, password, host, port, path, query, fragment, countof };\n```\nComponents are named by a public enum called `component`.  Note that the component `user` and `password` are populated if present and\n`userinfo` will also be populated.\n\n### other types\n| Type | Typedef of |Description |\n| --- | --- | --- |\n| `uri_len_t`  | `std::uint16_t` | the integral type used to store offsets and lengths|\n| `value_pair`  | `std::pair\u003cstd::string_view,std::string_view\u003e` |used to return tag value pairs|\n| `query_result`  | `std::vector\u003cvalue_pair\u003e` |used to return a collection of query pairs|\n| `range_pair`  | `std::pair\u003curi_len_t,uri_len_t\u003e` |used to store offset and length |\n| `comp_pair` | `std::pair\u003ccomponent, std::string_view\u003e`|used by `factory` to pass individual `component` pairs|\n| `comp_list` | `std::vector\u003cstd::string_view\u003e`|used by `factory`,`edit` and `make_source` to pass individual `component` values; each position in the vector corresponds to the component index|\n| `segments` | same as `comp_list`|used by `decode_segments`|\n| `port_pair` | same as `value_pair`|used by `find_port`|\n| `error` | `enum class error : uri_len_t { no_error, too_long, illegal_chars, empty_src, countof };`|error types|\n\n### consts\n| Const | Description |\n| ------------- | ------------- |\n| `uri_max_len`  | the maximum length of a supplied uri|\n\n## iii. Construction and destruction\n### ctor\n```c++\nclass basic_uri;\nconstexpr basic_uri(std::string_view src);                           // (1)\nconstexpr basic_uri(int bits);                                       // (2)\nconstexpr basic_uri();                                               // (3)\n\nclass uri;\nconstexpr uri(std::string src);                                      // (4)\nconstexpr uri(std::string_view src);                                 // (5)\nconstexpr uri(const char *src);                                      // (6)\nconstexpr uri();                                                     // (7)\n\ntemplate\u003csize_t sz\u003e\nclass uri_static;\nconstexpr uri_static(std::string src);                               // (8)\nconstexpr uri_static(std::string_view src);                          // (9)\nconstexpr uri_static(const char *src);                               // (10)\nconstexpr uri_static();                                              // (11)\n```\n\n1. Construct a `basic_uri` from a `std::string_view`. This base class does not store the string. Calls `parse()`. The source string must not go out of scope to use this object. If parsing\nfails, you can check for error using `operator bool` or `count()` and then `get_error()` for more info. Since this method takes a `std::string_view` you can declare objects `constexpr`. Note\nthat `std::string` contains a convert to `std::string_view` operator.\n1. Construct a `basic_uri` that has the corresponding bitset passed in `bits`. No components are present. Permits object to be used as a component bitset.\n1. Construct an empty `basic_uri`. It can be populated using `assign()`.\n1. Construct a `uri` from a `std::string`. Calls `parse()`.\nThe supplied string is moved or copied and stored by the object. You can check for error using `operator bool` or `count()` and then `get_error()` for more info.\n1. Construct a `uri` from a `std::string_view`. Creates a `std::string` from src and delegates to (4).\n1. Construct a `uri` from a `null` terminated `const char *`. Creates a `std::string` from src and delegates to (4).\n1. Construct an empty `uri`. It can be populated using `replace()`.\n1. Construct a `uri_static` from a `std::string`. The class is templated by the non-type parameter `sz` which sets the static size and maximum storage capacity\nfor the uri. Calls `parse()`.\n1. Construct a `uri_static` from a `std::string_view`. Creates a `std::string` from src and delegates to (8).\n1. Construct a `uri_static` from a `null` terminated `const char *`. Creates a `std::string` from src and delegates to (8).\n1. Construct an empty `uri_static` from a `std::string`. The class is templated by the non-type parameter `sz` which sets the static size and maximum storage capacity for the uri.\n\nAll of `uri` is within the namespace **`FIX8`**.\n\n### dtor\n```c++\nconstexpr ~basic_uri();\nconstexpr ~uri();\nconstexpr ~uri_static();\n```\n\nDestroy the `uri` or `basic_uri`. The `uri` and `uri_static` objects will release the stored string.\n\n## iv. Accessors\n### `test`\n```c++\nconstexpr bool test(uri::component what) const;\ntemplate\u003curi::component what\u003e\nconstexpr bool test() const;\ntemplate\u003ccomponent... comp\u003e\nconstexpr int test_any() const;\ntemplate\u003ccomponent... comp\u003e\nconstexpr int test_all() const;\n```\nReturn `true` if the specified component is present in the uri. Passing `countof` returns `true` if any component is present.\nUse the template version if you know the component ahead of time.\n`test_any` can be used to test for multiple components (any or all) in a single statement. As above, use the template version if you know the component ahead of time.\nSee the [test case](https://github.com/fix8mt/uri/blob/master/examples/unittests.cpp) \"test any/all range\" for example use.\n\n### `has_any`\n```c++\nconstexpr bool has_any() const;\n```\nReturn `true` if any component is present.\n\n### `has_[?]`\n```c++\nconstexpr bool has_[?component]() const;\n```\nReturn `true` if the specified component (`scheme`, `authority`, `userinfo`, `user`, `password`, `host`, `port`, `path`, `query`, `fragment`)\nis present in the uri.\n\n```c++\nconst uri u1{\"https://www.hello.com:8080/\"};\nif (u1.has_port())\n   .\n   .\n   .\n```\n\n### `get_component`\n```c++\nconstexpr std::string_view get_component(component what) const;\ntemplate\u003ccomponent what\u003e\nconstexpr std::string_view get_component() const;\n```\nReturn a `std::string_view` of the specified component or empty if component not found. Returns an empty `std::string_view` if not found or not a legal component.\nUse the template version if you know the component ahead of time.\n\n```c++\nconst uri u1{\"https://www.hello.com:8080/\"};\nuri::component what{uri::host};\nstd::cout \u003c\u003c u1.get_component(what) \u003c\u003c '\\n';\nstd::cout \u003c\u003c u1.get_component\u003curi::host\u003e() \u003c\u003c '\\n';\n```\n\n### `get_[?]`\n```c++\nconstexpr std::string_view get_[?component]() const;\n```\nReturn a `std::string_view` of the specified component (`scheme`, `authority`, `userinfo`, `user`, `password`, `host`, `port`, `path`, `query`, `fragment`).\nReturns an empty `std::string_view` if not found or not a legal component.\n\n```c++\nconst uri u1{\"https://www.hello.com:8080/\"};\nstd::cout \u003c\u003c u1.get_host() \u003c\u003c '\\n';\n```\n\n### `get_present`\n```c++\nconstexpr uri_len_t get_present() const;\n```\nReturn the present bitset as `uri_len_t` which has bits set corresponding to the component's enum position.\n\n### `operator bool`\n```c++\nconstexpr operator bool() const;\n```\nReturns true if parsing was successful, false on fail.\n\n### `get_error`\n```c++\nconstexpr error get_error() const;\n```\nReturn the last `uri::error` error enum. If no error returns `error::no_error`. Use it to obtain the reason a uri failed to parse.\n\n### `const operator[component]`\n```c++\nconstexpr const range_pair\u0026 operator[](component idx) const;\n```\nReturn a `const range_pair\u0026` which is a `std::pair\u003curi_len_t, uri_len_t\u003e\u0026` to the specified component at the index given in the ranges table. This provides read-only\naccess to the offset and length of the specified component and is used to create a `std::string_view`.\n\u003e [!WARNING]\n\u003e This is _not_ range checked.\n\n### `const at`\n```c++\ntemplate\u003ccomponent what\u003e\nconstexpr const range_pair\u0026 at() const;\n```\nReturn a `const range_pair\u0026` which is a `std::pair\u003curi_len_t, uri_len_t\u003e\u0026` to the specified component at the component given as a template parameter. This provides read-only\naccess to the offset and length of the specified component and is used to create a `std::string_view`. No copying, results point to uri source.\nUse this template version if you know the component ahead of time, otherwise use the subscript operator.\n\u003e [!WARNING]\n\u003e This is _not_ range checked.\n\n### `in_range`\n```c++\nconstexpr int in_range(std::string_view::size_type pos) const;\n```\nReturn a bitset of all components that the given position in a uri lie within. You can use `bitsum` to test results. See the \"in range\"\n[test case](https://github.com/fix8mt/uri/blob/master/examples/unittests.cpp) for example use.\n\n### `decode_query`\n```c++\ntemplate\u003cchar separator='\u0026',char tagequ='='\u003e\nconstexpr query_result decode_query(bool sort=false) const;\n```\nReturns a `std::vector` of pairs of `std::string_view` of the query component if present.  You can optionally override the value pair separator character using\nthe first non-type template parameter - some queries use `;`. You can also optionally override the value equality separator character using the second non-type\ntemplate parameter (some queries use `:`). Pass `true` to optionally sort the `query_result` lexicographically by the key. No copying, results point to uri source.\nReturns an empty vector if no query was found. The query is assumed to be in the form:\n```\n\u0026tag=value[\u0026tag=value...]\n```\nOr if you override, say\n```\n;tag:value[;tag:value...]\n```\nIf no value is present, just the tag will be populated with an empty value.\n\n### `find_query`\n```c++\nstatic constexpr std::string_view find_query (std::string_view what, const query_result\u0026 from);\n```\nFind the specified query key and return its value from the given `query_result`. `query_result` _must be sorted_ by key, as returned when\npassing `true` to `decode_query` or by calling `sort_query` first. If key not found return empty `std::string_view`. No copying, results point to uri source.\nComplexity at most $`2 * log^2(last - first) + O(1)`$ comparisons.\n\n### `decode_hex`\n```c++\nstatic constexpr std::string decode_hex(std::string_view src, bool unreserved=false);\nstatic constexpr std::string\u0026 decode_hex(std::string\u0026 result, bool unreserved=false); // in place decode\n```\nDecode any hex values present in the supplied string. Hex values are only recognised if they are in the form `%XX` where X is a hex digit (octet) `[0-9a-fA-F]`.\nBy default all percent-encoded hex values are decoded. Return in a new string or in place. If unreserved is `true` only unreserved characters will be decoded (see `is_unreserved()`).\n\n### `encode_hex`\n```c++\nstatic constexpr std::string encode_hex(std::string_view src);\n```\nEncode any hex values present in the supplied string. Hex values are only recognised if they are in the form `%XX` where X is a hex digit (octet) `[0-9a-fA-F]`.\nOnly chars that are reserved (see `is_reserved()`), whitespace or not printable are encoded.  Return in a new encoded string.\n\n### `is_unreserved`\n```c++\nstatic constexpr bool is_unreserved(char c);\n```\nReturn true if the given char is a member of the unreserved set as per RFC 3986, sec 2.3.\n\n### `is_reserved`\n```c++\nstatic constexpr bool is_reserved(char c);\n```\nReturn true if the given char is a member of the reserved set as per RFC 3986, sec 2.2.\n\n### `has_hex`\n```c++\nstatic constexpr bool has_hex(std::string_view src);\n```\nReturn true if any hex values are present in the supplied string. Hex values are only recognised if\nthey are in the form `%XX` where X is a hex digit (octet) `[0-9a-fA-F]`.\n\n### `find_hex`\n```c++\nstatic constexpr std::string_view::size_type find_hex(std::string_view src, std::string_view::size_type pos=0);\n```\nReturn the position of the first hex value (if any) in the supplied string. Optionally supply the starting offset in pos. Hex values are only recognised if\nthey are in the form `%XX` where X is a hex digit (octet) `[0-9a-fA-F]`. If not found returns `std::string_view::npos`.\n\n### `find_port`\n```c++\nstatic constexpr std::string_view find_port(std::string_view what);\n```\nReturn the default port as a `std::string_view` for the given scheme. For example, will return `80` if given `http`. Uses private member `_default_ports`\nwhich contains pairs of scheme/ports.\n\n### `decode_segments`\n```c++\nconstexpr segments decode_segments(bool filter=true) const;\n```\nReturns a `std::vector` of segments as `std::string_view` of the path component if present. If filter is `true` (default)\nremove `./` segments if found. Returns an empty vector if no path was found.\n\n### `normalize_str`\n```c++\nstatic constexpr std::string normalize_str(std::string_view src);\n```\nNormalize the given string as per RFC 3986, sec 6. The normalizations done are only those that preserve the original semantics. These are:\n\n1. Convert scheme =\u003e lower case\n1. Convert host =\u003e lower case\n1. Convert %hex =\u003e upper case\n1. Decode unreserved hex\n1. Remove dot segments (sec 5.2.4)\n1. Convert empty path to \"/\"\n\nReturns a `std::string` of the new normalized string or the same string if no normalizations possible.\n\n### `normalize_http_str`\n```c++\nstatic constexpr std::string normalize_http_str(std::string_view src);\n```\nNormalize the given string as per RFC 3986, sec 6, as in `normalize_str()` above. In addition the following normalizations are done:\n\n1. Remove default port (http and https only)\n\nReturns a `std::string` of the new normalized string or the same string if no normalizations possible.\n\n### `normalize`\n```c++\nconstexpr std::string normalize();\n```\nSame as `normalize_str` above but operates on the source string in the uri object. Returns the _original_ string and updates the current object with the new normalized string.\n\n### `normalize_http`\n```c++\nconstexpr std::string normalize_http();\n```\nSame as `normalize_http_str` above but operates on the source string in the uri object. Returns the _original_ string and updates the current object with the new normalized string.\nThe following example demonstrates the use and results of `normalize_http` (no exception thrown):\n\n```c++\n#include \u003ccassert\u003e\n#include \u003ciostream\u003e\n#include \u003cexception\u003e\n#include \u003carray\u003e\n#include \u003cfix8/uri.hpp\u003e\nusing namespace FIX8;\n\nint main(void)\n{\n   using namespace std::literals;\n   static constexpr std::array uris\n   {\n      \"https://www.test.com/\"sv, // all should normalize to this one\n      \"https://www.test.com\"sv,\n      \"https://www.test.com:/\"sv,\n      \"https://www.test.com:443/\"sv,\n   };\n   try\n   {\n      for (const auto pp : uris)\n      {\n         uri u1{pp};\n         u1.normalize_http();\n         if (u1.get_uri() != uris[0]);\n            throw std::logic_error(\"http normalization failure\");\n      }\n   }\n   catch(const std::exception\u0026 e)\n   {\n      std::cerr \u003c\u003c e.what() \u003c\u003c '\\n';\n   }\n   return 0;\n}\n```\n\n### `get_name`\n```c++\nstatic constexpr std::string_view get_name(component what);\ntemplate\u003ccomponent what\u003e\nstatic constexpr std::string_view get_name();\n```\nReturn a `std::string_view` of the specified component name. Returns an empty `std::string_view` if not found or not a legal component.\nUse the template version if you know the component ahead of time.\n\n### `get_uri`\n```c++\nconstexpr std::string_view get_uri() const;\n```\nReturn a `std::string_view` of the source uri. If not set return value will be empty.\n\n### `count`\n```c++\nconstexpr int count() const;\n```\nReturn the count of components in the uri.\n\n### `operator\u003c\u003c`\n```c++\nfriend std::ostream\u0026 operator\u003c\u003c(std::ostream\u0026 os, const basic_uri\u0026 what);\n```\nPrint the uri object to the specified stream. The source and individual components are printed. If a query is present, each tag value pair is printed; if\na path is present, each segment value is also printed.\n\n### `operator==`\n```c++\nfriend constexpr bool operator==(const basic_uri\u0026 lhs, const basic_uri\u0026 rhs);\nfriend constexpr bool operator==(const uri\u0026 lhs, const uri\u0026 rhs);\ntemplate\u003csize_t sz\u003e\nfriend static constexpr bool uri_static\u003csz\u003e::operator==(const uri_static\u0026 lhs, const uri_static\u0026 rhs);\n```\nEquivalence operators for `basic_uri`, `uri` and `uri_static`. These are implemented as follows:\n1. `basic_uri` - return `true` if the source uri strings are identical\n1. `uri`, `uri_static` - return `true` if the normalized source uri strings are identical\n\n### `operator%`\n```c++\nfriend constexpr bool operator%(const uri\u0026 lhs, const uri\u0026 rhs);\ntemplate\u003csize_t sz\u003e\nfriend static constexpr bool uri_static\u003csz\u003e::operator%(const uri_static\u0026 lhs, const uri_static\u0026 rhs);\n```\nEquivalence operators for http protocol for `uri` and `uri_static`. Return `true` if the `normalized_http` uri strings are identical.\n\n### `get_buffer`\n```c++\nconstexpr const std::string\u0026 get_buffer() const;\n```\nReturn a `const std::string\u0026` to the stored buffer. Only available from `uri`.\n\n### `has_any_authority`\n```c++\nconstexpr bool has_any_authority() const;\n```\nReturns true if any authority components are present. This means any one of `host`, `password`, `port`, `user` or `userinfo`.\n\n### `has_any_userinfo`\n```c++\nconstexpr bool has_any_userinfo() const;\n```\nReturns true if any userinfo components are present. This means any one of `user` or `password`.\n\n### `buffer`\n```c++\nconstexpr std::string_view buffer() const;\n```\nReturns a `std::string_view` of the current buffer used for all uri objects except `basic_uri`.\n\n### `max_storage`\n```c++\nstatic constexpr uri_len_t max_storage();\n```\nReturns the maximum storage available for all uri objects except `basic_uri`. For `uri` will return `uri_max_len`, for\n`uri_static\u003csz\u003e` will return the `sz` parameter.\n\n## v. Mutators\n### `set`\n```c++\nconstexpr void set(uri::component what);\ntemplate\u003curi::component what\u003e\nconstexpr void set();\ntemplate\u003ccomponent... comp\u003e\nconstexpr void set_all();\n```\nSet the specified component bit as present in the uri. Passing `uri::countof` sets all bits. Use the template version if you know the bit ahead of time. Use carefully.\n`set_all` can be used to set multiple components in a single statement. As above, use the template version if you know the component ahead of time.\nSee the [test case](https://github.com/fix8mt/uri/blob/master/examples/unittests.cpp) \"clear/set all range\" for example use.\n\n### `clear`\n```c++\nconstexpr void uri::clear(uri::component what);\ntemplate\u003curi::component what\u003e\nconstexpr void clear();\ntemplate\u003ccomponent... comp\u003e\nconstexpr void clear_all();\n```\nClear the specified component bit in the uri. Passing `uri::countof` clears all bits. Use the template version if you know the bit ahead of time. Use carefully.\n`clear_all` can be used to clear multiple components in a single statement. As above, use the template version if you know the component ahead of time.\nSee the [test case](https://github.com/fix8mt/uri/blob/master/examples/unittests.cpp) \"clear/set all range\" for example use.\n\n### `assign`\n```c++\nconstexpr int assign(std::string_view src);\n```\nReplace the current uri reference with the given reference. No storage is allocated. Return the number of components found.\n\n### `replace`\n```c++\nconstexpr std::string replace(std::string\u0026\u0026 src);\n```\nReplace the current uri with the given string. The storage is updated with a move (or copy) of the string. The old string is returned.\n\n### `set_error`\n```c++\nconstexpr void set_error(error what);\n```\nSet the last `uri::error` error to the error given. Setting an error is destructive and renders the uri unusable.\n\n### `operator[component]`\n```c++\nconstexpr range_pair\u0026 operator[](component idx);\n```\nReturn a `range_pair\u0026` which is a `std::pair\u003curi_len_t, uri_len_t\u003e\u0026` to the specified component at the index given in the ranges table. This provides direct\naccess to the offset and length of the specified component and is used to create a `std::string_view`.\n\u003e [!WARNING]\n\u003e This is _not_ range checked. Allows for modification of the `string_view` range. Use carefully.\n\n### `at`\n```c++\ntemplate\u003ccomponent what\u003e\nconstexpr range_pair\u0026 at();\n```\nReturn a `range_pair\u0026` which is a `std::pair\u003curi_len_t, uri_len_t\u003e\u0026` to the specified component at the component given as a template parameter. This provides direct\naccess to the offset and length of the specified component and is used to create a `std::string_view`.\nUse this template version if you know the component ahead of time, otherwise use the subscript operator.\n\n```c++\nuri u1{\"https://www.hello.com:8080/\"};\nauto\u0026 rp{u1.at\u003curi::host\u003e()};\nstd::cout \u003c\u003c rp.first \u003c\u003c ' ' \u003c\u003c rp.second \u003c\u003c '\\n';\n```\n\u003e [!WARNING]\n\u003e This is _not_ range checked. Allows for modification of the `string_view` range. Use carefully.\n\n### `parse`\n```c++\nconstexpr int parse();\n```\nParse the source string into components. Return the count of components found. Will reset a uri if already parsed. You can check for error using `get_error()` for more info.\n\n### `sort_query`\n```c++\nstatic constexpr void sort_query(query_result\u0026 query);\n```\nSort the supplied query alphanumerically based on the tag in the query value pair. Complexity at most $`2 * log^2(last - first) + O(1)`$ comparisons.\n\n## vi. Generation and editing\n### `factory`\n```c++\nstatic constexpr uri uri::factory(std::initializer_list\u003ccomp_pair\u003e from);\ntemplate\u003csize_t sz\u003e\nstatic constexpr uri_static\u003csz\u003e uri_static\u003csz\u003e::factory(std::initializer_list\u003ccomp_pair\u003e from);\n```\nCreate a `uri` from the supplied components. The `initializer_list` contains a 1..n `comp_pair` objects. The following constraints apply:\n1. If `authority` is supplied and any of the following components are present `host`, `password`, `port`, `user` or `userinfo` then `authority` is ignored;\n1. If `userinfo` is supplied and any of the following components are present `user` or `password` then `userinfo` is ignored;\n\n### `format`\n```c++\ntemplate\u003ctypename... Args\u003e\nstatic constexpr uri format(std::format_string\u003cArgs...\u003e fmt, Args\u0026\u0026... args);\n```\nCreate a `uri` from the supplied format string and arguments. See `std::format` for more on how to use this function. A uri will be created from the resulting string.\nSee above for example usage.\n\n### `edit`\n```c++\nconstexpr int edit(std::initializer_list\u003ccomp_pair\u003e from);\n```\nModify an existing `uri` by replacing existing components with the supplied components. Components not specified are left unchanged. The `initializer_list` contains a 1..n `comp_pair` objects. The same constraints as `factory` apply.\n\n### `make_uri`\n```c++\nstatic constexpr std::string make_uri(std::initializer_list\u003ccomp_pair\u003e from);\n```\nConstruct a `std::string` representation of a `uri` from the supplied components. The `initializer_list` contains a 1..n `comp_pair` objects. The same constraints as `factory` apply.\n\n# 5. Testing\n## Test cases\nThe header file `uriexamples.hpp` contains a data structure holding the [test cases](https://github.com/fix8mt/uri/blob/master/examples/unittests.cpp)\nused by the [Catch2](https://github.com/catchorg/Catch2.git) unit test app `unittests` and by the CLI test app `uritest`.\nYou can add your own test cases to `uriexamples.hpp` - the structure is easy enough to follow.\n\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003esample\u003c/i\u003e\u003c/summary\u003e\n\u003cp\u003e\n\n```c++\nconst std::vector\u003cstd::pair\u003cconst char *, std::vector\u003cstd::pair\u003curi::component, const char *\u003e\u003e\u003e\u003e tests\n{\n   { \"https://www.blah.com/\",\n      {\n         { scheme, \"https\" },\n         { authority, \"www.blah.com\" },\n         { host, \"www.blah.com\" },\n         { path, \"/\" },\n      }\n   },\n   { \"https://www.blah.com\",\n      {\n         { scheme, \"https\" },\n         { authority, \"www.blah.com\" },\n         { host, \"www.blah.com\" },\n         { path, \"\" }, // empty path\n      }\n   },\n   { \"https://www.blah.com:3000/test\",\n      {\n         { scheme, \"https\" },\n         { authority, \"www.blah.com:3000\" },\n         { host, \"www.blah.com\" },\n         { port, \"3000\" },\n         { path, \"/test\" },\n      }\n   },\n   { \"https://dakka@www.blah.com:3000/\",\n      {\n         { scheme, \"https\" },\n         { authority, \"dakka@www.blah.com:3000\" },\n         { user, \"dakka\" },\n         { host, \"www.blah.com\" },\n         { port, \"3000\" },\n         { path, \"/\" },\n      }\n   },\n.\n.\n.\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## `unittests`\nThis application is run by default if you run `make test` or `ctest`. When running using `ctest` use the following command:\n\n```bash\n$ ctest --output-on-failure\n```\nReview of the test cases in `unittests.cpp` will provide more insight into using the API.\n\n## `uritest`\nThis is a simple CLI test app which allows you to run individual or all tests from `uriexamples.hpp`, or test a uri passed from the command line.\n\n```CSV\n$ ./uritest -h\nUsage: ./uritest [uri...] [-t:T:d:hlasxf:]\n -a parse all examples (default)\n -d [uri] parse uri from CLI, show debug output\n -h help\n -l list tests\n -s show sizes\n -f [file] read and dump from file\n -T [num] static test to run\n -t [num] test to run\n -x special tests\n$\n```\n\n### adhoc test\nYou can an run adhoc test from the CLI as follows:\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003eoutput\u003c/i\u003e\u003c/summary\u003e\n\u003c/p\u003e\n\n```CSV\n$ ./uritest -d \"https://user:password@example.com:3000/path?search=1\u0026key=val\u0026when=now#frag\"\nuri         https://user:password@example.com:3000/path?search=1\u0026key=val\u0026when=now#frag\nscheme      https\nauthority   user:password@example.com:3000\nuserinfo    user:password\nuser        user\npassword    password\nhost        example.com\nport        3000\npath        /path\nquery       search=1\u0026key=val\u0026when=now\n   search      1\n   key         val\n   when        now\nfragment    frag\n\nbitset 1111111111 (0x3ff)\nscheme 0 (5)\nauthority 8 (30)\nuserinfo 8 (13)\nuser 8 (4)\npassword 13 (8)\nhost 22 (11)\nport 34 (4)\npath 38 (5)\nquery 44 (25)\nfragment 70 (5)\n$\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n### read from file\nYou can also read uris from a file and print out the parsed results. This example reads from the supplied `basiclist.hpp`:\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003eoutput\u003c/i\u003e\u003c/summary\u003e\n\u003c/p\u003e\n\n```CSV\n$ ./uritest -f ../examples/basiclist.hpp\nuri         https://telegraph.co.uk/index.html\nscheme      https\nauthority   telegraph.co.uk\nhost        telegraph.co.uk\npath        /index.html\n\nuri         https://bp.blogspot.com/index.html\nscheme      https\nauthority   bp.blogspot.com\nhost        bp.blogspot.com\npath        /index.html\n.\n.\n.\nuri         https://android.com/index.html\nscheme      https\nauthority   android.com\nhost        android.com\npath        /index.html\n\nuri         https://blog.me/index.html\"\nscheme      https\nauthority   blog.me\nhost        blog.me\npath        /index.html\"\n\n1000 uri(s) read from ../examples/basiclist.hpp\n$\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## `benchmarks`\nWe use the [Criterion](https://github.com/p-ranav/criterion) benchmarking library. The benchmark app is built by default.\nThe file `basiclist.hpp` contains 1000 generic URIs. The benchmark **creates 1000** `basic_uri`, `uri` and\n`uri_static` objects and measures the total time taken. We can calculate the average time to decode each URI[^1].\n\n![Benchmarks](https://github.com/fix8mt/uri/blob/master/assets/benchmarks.png)\n\nFrom the above results we can see the following average performance per URI:\n\n| Class | Decode(ns) |\n| --- | --- |\n| `basic_uri`  | **52** _ns_ |\n| `uri`  | **90** _ns_ |\n| `uri_static`  | **90** _ns_ |\n\n[^1]: Ubuntu 23.10, 12 core 4.7GHz Intel i7 Cometlake Processors, 15.3GB RAM; gcc-13.2.0\n\n# 6. Discussion\n## i. Non-validating\nThis class is non-validating. The source URI is expected to be normalised or at least parsable. This library provides\nnormalization functions which you can apply to your source uri strings before construction.\n\nValidation is out of scope for this implementation.  We decided against validating for a few reasons:\n1. Performance - validating is expensive; most URIs are generally parsable\n1. Complex - validation rules are complicated; for most use cases, simple rejection for gross rule violation is sufficient.\nSee [URL Standard](https://url.spec.whatwg.org/) for complete validation rules.\n\n## ii. Low level access\nThere are methods that provide direct access to the `range` table and `component` bitsets. You must ensure that you don't pass an invalid component\nindex when using these. Making changes to the range object with `operator[]` can have serious consequences. Use carefully.\n1. `constexpr range_pair\u0026 operator[](component idx)`;\n1. `constexpr void set(component what);`\n1. `template\u003ccomponent what\u003e constexpr void set()`;\n1. `constexpr void clear(component what);`\n1. `template\u003ccomponent what\u003e constexpr void clear()`;\n\n## iii. Sanity checking\nThis class will perform basic sanity checks on the source URI and refuses to continue parsing. You can test for failure using the `operator bool`. These are:\n1. Length - source must not exceed `uri_max_len` (`UINT16_MAX`)\n1. Illegal chars - source must not contain any whitespace characters\n\n## iv. Performance\nThis class performs well, with minimal latency. Since there is no copying of strings or sub-strings, the decoding functionality in `basic_uri` uses minimal cycles\n\\- especially for applications that can manage the storage of the source string themselves. The memory footprint of `basic_uri` is 64 bytes and will fit in a cache-line.\n\n- If storage of the source is needed, `uri` performs a single string copy (or move), and aside from that will have the same performance as `basic_uri`.\n\n- If you need to store the source URI but wish to avoid using dynamic memory, use `uri_static` (for example for including in another container).\nThis ensures a single allocation for the entire object. For most purposes\n(and excluding edits) a statically stored URI is the most efficient storage option. Be aware that the template parameter `sz`\nmust be large enough for any URI you wish to store and of course objects created with different templated sizes will be different types.\n\n- The `factory` and `edit` have more copying although even these still use `std::string_view` where possible with actual copying of strings or sub-strings occurring\nonce at most.\n\n- With all methods `constexpr` and `noexcept`, no `virtual` methods, header only and optimisation enabled, your compiler should be able to optimise your code most efficiently.\n\n- If you want to reduce the size of `basic_uri` further, you can change:\n```c++\nusing uri_len_t = std::uint16_t;\n```\nto:\n```c++\nusing uri_len_t = std::uint8_t;\n```\nThis will limit the maximum length of a URI to 256 bytes, but reduce the overall storage needed for `basic_uri` from `64` to `40` bytes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffix8mt%2Furi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffix8mt%2Furi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffix8mt%2Furi/lists"}