Csilk 0.2.1
A lightweight, high-performance C HTTP web framework
Loading...
Searching...
No Matches
reflect.c File Reference

Reflection and JSON binding implementation. More...

#include "csilk/reflection/reflect.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>
Include dependency graph for reflect.c:

Macros

#define MAX_REG_STRUCTS   256
 

Functions

void csilk_reflect_init (void)
 Initialize the reflection system (called once at startup).
 
static void registry_lock (void)
 Internal: acquire the global reflection registry mutex.
 
static void registry_unlock (void)
 Internal: unlock the global reflection registry mutex.
 
void csilk_reflect_register (const char *name, const csilk_field_desc_t *fields, size_t count)
 Register a struct type with the reflection engine.
 
const csilk_reflect_entry_tcsilk_reflect_find (const char *name)
 Look up a registered type descriptor by name.
 
void csilk_reflect_foreach (csilk_reflect_foreach_cb cb, void *user_data)
 Iterate over all registered reflection types and invoke a callback for each.
 
static cJSON * serialize_scalar (const void *addr, const csilk_field_desc_t *desc)
 Internal: serialize a single struct field value to a cJSON node.
 
static void struct_to_cjson_internal (cJSON *obj, const void *struct_ptr, const csilk_field_desc_t *descs, size_t field_count)
 Internal: walk all fields of a struct and build a cJSON object.
 
static void deserialize_scalar (const cJSON *item, void *addr, const csilk_field_desc_t *desc)
 Internal: deserialize a cJSON value into a single struct field.
 
static void cjson_to_struct_internal (const cJSON *obj, void *struct_ptr, const csilk_field_desc_t *descs, size_t field_count)
 Internal: walk a cJSON object and populate a struct's fields.
 
static int get_basic_type (const char *type_name, csilk_field_desc_t *out_desc)
 
char * csilk_json_marshal (const char *type_name, const void *ptr)
 Serialize a registered struct or basic type to a compact JSON string.
 
int csilk_json_unmarshal (const char *type_name, const char *json_str, void *ptr)
 Deserialize a JSON string into a registered struct or basic type instance.
 

Variables

static csilk_reflect_entry_t g_registry [MAX_REG_STRUCTS]
 
static size_t g_registry_count = 0
 
static uv_mutex_t g_registry_mutex
 
static int g_registry_mutex_init = 0
 

Detailed Description

Reflection and JSON binding implementation.

Architecture: The reflection system provides runtime type introspection via a global registry of struct descriptors. Each registered type stores an array of csilk_field_desc_t entries with field name, type, offset, size, and metadata (is_pointer, array_length, nested_type_name). The registry is a fixed-size array (256 entries) protected by a mutex for thread safety.

JSON marshalling (struct → JSON, csilk_json_marshal): Walk each field descriptor, compute the field's address by adding the compile-time offset to the struct base pointer, and serialize the value to a cJSON node. Nested structs trigger recursive serialization via a registry lookup of the nested type. Arrays serialize each element by stride (field->size) into a cJSON array.

JSON unmarshalling (JSON → struct, csilk_json_unmarshal): Parse the JSON string with cJSON_Parse, then for each field descriptor, look up the matching JSON key and deserialize the cJSON value into the field's memory address. String fields support both fixed-size buffers (strncpy) and heap-allocated pointers (malloc + copy). Nested structs are recursively deserialized with optional auto-allocation for pointer fields.

Both marshal and unmarshal have a fast path for basic scalar types (int, float, bool, string) — if type_name matches a known primitive, serialization bypasses the registry entirely.

Thread safety: All public functions lock the registry mutex for read/write access. csilk_reflect_foreach() uses two-phase iteration (collect names under lock, invoke callbacks outside lock) to avoid deadlock when callbacks re-enter the reflection API (e.g., add_schema() in swagger.c).

Macro Definition Documentation

◆ MAX_REG_STRUCTS

#define MAX_REG_STRUCTS   256

Function Documentation

◆ cjson_to_struct_internal()

static void cjson_to_struct_internal ( const cJSON *  obj,
void *  struct_ptr,
const csilk_field_desc_t *  descs,
size_t  field_count 
)
static

Internal: walk a cJSON object and populate a struct's fields.

For each field descriptor, looks up the matching JSON key in the cJSON object (case-sensitive using cJSON_GetObjectItemCaseSensitive). Array fields limit iteration to min(json_array_size, array_length). Non-matching keys are silently ignored.

Parameters
objSource cJSON object.
struct_ptrPointer to the target struct.
descsArray of field descriptors.
field_countNumber of field descriptors.

◆ csilk_json_marshal()

char * csilk_json_marshal ( const char *  type_name,
const void *  ptr 
)

Serialize a registered struct or basic type to a compact JSON string.

Serialise a reflected struct to a JSON string.

◆ csilk_json_unmarshal()

int csilk_json_unmarshal ( const char *  type_name,
const char *  json_str,
void *  ptr 
)

Deserialize a JSON string into a registered struct or basic type instance.

Deserialise a JSON string into a reflected struct.

◆ csilk_reflect_find()

