Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Neumenon/cowrie/llms.txt

Use this file to discover all available pages before exploring further.

C SDK

The C implementation provides a low-level, zero-dependency codec suitable for embedded systems, IoT devices, and performance-critical applications.

Installation

Using CMake

cd c
mkdir build && cd build
cmake ..
make
make install

Manual Integration

Copy the header files and source files to your project:
cp c/include/*.h your_project/include/
cp c/src/*.c your_project/src/

Basic Usage

Gen1: Lightweight Encoding

#include "cowrie_gen1.h"

// Create a value
cowrie_g1_value_t *obj = cowrie_g1_object(2);
cowrie_g1_object_set(obj, "name", cowrie_g1_string("Alice", 5));
cowrie_g1_object_set(obj, "age", cowrie_g1_int64(30));

// Encode
cowrie_g1_buf_t buf;
cowrie_g1_buf_init(&buf);
int err = cowrie_g1_encode(obj, &buf);
if (err != COWRIE_G1_OK) {
    fprintf(stderr, "Encode failed\n");
    return -1;
}

// Use buf.data and buf.len
write(fd, buf.data, buf.len);

// Cleanup
cowrie_g1_value_free(obj);
cowrie_g1_buf_free(&buf);

Gen2: Full Codec

#include "cowrie_gen2.h"

// Create value
cowrie_value_t *val = cowrie_object(2);
cowrie_object_set(val, "name", cowrie_string("Alice", 5));
cowrie_object_set(val, "age", cowrie_int64(30));

// Encode
uint8_t *data;
size_t len;
int err = cowrie_encode(val, &data, &len);
if (err != COWRIE_OK) {
    fprintf(stderr, "Encode failed\n");
    return -1;
}

// Use data
write(fd, data, len);

// Cleanup
free(data);
cowrie_value_free(val);

Type System

Creating Values (Gen1)

// Null
cowrie_g1_value_t *null_val = cowrie_g1_null();

// Boolean
cowrie_g1_value_t *bool_val = cowrie_g1_bool(true);

// Integer
cowrie_g1_value_t *int_val = cowrie_g1_int64(42);

// Float
cowrie_g1_value_t *float_val = cowrie_g1_float64(3.14);

// String (with length)
cowrie_g1_value_t *str_val = cowrie_g1_string("hello", 5);

// Bytes
uint8_t bytes[] = {0x01, 0x02, 0x03};
cowrie_g1_value_t *bytes_val = cowrie_g1_bytes(bytes, 3);

Creating Values (Gen2)

// UUID (16 bytes)
uint8_t uuid_bytes[16] = {0};
cowrie_value_t *uuid = cowrie_uuid128(uuid_bytes);

// Datetime (nanoseconds since epoch)
int64_t nanos = 1234567890123456789LL;
cowrie_value_t *dt = cowrie_datetime64(nanos);

// BigInt
uint8_t bigint_bytes[] = {0x01, 0x23, 0x45};
cowrie_value_t *big = cowrie_bigint(bigint_bytes, 3);

// Decimal128
uint8_t coef[16] = {0};
cowrie_value_t *dec = cowrie_decimal128(2, coef);  // scale=2

Reading Values

Gen1

cowrie_g1_value_t *val;
cowrie_g1_decode(data, len, &val);

switch (val->type) {
    case COWRIE_G1_TYPE_NULL:
        printf("null\n");
        break;
    
    case COWRIE_G1_TYPE_BOOL:
        printf("bool: %s\n", val->bool_val ? "true" : "false");
        break;
    
    case COWRIE_G1_TYPE_INT64:
        printf("int64: %lld\n", (long long)val->int64_val);
        break;
    
    case COWRIE_G1_TYPE_FLOAT64:
        printf("float64: %f\n", val->float64_val);
        break;
    
    case COWRIE_G1_TYPE_STRING:
        printf("string: %.*s\n", 
               (int)val->string_val.len, 
               val->string_val.data);
        break;
    
    case COWRIE_G1_TYPE_ARRAY:
        printf("array[%zu]\n", val->array_val.len);
        for (size_t i = 0; i < val->array_val.len; i++) {
            // Process val->array_val.items[i]
        }
        break;
    
    case COWRIE_G1_TYPE_OBJECT:
        printf("object{%zu}\n", val->object_val.len);
        for (size_t i = 0; i < val->object_val.len; i++) {
            const char *key = val->object_val.members[i].key;
            cowrie_g1_value_t *value = val->object_val.members[i].value;
            printf("  %s: ", key);
            // Process value
        }
        break;
}

cowrie_g1_value_free(val);

Gen2

cowrie_value_t *val;
cowrie_decode(data, len, &val);

switch (val->type) {
    case COWRIE_STRING:
        printf("%.*s\n", 
               (int)val->string_val.len,
               val->string_val.data);
        break;
    
    case COWRIE_INT64:
        printf("%lld\n", (long long)val->int64_val);
        break;
    
    case COWRIE_TENSOR:
        printf("Tensor<dtype=%d> shape=[",
               val->tensor_val.dtype);
        for (size_t i = 0; i < val->tensor_val.rank; i++) {
            printf("%llu%s", 
                   (unsigned long long)val->tensor_val.dims[i],
                   i < val->tensor_val.rank - 1 ? ", " : "");
        }
        printf("]\n");
        break;
    
    case COWRIE_ARRAY:
        for (size_t i = 0; i < val->array_val.len; i++) {
            // Process val->array_val.items[i]
        }
        break;
    
    case COWRIE_OBJECT:
        for (size_t i = 0; i < val->object_val.count; i++) {
            const char *key = val->object_val.keys[i];
            cowrie_value_t *value = val->object_val.values[i];
            printf("%s: ", key);
            // Process value
        }
        break;
}

cowrie_value_free(val);

Memory Management

Manual Memory

// Always free values when done
cowrie_g1_value_t *val = cowrie_g1_string("test", 4);
// ... use val ...
cowrie_g1_value_free(val);

// Free buffers
cowrie_g1_buf_t buf;
cowrie_g1_buf_init(&buf);
// ... use buf ...
cowrie_g1_buf_free(&buf);

Arena Allocator (Gen2)

// Create arena for batch allocations
cowrie_arena_t *arena = cowrie_arena_new(4096);  // 4KB

// Allocate values from arena
cowrie_value_t *v1 = cowrie_arena_string(arena, "test1", 5);
cowrie_value_t *v2 = cowrie_arena_string(arena, "test2", 5);

// Free entire arena at once
cowrie_arena_free(arena);

Error Handling

Gen1 Errors

int err = cowrie_g1_decode(data, len, &val);
switch (err) {
    case COWRIE_G1_OK:
        // Success
        break;
    case COWRIE_G1_ERR_NOMEM:
        fprintf(stderr, "Out of memory\n");
        break;
    case COWRIE_G1_ERR_INVALID:
        fprintf(stderr, "Invalid data\n");
        break;
    case COWRIE_G1_ERR_EOF:
        fprintf(stderr, "Unexpected end of data\n");
        break;
    case COWRIE_G1_ERR_DEPTH:
        fprintf(stderr, "Max nesting depth exceeded\n");
        break;
    default:
        fprintf(stderr, "Unknown error: %d\n", err);
}

Gen2 Errors

int err = cowrie_decode(data, len, &val);
if (err != COWRIE_OK) {
    const char *msg = cowrie_error_string(err);
    fprintf(stderr, "Decode error: %s\n", msg);
    return -1;
}

File I/O

Writing to File

#include "cowrie_gen1.h"
#include <stdio.h>

cowrie_g1_value_t *val = cowrie_g1_object(1);
cowrie_g1_object_set(val, "data", cowrie_g1_string("test", 4));

// Encode
cowrie_g1_buf_t buf;
cowrie_g1_buf_init(&buf);
cowrie_g1_encode(val, &buf);

// Write to file
FILE *f = fopen("output.cowrie", "wb");
fwrite(buf.data, 1, buf.len, f);
fclose(f);

// Cleanup
cowrie_g1_value_free(val);
cowrie_g1_buf_free(&buf);

Reading from File

#include "cowrie_gen1.h"
#include <stdio.h>
#include <stdlib.h>

// Read file
FILE *f = fopen("data.cowrie", "rb");
fseek(f, 0, SEEK_END);
size_t len = ftell(f);
fseek(f, 0, SEEK_SET);

uint8_t *data = malloc(len);
fread(data, 1, len, f);
fclose(f);

// Decode
cowrie_g1_value_t *val;
int err = cowrie_g1_decode(data, len, &val);
if (err == COWRIE_G1_OK) {
    // Process val
    cowrie_g1_value_free(val);
}

free(data);

Security Options

Gen1 Limits

// Configure security limits (optional)
struct cowrie_g1_limits {
    size_t max_depth;      // Default: 1000
    size_t max_array_len;  // Default: 100M
    size_t max_object_len; // Default: 10M
    size_t max_string_len; // Default: 500MB
    size_t max_bytes_len;  // Default: 1GB
};

cowrie_g1_limits_t limits = {
    .max_depth = 500,
    .max_array_len = 1000000,
    .max_object_len = 100000,
    .max_string_len = 10000000,
    .max_bytes_len = 100000000,
};

cowrie_g1_value_t *val;
int err = cowrie_g1_decode_with_limits(data, len, &val, &limits);

Performance Tips

  1. Reuse buffers: Initialize once, clear between uses
    cowrie_g1_buf_t buf;
    cowrie_g1_buf_init(&buf);
    for (int i = 0; i < N; i++) {
        buf.len = 0;  // Reset
        cowrie_g1_encode(values[i], &buf);
        // Use buf
    }
    cowrie_g1_buf_free(&buf);
    
  2. Use arena allocators: For batch allocations in Gen2
  3. Pre-allocate capacity: Reserve space for known sizes
    cowrie_g1_buf_reserve(&buf, 4096);
    
  4. Avoid copying: Use pointers and references when possible
  5. Profile your usage: Use tools like valgrind and gprof

Embedded Systems

Static Allocation

// Pre-allocate buffer on stack
uint8_t buffer[1024];
cowrie_g1_buf_t buf = {
    .data = buffer,
    .len = 0,
    .cap = sizeof(buffer),
};

// Encode without heap allocation
cowrie_g1_value_t val = {
    .type = COWRIE_G1_TYPE_STRING,
    .string_val = {.data = "test", .len = 4},
};

cowrie_g1_encode_static(&val, &buf);

No Standard Library

// Define custom allocators
void* my_malloc(size_t size);
void my_free(void* ptr);

// Override default allocators
cowrie_g1_set_allocator(my_malloc, my_free);

Complete Example

#include "cowrie_gen1.h"
#include <stdio.h>
#include <stdlib.h>

int main() {
    // Create data
    cowrie_g1_value_t *obj = cowrie_g1_object(3);
    cowrie_g1_object_set(obj, "name", cowrie_g1_string("Alice", 5));
    cowrie_g1_object_set(obj, "age", cowrie_g1_int64(30));
    
    // Array of scores
    double scores[] = {98.5, 87.3, 92.1};
    cowrie_g1_object_set(obj, "scores", 
                         cowrie_g1_float64_array(scores, 3));
    
    // Encode
    cowrie_g1_buf_t buf;
    cowrie_g1_buf_init(&buf);
    int err = cowrie_g1_encode(obj, &buf);
    if (err != COWRIE_G1_OK) {
        fprintf(stderr, "Encode failed: %d\n", err);
        return 1;
    }
    
    printf("Encoded size: %zu bytes\n", buf.len);
    
    // Decode
    cowrie_g1_value_t *decoded;
    err = cowrie_g1_decode(buf.data, buf.len, &decoded);
    if (err != COWRIE_G1_OK) {
        fprintf(stderr, "Decode failed: %d\n", err);
        return 1;
    }
    
    // Access data
    if (decoded->type == COWRIE_G1_TYPE_OBJECT) {
        cowrie_g1_value_t *name = cowrie_g1_object_get(decoded, "name");
        if (name && name->type == COWRIE_G1_TYPE_STRING) {
            printf("Name: %.*s\n", 
                   (int)name->string_val.len,
                   name->string_val.data);
        }
    }
    
    // Cleanup
    cowrie_g1_value_free(obj);
    cowrie_g1_value_free(decoded);
    cowrie_g1_buf_free(&buf);
    
    return 0;
}

Compilation

# Compile with optimization
gcc -O3 -o example example.c -lcowrie

# Compile for embedded (no stdlib)
gcc -nostdlib -O2 -o firmware firmware.c cowrie_gen1.c

# Cross-compile for ARM
arm-none-eabi-gcc -mcpu=cortex-m4 -o app.elf app.c -lcowrie

Build docs developers (and LLMs) love