#include "graph.h"
#include "wrouter.h"
#include "router.h"
#include "symbol.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <limits.h>
/**
* Compile the route graph from the builder's route tree.
*
* This will compile an immutable router from a route tree, which is therefore
* thread-safe. The router consists of a graph, symbols, and terminals.
*/
wrouter_t *wrouter_compile(const wrouter_builder_t *builder, wrouter_error_t *err)
{
graph_stats_t stats = { 0 };
uint16_t *cursor;
// Require an error output so failures can be reported.
if (err == NULL)
return NULL;
*err = WROUTER_OK;
if (builder == NULL) {
*err = WROUTER_ERR_NULL_ARGUMENT;
return NULL;
}
if (builder->corrupted) {
*err = WROUTER_ERR_BUILDER_CORRUPTED;
return NULL;
}
// New router.
wrouter_t *router = calloc(1, sizeof(wrouter_t));
if (router == NULL)
goto no_memory;
// Copy options from the builder onto the router.
router->fallback = builder->fallback;
router->retain = builder->retain;
router->release = builder->release;
router_retain(router, &router->fallback);
// Obtain graph statistics.
graph_stats(builder->root, &stats);
router->num_routes = stats.terminals;
router->max_params = stats.max_params;
router->graph_size = stats.size;
// Range checking.
if (stats.size > UINT16_MAX || stats.terminals > UINT16_MAX)
goto out_of_range;
// Allocate and compile symbols for literals.
if ((*err = symbol_compile(&builder->literals, &router->literals)))
goto failure;
// Allocate and compile symbols for parameters.
if ((*err = symbol_compile(&builder->params, &router->params)))
goto failure;
if (terminal_alloc(&router->terminals, stats.terminals))
goto no_memory;
// Allocate the graph.
router->graph = calloc(stats.size, sizeof(uint16_t));
if (router->graph == NULL)
goto no_memory;
// Compile the graph.
cursor = router->graph;
graph_compile(router, builder->root, &cursor);
return router;
out_of_range:
*err = WROUTER_ERR_OUT_OF_RANGE;
goto failure;
no_memory:
*err = WROUTER_ERR_NO_MEMORY;
goto failure;
failure:
wrouter_free(router);
return NULL;
}
/**
* Compile the router and destroy the builder.
*/
wrouter_t *wrouter_consume(wrouter_builder_t **bpp, wrouter_error_t *err)
{
wrouter_t *router;
if (err == NULL)
goto failure;
if (bpp == NULL || *bpp == NULL) {
*err = WROUTER_ERR_NULL_ARGUMENT;
goto failure;
}
router = wrouter_compile(*bpp, err);
wrouter_builder_destroy(bpp);
return router;
failure:
// Always destroy the builder.
wrouter_builder_destroy(bpp);
return NULL;
}