11#include < sourcemeta/core/uritemplate.h>
22
3+ #include < array> // std::array
34#include < cassert> // assert
45#include < cstring> // std::memcmp, std::memcpy
56#include < fstream> // std::ofstream, std::ifstream
@@ -14,7 +15,7 @@ namespace sourcemeta::core {
1415namespace {
1516
1617constexpr std::uint32_t ROUTER_MAGIC = 0x52544552 ; // "RTER"
17- constexpr std::uint32_t ROUTER_VERSION = 3 ;
18+ constexpr std::uint32_t ROUTER_VERSION = 4 ;
1819constexpr std::uint32_t NO_CHILD = std::numeric_limits<std::uint32_t >::max();
1920
2021// Type tags for argument value serialization
@@ -47,6 +48,8 @@ struct alignas(8) SerializedNode {
4748 URITemplateRouter::NodeType type;
4849 std::uint8_t padding;
4950 URITemplateRouter::Identifier identifier;
51+ URITemplateRouter::Identifier context;
52+ std::array<std::uint8_t , 6 > padding2;
5053};
5154
5255// Binary search for a literal child matching the given segment
@@ -109,6 +112,7 @@ auto URITemplateRouterView::save(const URITemplateRouter &router,
109112 root_serialized.type = URITemplateRouter::NodeType::Root;
110113 root_serialized.padding = 0 ;
111114 root_serialized.identifier = root.identifier ;
115+ root_serialized.context = root.context ;
112116
113117 if (root.literals .empty ()) {
114118 root_serialized.first_literal_child = NO_CHILD;
@@ -146,6 +150,7 @@ auto URITemplateRouterView::save(const URITemplateRouter &router,
146150
147151 serialized.padding = 0 ;
148152 serialized.identifier = node->identifier ;
153+ serialized.context = node->context ;
149154
150155 const auto first_child_index =
151156 static_cast <std::uint32_t >(nodes.size () + queue.size () + 1 );
@@ -314,23 +319,24 @@ URITemplateRouterView::URITemplateRouterView(const std::uint8_t *data,
314319 const std::size_t size)
315320 : data_{data, data + size} {}
316321
317- auto URITemplateRouterView::match (const std::string_view path,
318- const URITemplateRouter::Callback &callback)
319- const -> URITemplateRouter::Identifier {
322+ auto URITemplateRouterView::match (
323+ const std::string_view path,
324+ const URITemplateRouter::Callback &callback) const
325+ -> std::pair<URITemplateRouter::Identifier, URITemplateRouter::Identifier> {
320326 if (this ->data_ .size () < sizeof (RouterHeader)) {
321- return 0 ;
327+ return {} ;
322328 }
323329
324330 const auto *header =
325331 reinterpret_cast <const RouterHeader *>(this ->data_ .data ());
326332 if (header->magic != ROUTER_MAGIC || header->version != ROUTER_VERSION) {
327- return 0 ;
333+ return {} ;
328334 }
329335
330336 if (header->node_count == 0 ||
331337 header->node_count > (this ->data_ .size () - sizeof (RouterHeader)) /
332338 sizeof (SerializedNode)) {
333- return 0 ;
339+ return {} ;
334340 }
335341
336342 const auto *nodes = reinterpret_cast <const SerializedNode *>(
@@ -340,12 +346,12 @@ auto URITemplateRouterView::match(const std::string_view path,
340346 const auto expected_string_table_offset = sizeof (RouterHeader) + nodes_size;
341347 if (header->string_table_offset < expected_string_table_offset ||
342348 header->string_table_offset > this ->data_ .size ()) {
343- return 0 ;
349+ return {} ;
344350 }
345351
346352 if (header->arguments_offset < header->string_table_offset ||
347353 header->arguments_offset > this ->data_ .size ()) {
348- return 0 ;
354+ return {} ;
349355 }
350356
351357 const auto *string_table = reinterpret_cast <const char *>(
@@ -355,26 +361,29 @@ auto URITemplateRouterView::match(const std::string_view path,
355361
356362 // Empty path matches empty template
357363 if (path.empty ()) {
358- return nodes[0 ].identifier ;
364+ return { nodes[0 ].identifier , nodes[ 0 ]. context } ;
359365 }
360366
361367 // Root path "/" is stored as an empty literal segment
362368 if (path.size () == 1 && path[0 ] == ' /' ) {
363369 const auto &root = nodes[0 ];
364370 if (root.first_literal_child == NO_CHILD) {
365- return 0 ;
371+ return {} ;
366372 }
367373
368374 if (root.first_literal_child >= header->node_count ||
369375 root.literal_child_count >
370376 header->node_count - root.first_literal_child ) {
371- return 0 ;
377+ return {} ;
372378 }
373379
374380 const auto match = binary_search_literal_children (
375381 nodes, string_table, string_table_size, root.first_literal_child ,
376382 root.literal_child_count , " " , 0 );
377- return match != NO_CHILD ? nodes[match].identifier : 0 ;
383+ if (match == NO_CHILD) {
384+ return {};
385+ }
386+ return {nodes[match].identifier , nodes[match].context };
378387 }
379388
380389 // Walk the trie, matching each path segment
@@ -401,7 +410,7 @@ auto URITemplateRouterView::match(const std::string_view path,
401410
402411 // Empty segment (from double slash or trailing slash) doesn't match
403412 if (segment_length == 0 ) {
404- return 0 ;
413+ return {} ;
405414 }
406415
407416 const auto &node = nodes[current_node];
@@ -411,7 +420,7 @@ auto URITemplateRouterView::match(const std::string_view path,
411420 if (node.first_literal_child != NO_CHILD) {
412421 if (node.first_literal_child >= node_count ||
413422 node.literal_child_count > node_count - node.first_literal_child ) {
414- return 0 ;
423+ return {} ;
415424 }
416425
417426 const auto literal_match = binary_search_literal_children (
@@ -432,15 +441,15 @@ auto URITemplateRouterView::match(const std::string_view path,
432441 if (node.variable_child >= node_count ||
433442 variable_index >
434443 std::numeric_limits<URITemplateRouter::Index>::max ()) {
435- return 0 ;
444+ return {} ;
436445 }
437446
438447 const auto &variable_node = nodes[node.variable_child ];
439448
440449 if (variable_node.string_offset > string_table_size ||
441450 variable_node.string_length >
442451 string_table_size - variable_node.string_offset ) {
443- return 0 ;
452+ return {} ;
444453 }
445454
446455 // Check if this is an expansion (catch-all)
@@ -451,7 +460,7 @@ auto URITemplateRouterView::match(const std::string_view path,
451460 {string_table + variable_node.string_offset ,
452461 variable_node.string_length },
453462 {segment_start, remaining_length});
454- return variable_node.identifier ;
463+ return { variable_node.identifier , variable_node. context } ;
455464 }
456465
457466 // Regular variable - match single segment
@@ -469,10 +478,10 @@ auto URITemplateRouterView::match(const std::string_view path,
469478 }
470479
471480 // No match
472- return 0 ;
481+ return {} ;
473482 }
474483
475- return nodes[current_node].identifier ;
484+ return { nodes[current_node].identifier , nodes[current_node]. context } ;
476485}
477486
478487auto URITemplateRouterView::arguments (
0 commit comments