Skip to content

Commit bf3910c

Browse files
committed
[WIP] Prototype a URI Template router
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent f1d58ad commit bf3910c

10 files changed

Lines changed: 1398 additions & 3 deletions

File tree

config.cmake.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS})
5454
elseif(component STREQUAL "uri")
5555
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake")
5656
elseif(component STREQUAL "uritemplate")
57+
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
5758
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uritemplate.cmake")
5859
elseif(component STREQUAL "json")
5960
find_dependency(mpdecimal CONFIG)
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME uritemplate
2-
PRIVATE_HEADERS error.h token.h
3-
SOURCES helpers.h uritemplate.cc)
2+
PRIVATE_HEADERS error.h token.h router.h
3+
SOURCES helpers.h uritemplate.cc uritemplate_router.cc uritemplate_router_view.cc)
44

55
if(SOURCEMETA_CORE_INSTALL)
66
sourcemeta_library_install(NAMESPACE sourcemeta PROJECT core NAME uritemplate)
77
endif()
8+
9+
target_link_libraries(sourcemeta_core_uritemplate PUBLIC sourcemeta::core::io)

src/core/uritemplate/include/sourcemeta/core/uritemplate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
// NOLINTBEGIN(misc-include-cleaner)
99
#include <sourcemeta/core/uritemplate_error.h>
10+
#include <sourcemeta/core/uritemplate_router.h>
1011
#include <sourcemeta/core/uritemplate_token.h>
1112
// NOLINTEND(misc-include-cleaner)
1213

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#ifndef SOURCEMETA_CORE_URITEMPLATE_ROUTER_H_
2+
#define SOURCEMETA_CORE_URITEMPLATE_ROUTER_H_
3+
4+
#ifndef SOURCEMETA_CORE_URITEMPLATE_EXPORT
5+
#include <sourcemeta/core/uritemplate_export.h>
6+
#endif
7+
8+
#include <sourcemeta/core/io.h>
9+
10+
// NOLINTBEGIN(misc-include-cleaner)
11+
#include <sourcemeta/core/uritemplate_token.h>
12+
// NOLINTEND(misc-include-cleaner)
13+
14+
#include <cstdint> // std::uint16_t, std::uint32_t, std::uint8_t
15+
#include <filesystem> // std::filesystem::path
16+
#include <functional> // std::function
17+
#include <memory> // std::unique_ptr
18+
#include <optional> // std::optional
19+
#include <string_view> // std::string_view
20+
#include <vector> // std::vector
21+
22+
namespace sourcemeta::core {
23+
24+
#if defined(_MSC_VER)
25+
#pragma warning(push)
26+
#pragma warning(disable : 4251)
27+
#endif
28+
29+
/// @ingroup uritemplate
30+
/// A URI Template path router
31+
class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouter {
32+
public:
33+
/// A handler identifier 0 means "no handler"
34+
using Identifier = std::uint16_t;
35+
36+
/// The match callback
37+
using Callback = std::function<void(std::string_view, std::string_view)>;
38+
39+
/// A node in the router trie
40+
template <typename T> struct Node {
41+
Identifier identifier{0};
42+
43+
// This children distinction enforces that there can only be one non-literal
44+
// child at the type level. Also allows us to more efficiently search on
45+
// literals
46+
std::vector<std::unique_ptr<T>> literals;
47+
std::unique_ptr<T> variable;
48+
};
49+
50+
/// A token in the router trie
51+
struct Token {
52+
URITemplateToken value;
53+
Node<Token> data;
54+
};
55+
56+
/// Construct an empty router
57+
URITemplateRouter() = default;
58+
59+
// To avoid mistakes
60+
URITemplateRouter(const URITemplateRouter &) = delete;
61+
URITemplateRouter(URITemplateRouter &&) = delete;
62+
auto operator=(const URITemplateRouter &) -> URITemplateRouter & = delete;
63+
auto operator=(URITemplateRouter &&) -> URITemplateRouter & = delete;
64+
65+
/// Add a route to the router
66+
auto add(const std::string_view uri_template, const Identifier identifier)
67+
-> void;
68+
69+
/// Save the router to a binary file
70+
auto save(const std::filesystem::path &path) const -> void;
71+
72+
/// Match a path against the router
73+
[[nodiscard]] auto match(const std::string_view path,
74+
const Callback &callback) const -> Identifier;
75+
76+
private:
77+
Node<Token> root_;
78+
};
79+
80+
/// @ingroup uritemplate
81+
/// A read-only memory-mapped view of a serialized URI Template router
82+
class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouterView {
83+
public:
84+
/// A serialized node in the binary format
85+
struct alignas(8) Node {
86+
std::uint32_t string_offset;
87+
std::uint32_t string_length;
88+
std::uint32_t first_literal_child;
89+
std::uint32_t literal_child_count;
90+
std::uint32_t variable_child;
91+
std::uint8_t type;
92+
std::uint8_t padding;
93+
URITemplateRouter::Identifier identifier;
94+
std::uint32_t reserved;
95+
};
96+
97+
URITemplateRouterView(const std::filesystem::path &path);
98+
~URITemplateRouterView();
99+
100+
// To avoid mistakes
101+
URITemplateRouterView(const URITemplateRouterView &) = delete;
102+
URITemplateRouterView(URITemplateRouterView &&) = delete;
103+
auto operator=(const URITemplateRouterView &)
104+
-> URITemplateRouterView & = delete;
105+
auto operator=(URITemplateRouterView &&) -> URITemplateRouterView & = delete;
106+
107+
/// Match a path against the router
108+
[[nodiscard]] auto match(const std::string_view path,
109+
const URITemplateRouter::Callback &callback) const
110+
-> URITemplateRouter::Identifier;
111+
112+
private:
113+
FileView file_view_;
114+
};
115+
116+
#if defined(_MSC_VER)
117+
#pragma warning(pop)
118+
#endif
119+
120+
} // namespace sourcemeta::core
121+
122+
#endif

0 commit comments

Comments
 (0)