Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

C Querying

Query operations and result handling in the Matchy C API.

Overview

The C API provides functions to open databases and perform queries against IPs, strings, and patterns. All query functions are thread-safe for concurrent reads.

Opening Databases

Open from File

matchy_t *matchy_open(const char *filename);

Opens a database file with validation:

  • Memory-maps the file
  • Validates MMDB structure
  • Checks PARAGLOB section
  • Validates all UTF-8 strings

Returns NULL on error.

Example:

matchy_t *db = matchy_open("database.mxy");
if (!db) {
    fprintf(stderr, "Failed to open database\n");
    return 1;
}

// Use db...

matchy_close(db);

Open Trusted Database

matchy_t *matchy_open_trusted(const char *filename);

Opens a database without UTF-8 validation:

  • ~15-20% faster than matchy_open()
  • Use only for databases you control
  • Unsafe for untrusted sources

Example:

// Safe: database created by your own application
matchy_t *db = matchy_open_trusted("internal.mxy");

⚠️ Warning: Never use with databases from untrusted sources!

Open from Buffer

matchy_t *matchy_open_buffer(const uint8_t *buffer, uintptr_t size);

Opens a database from memory:

  • Buffer must remain valid for database lifetime
  • No file I/O required
  • Useful for embedded databases

Example:

uint8_t *buffer = load_database_somehow();
uintptr_t size = get_database_size();

matchy_t *db = matchy_open_buffer(buffer, size);
if (!db) {
    free(buffer);
    return 1;
}

// Query db...

matchy_close(db);
free(buffer);  // Safe to free after close

Query Operations

Unified Lookup

int32_t matchy_lookup(matchy_t *db, 
                      const char *text, 
                      matchy_result_t **result);

Queries the database with automatic type detection:

  • IP address: Parses as IPv4 or IPv6
  • Domain/string: Searches patterns and exact strings
  • Other text: Pattern matching only

Returns:

  • MATCHY_SUCCESS (0) on success
  • Error code on failure
  • *result set to NULL if no match

Example:

matchy_result_t *result = NULL;
int32_t err = matchy_lookup(db, "192.0.2.1", &result);

if (err != MATCHY_SUCCESS) {
    fprintf(stderr, "Query error: %d\n", err);
    return 1;
}

if (result != NULL) {
    printf("Match found!\n");
    matchy_free_result(result);
} else {
    printf("No match\n");
}

IP Lookup

int32_t matchy_lookup_ip(matchy_t *db, 
                         struct sockaddr *addr, 
                         matchy_result_t **result);

Direct IP lookup using sockaddr:

  • Supports IPv4 (sockaddr_in)
  • Supports IPv6 (sockaddr_in6)
  • Faster than parsing text

Example:

struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("192.0.2.1");

matchy_result_t *result = NULL;
int32_t err = matchy_lookup_ip(db, (struct sockaddr *)&addr, &result);

if (err == MATCHY_SUCCESS && result) {
    // Process result...
    matchy_free_result(result);
}

String Lookup

int32_t matchy_lookup_string(matchy_t *db, 
                             const char *text, 
                             matchy_result_t **result);

Pattern and exact string matching:

  • Searches glob patterns
  • Searches exact string table
  • Returns first match

Example:

matchy_result_t *result = NULL;
int32_t err = matchy_lookup_string(db, "test.example.com", &result);

if (err == MATCHY_SUCCESS && result) {
    printf("Matched pattern or exact string\n");
    matchy_free_result(result);
}

Result Handling

Get Result Type

uint32_t matchy_result_type(const matchy_result_t *result);

Returns the match type:

  • MATCHY_RESULT_IP (1) - IP address match
  • MATCHY_RESULT_PATTERN (2) - Pattern match
  • MATCHY_RESULT_EXACT_STRING (3) - Exact string match

Example:

uint32_t type = matchy_result_type(result);

switch (type) {
case MATCHY_RESULT_IP:
    printf("IP match\n");
    break;
case MATCHY_RESULT_PATTERN:
    printf("Pattern match\n");
    break;
case MATCHY_RESULT_EXACT_STRING:
    printf("Exact string match\n");
    break;
}

Get Entry Data

int32_t matchy_result_get_entry(const matchy_result_t *result,
                                matchy_entry_s *entry);

Extracts structured data from the result:

Example:

matchy_entry_s entry = {0};
if (matchy_result_get_entry(result, &entry) == MATCHY_SUCCESS) {
    // Entry contains structured data
    // See Data Types Reference for details
}

Extract Entry Data

int32_t matchy_aget_value(const matchy_entry_s *entry,
                          matchy_entry_data_t *data,
                          const char *const *path);

Navigates structured data:

Example:

matchy_entry_s entry = {0};
matchy_result_get_entry(result, &entry);

const char *path[] = {"metadata", "country", NULL};
matchy_entry_data_t data = {0};

if (matchy_aget_value(&entry, &data, path) == MATCHY_SUCCESS) {
    if (data.type == MATCHY_DATA_TYPE_UTF8_STRING) {
        printf("Country: %s\n", data.value.utf8_string);
    }
}

