Skip to main content

c_src/libwrouter/src/segment.c

#include "common.h"
#include "symbol.h"
#include "wrouter.h"
#include "segment.h"
#include "token.h"
#include "graph.h"
#include <stdint.h>
#include <stdlib.h>

segment_t *segment_create(symbol_table_t *symtbl, token_t *token, wrouter_error_t *err)
{
    // Create segment.
    segment_t *segment = calloc(1, sizeof(segment_t));
    if (segment == NULL) {
        *err = WROUTER_ERR_NO_MEMORY;
        return NULL;
    }

    // Append parameter symbol to the parameter symbol table.
    segment->str = symbol_append(symtbl, token->ptr, token->length, err);
    segment->str_length = token->length;
    if (segment->str == NULL || *err) {
        free(segment);
        return NULL;
    }

    return segment;
}

wrouter_error_t segment_append_child(segment_t *cur, segment_t *child)
{
    if (cur->child_count >= NODE_MAX_CHILD_COUNT)
        return WROUTER_ERR_OUT_OF_RANGE;

    if (cur->head == NULL) {
        cur->head = child;
        cur->tail = child;
    } else {
        cur->tail->next = child;
        cur->tail = child;
    }

    cur->child_count++;

    return WROUTER_OK;
}

/**
 * Find a child of a segment by token.
 */
segment_t *segment_find_child_by_token(const segment_t *segment, const token_t tok)
{
    if (tok.ptr == NULL)
        return NULL;

    for (segment_t *child = segment->head; child; child = child->next) {
        if (token_matches_segment(tok, child))
            return child;
    }

    return NULL;
}

void segment_free(segment_t *segment)
{
    if (segment == NULL)
        return;

    switch (segment->spec_type) {
        case SPEC_WILDCARD:
            free(segment->special.wildcard);
            break;

        case SPEC_PARAM:
            segment_free(segment->special.param);
            break;

        case SPEC_NONE:
            break;
    }

    // Free child segments.
    segment_t *child = segment->head;
    while (child) {
        segment_t *next = child->next;

        segment_free(child);

        child = next;
    }

    free(segment->trailing);
    free(segment->terminal);
    free(segment);
}

void segment_release(const segment_t *segment, const wrouter_reference_fn release)
{
    // Descend child segments.
    for (const segment_t *child = segment->head; child; child = child->next) {
        segment_release(child, release);
    }

    switch (segment->spec_type) {
        case SPEC_PARAM:
            // Descend into parameters.
            segment_release(segment->special.param, release);
            break;

        case SPEC_WILDCARD:
            // Release wildcard route context.
            release(segment->special.wildcard->ctx);
            break;

        case SPEC_NONE:
            break;
    }

    // Release trailing-slash route context.
    if (segment->trailing)
        release(segment->trailing->ctx);

    // Release segment terminal route context.
    if (segment->terminal)
        release(segment->terminal->ctx);
}