@@ -15,7 +15,7 @@ namespace sourcemeta::core {
1515namespace {
1616
1717constexpr std::uint32_t ROUTER_MAGIC = 0x52544552 ; // "RTER"
18- constexpr std::uint32_t ROUTER_VERSION = 4 ;
18+ constexpr std::uint32_t ROUTER_VERSION = 5 ;
1919constexpr std::uint32_t NO_CHILD = std::numeric_limits<std::uint32_t >::max();
2020
2121// Type tags for argument value serialization
@@ -31,6 +31,7 @@ struct RouterHeader {
3131 std::uint32_t arguments_offset;
3232 std::uint32_t base_path_offset;
3333 std::uint32_t base_path_length;
34+ std::uint32_t otherwise_context;
3435};
3536
3637struct ArgumentEntryHeader {
@@ -258,6 +259,7 @@ auto URITemplateRouterView::save(const URITemplateRouter &router,
258259 header.string_table_offset + string_table.size ());
259260 header.base_path_offset = base_path_string_offset;
260261 header.base_path_length = static_cast <std::uint32_t >(base_path_value.size ());
262+ header.otherwise_context = router.otherwise_context ();
261263
262264 std::ofstream file (path, std::ios::binary);
263265 if (!file) {
@@ -333,10 +335,23 @@ auto URITemplateRouterView::match(
333335 return {};
334336 }
335337
338+ const auto otherwise_context =
339+ static_cast <URITemplateRouter::Identifier>(header->otherwise_context );
340+ const auto finalize =
341+ [otherwise_context](const URITemplateRouter::Identifier identifier,
342+ const URITemplateRouter::Identifier context)
343+ -> std::pair<URITemplateRouter::Identifier,
344+ URITemplateRouter::Identifier> {
345+ if (identifier == 0 ) {
346+ return {URITemplateRouter::Identifier{0 }, otherwise_context};
347+ }
348+ return {identifier, context};
349+ };
350+
336351 if (header->node_count == 0 ||
337352 header->node_count > (this ->data_ .size () - sizeof (RouterHeader)) /
338353 sizeof (SerializedNode)) {
339- return {} ;
354+ return finalize ( 0 , 0 ) ;
340355 }
341356
342357 const auto *nodes = reinterpret_cast <const SerializedNode *>(
@@ -346,12 +361,12 @@ auto URITemplateRouterView::match(
346361 const auto expected_string_table_offset = sizeof (RouterHeader) + nodes_size;
347362 if (header->string_table_offset < expected_string_table_offset ||
348363 header->string_table_offset > this ->data_ .size ()) {
349- return {} ;
364+ return finalize ( 0 , 0 ) ;
350365 }
351366
352367 if (header->arguments_offset < header->string_table_offset ||
353368 header->arguments_offset > this ->data_ .size ()) {
354- return {} ;
369+ return finalize ( 0 , 0 ) ;
355370 }
356371
357372 const auto *string_table = reinterpret_cast <const char *>(
@@ -361,29 +376,29 @@ auto URITemplateRouterView::match(
361376
362377 // Empty path matches empty template
363378 if (path.empty ()) {
364- return { nodes[0 ].identifier , nodes[0 ].context } ;
379+ return finalize ( nodes[0 ].identifier , nodes[0 ].context ) ;
365380 }
366381
367382 // Root path "/" is stored as an empty literal segment
368383 if (path.size () == 1 && path[0 ] == ' /' ) {
369384 const auto &root = nodes[0 ];
370385 if (root.first_literal_child == NO_CHILD) {
371- return {} ;
386+ return finalize ( 0 , 0 ) ;
372387 }
373388
374389 if (root.first_literal_child >= header->node_count ||
375390 root.literal_child_count >
376391 header->node_count - root.first_literal_child ) {
377- return {} ;
392+ return finalize ( 0 , 0 ) ;
378393 }
379394
380395 const auto match = binary_search_literal_children (
381396 nodes, string_table, string_table_size, root.first_literal_child ,
382397 root.literal_child_count , " " , 0 );
383398 if (match == NO_CHILD) {
384- return {} ;
399+ return finalize ( 0 , 0 ) ;
385400 }
386- return { nodes[match].identifier , nodes[match].context } ;
401+ return finalize ( nodes[match].identifier , nodes[match].context ) ;
387402 }
388403
389404 // Walk the trie, matching each path segment
@@ -410,7 +425,7 @@ auto URITemplateRouterView::match(
410425
411426 // Empty segment (from double slash or trailing slash) doesn't match
412427 if (segment_length == 0 ) {
413- return {} ;
428+ return finalize ( 0 , 0 ) ;
414429 }
415430
416431 const auto &node = nodes[current_node];
@@ -420,7 +435,7 @@ auto URITemplateRouterView::match(
420435 if (node.first_literal_child != NO_CHILD) {
421436 if (node.first_literal_child >= node_count ||
422437 node.literal_child_count > node_count - node.first_literal_child ) {
423- return {} ;
438+ return finalize ( 0 , 0 ) ;
424439 }
425440
426441 const auto literal_match = binary_search_literal_children (
@@ -441,15 +456,15 @@ auto URITemplateRouterView::match(
441456 if (node.variable_child >= node_count ||
442457 variable_index >
443458 std::numeric_limits<URITemplateRouter::Index>::max ()) {
444- return {} ;
459+ return finalize ( 0 , 0 ) ;
445460 }
446461
447462 const auto &variable_node = nodes[node.variable_child ];
448463
449464 if (variable_node.string_offset > string_table_size ||
450465 variable_node.string_length >
451466 string_table_size - variable_node.string_offset ) {
452- return {} ;
467+ return finalize ( 0 , 0 ) ;
453468 }
454469
455470 // Check if this is an expansion (catch-all)
@@ -460,7 +475,7 @@ auto URITemplateRouterView::match(
460475 {string_table + variable_node.string_offset ,
461476 variable_node.string_length },
462477 {segment_start, remaining_length});
463- return { variable_node.identifier , variable_node.context } ;
478+ return finalize ( variable_node.identifier , variable_node.context ) ;
464479 }
465480
466481 // Regular variable - match single segment
@@ -478,10 +493,10 @@ auto URITemplateRouterView::match(
478493 }
479494
480495 // No match
481- return {} ;
496+ return finalize ( 0 , 0 ) ;
482497 }
483498
484- return { nodes[current_node].identifier , nodes[current_node].context } ;
499+ return finalize ( nodes[current_node].identifier , nodes[current_node].context ) ;
485500}
486501
487502auto URITemplateRouterView::arguments (
0 commit comments