Complete Examples

Single Query

#include <matchy/matchy.h>
#include <stdio.h>

int main(void) {
    // Open database
    matchy_t *db = matchy_open("database.mxy");
    if (!db) {
        fprintf(stderr, "Failed to open database\n");
        return 1;
    }
    
    // Query
    matchy_result_t *result = NULL;
    int32_t err = matchy_lookup(db, "192.0.2.1", &result);
    
    if (err != MATCHY_SUCCESS) {
        fprintf(stderr, "Query failed: %d\n", err);
        matchy_close(db);
        return 1;
    }
    
    if (result) {
        printf("Match found!\n");
        matchy_free_result(result);
    } else {
        printf("No match\n");
    }
    
    matchy_close(db);
    return 0;
}

Batch Queries

void batch_query(matchy_t *db, const char **queries, size_t count) {
    for (size_t i = 0; i < count; i++) {
        matchy_result_t *result = NULL;
        
        if (matchy_lookup(db, queries[i], &result) == MATCHY_SUCCESS) {
            if (result) {
                printf("%s: MATCH\n", queries[i]);
                matchy_free_result(result);
            } else {
                printf("%s: no match\n", queries[i]);
            }
        }
    }
}

Multi-threaded Queries

#include <pthread.h>

struct query_args {
    matchy_t *db;
    const char *query;
};

void *query_thread(void *arg) {
    struct query_args *args = arg;
    matchy_result_t *result = NULL;
    
    if (matchy_lookup(args->db, args->query, &result) == MATCHY_SUCCESS) {
        if (result) {
            printf("[%ld] Match: %s\n", 
                   (long)pthread_self(), args->query);
            matchy_free_result(result);
        }
    }
    
    return NULL;
}

int main(void) {
    matchy_t *db = matchy_open("database.mxy");
    if (!db) return 1;
    
    pthread_t threads[4];
    struct query_args args[4] = {
        {db, "192.0.2.1"},
        {db, "10.0.0.1"},
        {db, "example.com"},
        {db, "*.test.com"}
    };
    
    // Spawn threads (safe: db is thread-safe for reads)
    for (int i = 0; i < 4; i++) {
        pthread_create(&threads[i], NULL, query_thread, &args[i]);
    }
    
    // Wait for completion
    for (int i = 0; i < 4; i++) {
        pthread_join(threads[i], NULL);
    }
    
    matchy_close(db);
    return 0;
}

Performance Tips

1. Reuse Database Handle

Slow:

for (int i = 0; i < 1000; i++) {
    matchy_t *db = matchy_open("database.mxy");
    matchy_lookup(db, queries[i], &result);
    matchy_close(db);
}

Fast:

matchy_t *db = matchy_open("database.mxy");
for (int i = 0; i < 1000; i++) {
    matchy_lookup(db, queries[i], &result);
    if (result) matchy_free_result(result);
}
matchy_close(db);

2. Use Trusted Mode for Known Databases

// 15-20% faster for databases you control
matchy_t *db = matchy_open_trusted("internal.mxy");

3. Free Results Promptly

matchy_result_t *result = NULL;
matchy_lookup(db, query, &result);

if (result) {
    // Extract what you need
    uint32_t type = matchy_result_type(result);
    
    // Free immediately
    matchy_free_result(result);
}

4. Use Direct IP Lookup

Slower:

matchy_lookup(db, "192.0.2.1", &result);  // Parses string

Faster:

struct sockaddr_in addr = /* ... */;
matchy_lookup_ip(db, (struct sockaddr *)&addr, &result);  // Direct

Error Handling

Check All Return Codes

matchy_t *db = matchy_open(filename);
if (!db) {
    fprintf(stderr, "Open failed\n");
    return 1;
}

matchy_result_t *result = NULL;
int32_t err = matchy_lookup(db, query, &result);

if (err != MATCHY_SUCCESS) {
    fprintf(stderr, "Lookup failed: %d\n", err);
    matchy_close(db);
    return 1;
}

// Check for no match
if (!result) {
    printf("No match found\n");
}

matchy_close(db);

Common Error Codes

  • MATCHY_SUCCESS (0) - Success
  • MATCHY_ERROR_INVALID_PARAM (-5) - NULL parameter
  • MATCHY_ERROR_FILE_NOT_FOUND (-1) - File doesn't exist
  • MATCHY_ERROR_INVALID_FORMAT (-2) - Corrupt database
  • MATCHY_ERROR_CORRUPT_DATA (-3) - Data integrity error

Thread Safety

Safe: Concurrent Queries

// Thread 1
matchy_lookup(db, "query1", &r1);

// Thread 2 (safe!)
matchy_lookup(db, "query2", &r2);

Unsafe: Query During Close

// Thread 1: Querying
matchy_lookup(db, query, &result);

// Thread 2: Closing (RACE CONDITION!)
matchy_close(db);

Pattern: Thread-Safe Queries

// Main thread
matchy_t *db = matchy_open("database.mxy");

// Spawn worker threads
// ... all threads use db safely ...

// Wait for all threads to finish
// ... join threads ...

// Only then close
matchy_close(db);

See Also