blob: 0d2d6f3f1a1ce246f9c0d060b25854aad27258f3 [file] [log] [blame] [view] [raw]
---
title: facil.io - a Dynamic Type System using facil.io objects.
toc: true
layout: api
---
# Dynamic Type System: facil.io objects (`FIOBJ`)
In this page you will find a general overview. For detailed API information please visit the Core or Type pages.
## Overview
`facil.io` offers a dynamic type system that makes it a breeze to mix object types together.
This dynamic type system is an independent module within the `facil.io` core and can be used separately.
The `FIOBJ` type API is divided by it's inner types (tested using `FIOBJ_TYPE(obj)` or `FIOBJ_TYPE_IS(obj, type)`):
* [Core and Generic API](fiobj_core)
* [Primitive Types](fiobj_primitives)
* [Number / Float](fiobj_numbers)
* [String](fiobj_str)
* [Array](fiobj_ary)
* [Hash](fiobj_hash)
* [Data](fiobj_data)
* [JSON](fiobj_json)
### Why we need dynamic types?
C doesn't lend itself easily to the dynamic types that are often used in languages such as Javascript. This makes it harder to use an optimized C backend (server) when the frontend (client / browser) expects multi-type responses such as JSON objects.
To resolve this difference in expectations, `facil.io` offers the `FIOBJ` type system.
This is an opaque type that can be tested using `FIOBJ_TYPE(obj)` or `FIOBJ_TYPE_IS(obj, type)`.
This offers the following advantages (among others):
* Saves you precious development time.
* Allows deep integration with `facil.io` services, reducing the need to translate from one type to another.
* Allows for "typeless" actions, such as collection iteration (`fiobj_each2`), simple conversion (`fiobj_obj2num` and `fiobj_obj2cstr`), deallocation (`fiobj_free`). reference counting (`fiobj_dup`) and equality checks (`fiobj_iseq`).
* Offers JSON parsing and formatting to and from `FIOBJ`.
* Offers non-recursive iteration.
## API Considerations
This is a short summery regarding the API and it's use. The `fiobj_*` API is well documented in the header files, so only main guidelines are mentioned.
### Functional Access
All object access should be functional, or using the macros provided. Although this requirement can be circumvented, using the functional interface should be preferred.
For example:
```c
/* this will work */
FIOBJ str = fiobj_str_buf(6); /* automatically adds room for the NUL terminator */
fio_cstr_s raw_str = fiobj_obj2cstr(str);
memcpy(raw_str.buffer, "Hello!", 6);
fiobj_str_resize(str, 6);
// ...
fiobj_free(str);
/* this is better */
FIOBJ str = fiobj_str_buf(6);
fiobj_str_write(str, "Hello!", 6);
// ...
fiobj_free(str);
/* for simple strings, one line will do */
FIOBJ str = fiobj_str_new("Hello!", 6);
// ...
fiobj_free(str);
/* for more complex cases, printf style is supported */
FIOBJ str = fiobj_strprintf("%s %d" , "Hello!", 42)
// ...
fiobj_free(str);
/* or */
FIOBJ str = fiobj_str_buf(0);
fiobj_str_write2(str, "%s %d" , "Hello!", 42);
// ...
fiobj_free(str);
/* for static strings, this is the best */
FIOBJ str = fiobj_str_static("Hello!", 6);
// ...
fiobj_free(str);
```
### Ownership Follows Nesting
An object's memory should *always* be managed by it's "owner". This usually means the calling function.
*However*, when an object is nested within another object (i.e., placed in an Array or set as the *value* for a Hash or an HTTP header), **the ownership of the object is transferred**.
In the following example, the String nested within the Array is freed when the Array is freed:
```c
FIOBJ ary = fiobj_ary_new();
FIOBJ str = fiobj_str_new("Hello!", 6);
fiobj_ary_push(ary, str);
// ...
fiobj_free(ary);
```
Hashes follow the same rule. However...
It's important to note that Hash keys aren't transferred to the Hash (they are used to access and store data, but they are not the data itself).
When calling `fiobj_hash_set`, we are storing a *value* in the Hash, the *key* is what we use to access that value. This is why **the key's ownership remains with the calling function**. i.e.:
```c
FIOBJ h = fiobj_hash_new();
static __thread FIOBJ ID = NULL;
if(!ID)
ID = fiobj_str_new("id", 2);
/* By placing the Number in the Hash, it will be deallocated together with the Hash */
fiobj_hash_set(h, ID, fiobj_num_new(42));
// ...
fiobj_free(h); /* Although we free the Hash, the ID remains in the memory */
if(0) {
// I assume ID will be reused, but if it's temporary, we need to free it
fiobj_free(ID);
ID = NULL;
}
```
### Passing By Reference
All objects are passed along by reference. The `dup` (duplication) process simply increases the reference count.
This is a very powerful tool. In the following example, `str2` is a "copy" **by reference** of `str`. By editing `str2` we're also editing `str`:
```c
FIOBJ str = fiobj_str_new("Hello!", 6);
FIOBJ str2 = fiobj_dup(str);
/* We'll edit str2 to say "Hello There!" instead of "Hello!" */
fiobj_str_resize(str2, 5);
fiobj_str_write(str2, " There!", 7);
/* This prints "Hello There!" because str was edited by reference! */
printf("%s\n", fiobj_obj2cstr(str).data);
/* we need to free both references to free the memory */
fiobj_free(str);
fiobj_free(str2);
```
An independent copy can be created using an object's specific copy function. This example create a new, independent, object instead of referencing the old one:
```c
FIOBJ str = fiobj_str_new("Hello!", 6);
/* create a copy instead of a reference */
FIOBJ str2 = fiobj_str_copy(str);
/* this is the same as */
FIOBJ str3 = fiobj_str_new(fiobj_obj2cstr(str).data, fiobj_obj2cstr(str).len);
// ...
fiobj_free(str);
fiobj_free(str2);
fiobj_free(str3);
```
Copy by reference produces a deep reference adjustment, so Arrays and Hashes can be safely copied by reference.
```c
FIOBJ ary = fiobj_ary_new();
fiobj_ary_push(ary, fiobj_str_new("Hello!", 6));
FIOBJ ary_copy = fiobj_dup(ary);
// ...
fiobj_free(ary);
// all the items in ary2 are still accessible.
fprintf(stderr, "%s\n", fiobj_obj2cstr( fiobj_ary_index(ary_copy, -1) ).buffer );
fiobj_free(ary_copy);
```
### Cyclic Nesting Errors
Cyclic protection is unsupported mostly because of performance concerns, but also because cyclic nesting is impractical for network applications (for example, how would a cyclic object be formatted into JSON?).
Cyclic nesting should be avoided. For example, the following code will crash:
```c
FIOBJ ary = fiobj_ary_new();
FIOBJ ary2 = fiobj_ary_new();
// cyclic nesting
fiobj_ary_push(ary, ary2);
fiobj_ary_push(ary2, ary);
// free might crash or produce unexpected results
fiobj_free(ary);
// each2 will cycle forever
fiobj_each2(ary2, ...);
```
## Independence
The `FIOBJ` module is independent and can be extracted from `facil.io` by copying the `fiobj.h` file (under `lib/facil/core/types`) and all the files in the `lib/facil/core/types/fiobj` folder.
Place these files in your project and use to your heart's content.
The module is licensed under the same MIT license offered by the rest of the `facil.io` source code.