const csilk_reflect_entry_t * csilk_reflect_find ( const char *  name)

Look up a registered type descriptor by name.

Look up a registered type by name.

Searches the global registry for a type matching name.

Parameters
nameType name to find (case-sensitive).
Returns
Pointer to the type's reflection entry, or NULL if not found.
Note
Thread-safe. The returned pointer is valid for the lifetime of the registration.

◆ csilk_reflect_foreach()

void csilk_reflect_foreach ( csilk_reflect_foreach_cb  cb,
void *  user_data 
)

Iterate over all registered reflection types and invoke a callback for each.

Iterate over all registered reflection types.

Collects type names into a temporary array while holding the registry lock, then releases the lock and invokes the callback for each name. This two-phase approach avoids deadlocks when the callback itself calls back into reflection APIs (e.g., csilk_reflect_find()).

Parameters
cbCallback invoked once per registered type.
user_dataOpaque pointer passed through to the callback.
Note
Thread-safe. The callback receives a const pointer to the entry, but this pointer should not be stored beyond the callback invocation.

◆ csilk_reflect_init()

void csilk_reflect_init ( void  )

Initialize the reflection system (called once at startup).

Initialise the reflection subsystem.

Creates the global registry mutex. Idempotent — safe to call multiple times. Automatically called by csilk_server_new() and other entry points.

Note
The reflection registry is a global array of up to MAX_REG_STRUCTS (256) entries protected by a mutex. All public reflection functions are thread-safe.

◆ csilk_reflect_register()

void csilk_reflect_register ( const char *  name,
const csilk_field_desc_t *  fields,
size_t  count 
)

Register a struct type with the reflection engine.

Manually register a struct type for reflection.

Adds a type name and its field descriptors to the global registry. Once registered, the type can be serialized/deserialized to/from JSON via csilk_json_marshal() / csilk_json_unmarshal(). The CSILK_REGISTER_REFLECT() macro generates the field array and calls this function automatically.

Parameters
nameType name string (e.g., "my_request_t"). Must remain valid for the lifetime of the registration.
fieldsArray of csilk_field_desc_t describing each struct field.
countNumber of fields in the array.
Note
Thread-safe. If the registry is full (256 types), the registration is silently dropped. Types registered first take precedence.

◆ deserialize_scalar()

static void deserialize_scalar ( const cJSON *  item,
void *  addr,
const csilk_field_desc_t *  desc 
)
static

Internal: deserialize a cJSON value into a single struct field.

Maps cJSON types back to C primitives based on the field descriptor. For CSILK_TYPE_STRING, handles both fixed-size buffers (strncpy) and pointer fields (malloc + copy). For CSILK_TYPE_STRUCT, recursively calls cjson_to_struct_internal(). Null JSON values or missing items cause the field to be skipped (left at its current value).

Parameters
itemSource cJSON node (may be NULL or Null).
addrMemory address of the target field.
descField descriptor with type, size, and pointer flag.
Note
For pointer string fields, any existing allocation is freed before the new value is assigned.

◆ get_basic_type()

static int get_basic_type ( const char *  type_name,
csilk_field_desc_t *  out_desc 
)
static

◆ registry_lock()

static void registry_lock ( void  )
static

Internal: acquire the global reflection registry mutex.

Initializes the mutex on first call if not yet initialized. Blocks until the lock is acquired.

◆ registry_unlock()

static void registry_unlock ( void  )
static

Internal: unlock the global reflection registry mutex.

Must be called after registry_lock() to release the lock around the registered types table.

◆ serialize_scalar()

static cJSON * serialize_scalar ( const void *  addr,
const csilk_field_desc_t *  desc 
)
static

Internal: serialize a single struct field value to a cJSON node.

Maps C primitive types and nested structs to cJSON values based on the field descriptor. Supports int8/16/32/64, uint8/16/32/64, float, double, bool, string (fixed-size buffer or pointer), and nested struct types. For nested structs, recursively calls struct_to_cjson_internal().

Parameters
addrMemory address of the field within the source struct.
descField descriptor specifying type, offset, and metadata.
Returns
cJSON node (owned by caller), or cJSON_CreateNull() on failure.
Note
The caller must free the returned cJSON node with cJSON_Delete().

◆ struct_to_cjson_internal()

static void struct_to_cjson_internal ( cJSON *  obj,
const void *  struct_ptr,
const csilk_field_desc_t *  descs,
size_t  field_count 
)
static

Internal: walk all fields of a struct and build a cJSON object.

Iterates over the field descriptors, computes each field's address by adding the offset to the struct pointer, and serializes each field to a cJSON node added to the object. Array fields are serialized as cJSON arrays (one element per array slot). Non-array fields use the field's json_key as the object key.

Parameters
objTarget cJSON object to populate.
struct_ptrPointer to the source struct (must not be NULL).
descsArray of field descriptors.
field_countNumber of field descriptors.

Variable Documentation

◆ g_registry

◆ g_registry_count

size_t g_registry_count = 0
static

◆ g_registry_mutex

uv_mutex_t g_registry_mutex
static

◆ g_registry_mutex_init

int g_registry_mutex_init = 0
static