xref: /freebsd/contrib/libcbor/examples/crash_course.c (revision b5b9517bfe394e55088f5a05882eabae7e9b7b29)
1*b5b9517bSEd Maste /*
2*b5b9517bSEd Maste  * Copyright (c) 2014-2020 Pavel Kalvoda <me@pavelkalvoda.com>
3*b5b9517bSEd Maste  *
4*b5b9517bSEd Maste  * libcbor is free software; you can redistribute it and/or modify
5*b5b9517bSEd Maste  * it under the terms of the MIT license. See LICENSE for details.
6*b5b9517bSEd Maste  */
7*b5b9517bSEd Maste 
8*b5b9517bSEd Maste #include <stdbool.h>
9*b5b9517bSEd Maste #include <stdio.h>
10*b5b9517bSEd Maste #include <string.h>
11*b5b9517bSEd Maste #include "cbor.h"
12*b5b9517bSEd Maste 
13*b5b9517bSEd Maste // Part 1: Begin
14*b5b9517bSEd Maste void item_examples() {
15*b5b9517bSEd Maste   // A cbor_item_t can contain any CBOR data type
16*b5b9517bSEd Maste   cbor_item_t* float_item = cbor_build_float4(3.14f);
17*b5b9517bSEd Maste   cbor_item_t* string_item = cbor_build_string("Hello World!");
18*b5b9517bSEd Maste   cbor_item_t* array_item = cbor_new_indefinite_array();
19*b5b9517bSEd Maste 
20*b5b9517bSEd Maste   // They can be inspected
21*b5b9517bSEd Maste   assert(cbor_is_float(float_item));
22*b5b9517bSEd Maste   assert(cbor_typeof(string_item) == CBOR_TYPE_STRING);
23*b5b9517bSEd Maste   assert(cbor_array_is_indefinite(array_item));
24*b5b9517bSEd Maste   assert(cbor_array_size(array_item) == 0);
25*b5b9517bSEd Maste 
26*b5b9517bSEd Maste   // The data can be accessed
27*b5b9517bSEd Maste   assert(cbor_float_get_float4(float_item) == 3.14f);
28*b5b9517bSEd Maste   assert(memcmp(cbor_string_handle(string_item), "Hello World!",
29*b5b9517bSEd Maste                 cbor_string_length(string_item)) == 0);
30*b5b9517bSEd Maste 
31*b5b9517bSEd Maste   // And they can be modified
32*b5b9517bSEd Maste   assert(cbor_array_push(array_item, float_item));
33*b5b9517bSEd Maste   assert(cbor_array_push(array_item, string_item));
34*b5b9517bSEd Maste   assert(cbor_array_size(array_item) == 2);
35*b5b9517bSEd Maste 
36*b5b9517bSEd Maste   // At the end of their lifetime, items must be freed
37*b5b9517bSEd Maste   cbor_decref(&float_item);
38*b5b9517bSEd Maste   cbor_decref(&string_item);
39*b5b9517bSEd Maste   cbor_decref(&array_item);
40*b5b9517bSEd Maste }
41*b5b9517bSEd Maste // Part 1: End
42*b5b9517bSEd Maste 
43*b5b9517bSEd Maste // Part 2: Begin
44*b5b9517bSEd Maste void encode_decode() {
45*b5b9517bSEd Maste   cbor_item_t* item = cbor_build_uint8(42);
46*b5b9517bSEd Maste 
47*b5b9517bSEd Maste   // Serialize the item to a buffer (it will be allocated by libcbor)
48*b5b9517bSEd Maste   unsigned char* buffer;
49*b5b9517bSEd Maste   size_t buffer_size;
50*b5b9517bSEd Maste   cbor_serialize_alloc(item, &buffer, &buffer_size);
51*b5b9517bSEd Maste   assert(buffer_size == 2);
52*b5b9517bSEd Maste   assert(buffer[0] == 0x18);  // Encoding byte for uint8
53*b5b9517bSEd Maste   assert(buffer[1] == 42);    // The value itself
54*b5b9517bSEd Maste 
55*b5b9517bSEd Maste   // And deserialize bytes back to an item
56*b5b9517bSEd Maste   struct cbor_load_result result;
57*b5b9517bSEd Maste   cbor_item_t* decoded_item = cbor_load(buffer, buffer_size, &result);
58*b5b9517bSEd Maste   assert(result.error.code == CBOR_ERR_NONE);
59*b5b9517bSEd Maste   assert(cbor_isa_uint(decoded_item));
60*b5b9517bSEd Maste   assert(cbor_get_uint8(decoded_item) == 42);
61*b5b9517bSEd Maste 
62*b5b9517bSEd Maste   // Free the allocated buffer and items
63*b5b9517bSEd Maste   free(buffer);
64*b5b9517bSEd Maste   cbor_decref(&decoded_item);
65*b5b9517bSEd Maste   cbor_decref(&item);
66*b5b9517bSEd Maste }
67*b5b9517bSEd Maste // Part 2: End
68*b5b9517bSEd Maste 
69*b5b9517bSEd Maste // Part 3: Begin
70*b5b9517bSEd Maste void reference_counting() {
71*b5b9517bSEd Maste   // cbor_item_t is a reference counted pointer under the hood
72*b5b9517bSEd Maste   cbor_item_t* item = cbor_build_uint8(42);
73*b5b9517bSEd Maste 
74*b5b9517bSEd Maste   // Reference count starts at 1
75*b5b9517bSEd Maste   assert(cbor_refcount(item) == 1);
76*b5b9517bSEd Maste 
77*b5b9517bSEd Maste   // Most operations have reference semantics
78*b5b9517bSEd Maste   cbor_item_t* array_item = cbor_new_definite_array(1);
79*b5b9517bSEd Maste   assert(cbor_array_push(array_item, item));
80*b5b9517bSEd Maste   assert(cbor_refcount(item) == 2);  // item and array_item reference it
81*b5b9517bSEd Maste   cbor_item_t* first_array_element = cbor_array_get(array_item, 0);
82*b5b9517bSEd Maste   assert(first_array_element == item);  // same item under the hood
83*b5b9517bSEd Maste   assert(cbor_refcount(item) ==
84*b5b9517bSEd Maste          3);  // and now first_array_element also points to it
85*b5b9517bSEd Maste 
86*b5b9517bSEd Maste   // To release the reference, use cbor_decref
87*b5b9517bSEd Maste   cbor_decref(&first_array_element);
88*b5b9517bSEd Maste 
89*b5b9517bSEd Maste   // When reference count reaches 0, the item is freed
90*b5b9517bSEd Maste   assert(cbor_refcount(array_item) == 1);
91*b5b9517bSEd Maste   cbor_decref(&array_item);
92*b5b9517bSEd Maste   assert(array_item == NULL);
93*b5b9517bSEd Maste   assert(cbor_refcount(item) == 1);
94*b5b9517bSEd Maste 
95*b5b9517bSEd Maste   // Be careful, loops leak memory!
96*b5b9517bSEd Maste 
97*b5b9517bSEd Maste   // Deep copy copies the whole item tree
98*b5b9517bSEd Maste   cbor_item_t* item_copy = cbor_copy(item);
99*b5b9517bSEd Maste   assert(cbor_refcount(item) == 1);
100*b5b9517bSEd Maste   assert(cbor_refcount(item_copy) == 1);
101*b5b9517bSEd Maste   assert(item_copy != item);
102*b5b9517bSEd Maste   cbor_decref(&item);
103*b5b9517bSEd Maste   cbor_decref(&item_copy);
104*b5b9517bSEd Maste }
105*b5b9517bSEd Maste // Part 3: End
106*b5b9517bSEd Maste 
107*b5b9517bSEd Maste // Part 4: Begin
108*b5b9517bSEd Maste void moving_values() {
109*b5b9517bSEd Maste   {
110*b5b9517bSEd Maste     // Move the "42" into an array.
111*b5b9517bSEd Maste     cbor_item_t* array_item = cbor_new_definite_array(1);
112*b5b9517bSEd Maste     // The line below leaks memory!
113*b5b9517bSEd Maste     assert(cbor_array_push(array_item, cbor_build_uint8(42)));
114*b5b9517bSEd Maste     cbor_item_t* first_array_element = cbor_array_get(array_item, 0);
115*b5b9517bSEd Maste     assert(cbor_refcount(first_array_element) == 3);  // Should be 2!
116*b5b9517bSEd Maste     cbor_decref(&first_array_element);
117*b5b9517bSEd Maste     cbor_decref(&array_item);
118*b5b9517bSEd Maste     assert(cbor_refcount(first_array_element) == 1);  // Shouldn't exist!
119*b5b9517bSEd Maste     // Clean up
120*b5b9517bSEd Maste     cbor_decref(&first_array_element);
121*b5b9517bSEd Maste   }
122*b5b9517bSEd Maste 
123*b5b9517bSEd Maste   {
124*b5b9517bSEd Maste     // A correct way to move values is to decref them in the caller scope.
125*b5b9517bSEd Maste     cbor_item_t* array_item = cbor_new_definite_array(1);
126*b5b9517bSEd Maste     cbor_item_t* item = cbor_build_uint8(42);
127*b5b9517bSEd Maste     assert(cbor_array_push(array_item, item));
128*b5b9517bSEd Maste     assert(cbor_refcount(item) == 2);
129*b5b9517bSEd Maste     // "Give up" the item
130*b5b9517bSEd Maste     cbor_decref(&item);
131*b5b9517bSEd Maste     cbor_decref(&array_item);
132*b5b9517bSEd Maste     // item is a dangling pointer at this point
133*b5b9517bSEd Maste   }
134*b5b9517bSEd Maste 
135*b5b9517bSEd Maste   {
136*b5b9517bSEd Maste     // cbor_move avoids the need to decref and the dangling pointer
137*b5b9517bSEd Maste     cbor_item_t* array_item = cbor_new_definite_array(1);
138*b5b9517bSEd Maste     assert(cbor_array_push(array_item, cbor_move(cbor_build_uint8(42))));
139*b5b9517bSEd Maste     cbor_item_t* first_array_element = cbor_array_get(array_item, 0);
140*b5b9517bSEd Maste     assert(cbor_refcount(first_array_element) == 2);
141*b5b9517bSEd Maste     cbor_decref(&first_array_element);
142*b5b9517bSEd Maste     cbor_decref(&array_item);
143*b5b9517bSEd Maste   }
144*b5b9517bSEd Maste }
145*b5b9517bSEd Maste // Part 4: End
146*b5b9517bSEd Maste 
147*b5b9517bSEd Maste // Part 5: Begin
148*b5b9517bSEd Maste // Refcount can be managed in conjunction with ownership
149*b5b9517bSEd Maste static cbor_item_t* global_item = NULL;
150*b5b9517bSEd Maste 
151*b5b9517bSEd Maste // This function takes shared ownership of the item
152*b5b9517bSEd Maste void borrow_item(cbor_item_t* item) {
153*b5b9517bSEd Maste   global_item = item;
154*b5b9517bSEd Maste   // Mark the extra reference
155*b5b9517bSEd Maste   cbor_incref(item);
156*b5b9517bSEd Maste }
157*b5b9517bSEd Maste 
158*b5b9517bSEd Maste void return_item() {
159*b5b9517bSEd Maste   cbor_decref(&global_item);
160*b5b9517bSEd Maste   global_item = NULL;
161*b5b9517bSEd Maste }
162*b5b9517bSEd Maste 
163*b5b9517bSEd Maste void reference_ownership() {
164*b5b9517bSEd Maste   cbor_item_t* item = cbor_build_uint8(42);
165*b5b9517bSEd Maste 
166*b5b9517bSEd Maste   // Lend the item
167*b5b9517bSEd Maste   borrow_item(item);
168*b5b9517bSEd Maste   assert(cbor_refcount(item) == 2);
169*b5b9517bSEd Maste   cbor_decref(&item);
170*b5b9517bSEd Maste 
171*b5b9517bSEd Maste   // Release the shared ownership. return_item will deallocate the item.
172*b5b9517bSEd Maste   return_item();
173*b5b9517bSEd Maste }
174*b5b9517bSEd Maste // Part 5: End
175*b5b9517bSEd Maste 
176*b5b9517bSEd Maste int main(void) {
177*b5b9517bSEd Maste   item_examples();
178*b5b9517bSEd Maste   encode_decode();
179*b5b9517bSEd Maste   reference_counting();
180*b5b9517bSEd Maste   moving_values();
181*b5b9517bSEd Maste   reference_ownership();
182*b5b9517bSEd Maste   return 0;
183*b5b9517bSEd Maste }
184