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