Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # C bindings guide | |
| 2 | |
| 3 The Mojo C bindings are a way to talk the Mojom protocol, the canonical protocol | |
| 4 for communication between Mojo programs. The library under `bindings/` provides | |
| 5 functionality for encoding, decoding and other computation, so it needs to be | |
| 6 linked together with C code generated from .mojom files. These C bindings are | |
| 7 lower-level than the C++ bindings (or any other language, for that matter), | |
| 8 are more error-prone, and require some knowledge of the C Mojo API and the | |
| 9 mojom encoding format. This document assumes the reader knows about (or knows | |
| 10 how to look up) this relevant information. Consequently, C bindings can also | |
| 11 be faster; generated bindings are smaller than the C++ equivalent, while | |
| 12 encoding and decoding is faster. The intention is to use them only when you | |
| 13 require speed and flexibility. | |
| 14 | |
| 15 ## Structs | |
| 16 | |
| 17 Let's look at what the generated code looks like for the following struct: | |
| 18 | |
| 19 ``` mojom | |
| 20 module example; | |
| 21 | |
| 22 enum Gender { MALE, FEMALE }; | |
| 23 struct Person { | |
| 24 uint32 age; | |
| 25 string name; | |
| 26 Gender gender; | |
| 27 }; | |
| 28 ``` | |
| 29 | |
| 30 A small snippet of the generated C code for the struct and enum: | |
| 31 | |
| 32 ```C | |
| 33 // Generated code for mojom enum 'example.Gender'. | |
| 34 typedef uint32_t example_Gender; | |
| 35 enum example_Gender_Enum { | |
| 36 examples_Gender_MALE = 0, | |
| 37 examples_Gender_MALE = 1, | |
| 38 }; | |
| 39 | |
| 40 // Generated code for mojom struct 'example.Person'. | |
| 41 union example_PersonPtr { | |
| 42 struct example_Person* ptr; | |
| 43 uint64_t offset; | |
| 44 }; | |
| 45 struct example_Person { | |
| 46 struct MojomStructHeader header_; | |
| 47 uint32_t age; | |
| 48 example_Gender gender; | |
| 49 union MojomStringHeaderPtr name; | |
| 50 }; | |
| 51 ``` | |
| 52 | |
| 53 The mojom wire format of a struct is comparable to the C memory model of a | |
| 54 struct, with some restrictions; in the example above, we see that the order of | |
| 55 the fields is different between the mojom and C structs, since the generated C | |
| 56 structs are in packing order, not ordinal order. Although not applicable in this | |
| 57 example, there may be additional fields inserted in the generated C struct for | |
| 58 padding purposes -- since 4-byte data types need to be 4-byte aligned, the | |
| 59 generated C bindings may include some fields not explicitly present in the | |
| 60 mojom. Since it's not immediately obvious where padding fields could be | |
| 61 inserted, it helps to examine the generated C struct to make sure what the | |
| 62 fields are, and if possible, set them using field initializers. The | |
| 63 `example_PersonPtr` union is used to represent an offset in the encoded form, or | |
| 64 a pointer in the unencoded form. | |
| 65 | |
| 66 Since mojom objects appear in depth-first order relative to their parent object, | |
| 67 we can use a `struct MojomBuffer` and calls to `MojomBuffer_Allocate(..)` to | |
| 68 linearly allocate space. The struct needs to be constructed and provided by the | |
| 69 user, and it contains 3 fields: A pointer to the buffer, size of the buffer in | |
| 70 bytes, and the byte-position of the next allocation, typically set to 0. | |
| 71 | |
| 72 For instance, to allocate space for the `name` parameter of an `example_Person`, | |
| 73 we can do so this way: | |
| 74 ```C | |
| 75 char byte_buffer[512] = {0}; | |
| 76 struct MojomBuffer buf = {byte_buffer, sizeof(byte_buffer), 0}; | |
| 77 | |
| 78 // First allocate space for the example_Person struct: | |
| 79 struct example_Person* person = | |
| 80 (struct example_Person*)MojomBuffer_Allocate(&buf, sizeof(struct example_Pers on)); | |
| 81 | |
| 82 // Allocate enough space for a 10 character string. | |
| 83 person->name.ptr = (struct MojomStringHeader*)MojomBuffer_Allocate( | |
| 84 &buf, | |
| 85 sizeof(struct MojomStringHeader) + 10); | |
| 86 ``` | |
| 87 | |
| 88 We can extract how much buffer space was used by reading `buf.num_byes_used`. | |
| 89 | |
| 90 Along with the C struct, there are some functions generated that help encode and | |
| 91 decode mojom structs, amongst other things. For the `example.Person` mojom | |
| 92 struct, the following functions are generated: | |
| 93 | |
| 94 ```c | |
| 95 struct example_Person* example_Person_DeepCopy( | |
| 96 struct MojomBuffer* in_buffer, | |
| 97 struct example_Person* in_data); | |
| 98 | |
| 99 void example_Person_EncodePointersAndHandles( | |
| 100 struct example_Person* inout_struct, uint32_t in_struct_size, | |
| 101 struct MojomHandleBuffer* inout_handle_buffer); | |
| 102 | |
| 103 void example_Person_DecodePointersAndHandles( | |
| 104 struct example_Person* inout_struct, uint32_t in_struct_size, | |
| 105 MojomHandle inout_handles[], uin32_t in_num_handles); | |
| 106 | |
| 107 MojomValidationResult example_Person_Validate( | |
| 108 const struct example_Person* in_struct, uint32_t in_struct_size, | |
| 109 uint32_t in_num_handles); | |
| 110 ``` | |
| 111 | |
| 112 The generated `example_Person_DeepCopy(..)` function is used to copy over the | |
| 113 `in_data` into another buffer, specified by `MojomBuffer`. The primary purpose | |
| 114 of this function is "linearize" a given `struct example_Person` and its | |
| 115 referenced objects into the new buffer. This essentially recursively copies all | |
| 116 objects in encoding order. The returned copy can then be encoded. | |
| 117 | |
| 118 Example usage copying a struct example_Person `person`: | |
| 119 ```c | |
| 120 ... | |
| 121 char byte_buffer[512] = {0}; | |
| 122 struct MojomBuffer buf = {byte_buffer, sizeof(byte_buffer), 0}; | |
| 123 struct example_Person* new_person = example_Person_DeepCopy(&buf, person); | |
| 124 assert(new_person != NULL); | |
| 125 ... | |
| 126 ``` | |
| 127 | |
| 128 The generated `example_Person_EncodePointersAndHandles(..)` is used to encode | |
| 129 a given C struct so that it's in wire-format, ready to send over a message pipe. | |
| 130 This encoding process involves translating pointers into relative offsets, and | |
| 131 extracting handles out of the struct into a separate handle array (and replacing | |
| 132 the handle values in the struct with references into the handle array). The | |
| 133 supplied `struct MojomHandleBuffer` needs to be constructed and provided by the | |
| 134 user and contains 3 fields: pointer to a handles array, the size of the array | |
| 135 (number of elements), and the starting offset into the array where handles can | |
| 136 be moved into (typically set to 0). | |
| 137 | |
| 138 The generated `example_Person_DecodePointersAndHandles(..)` does the inverse -- | |
| 139 it translates relative offsets into pointers, and moves handles out of the | |
| 140 handle array and into the struct (based on the encoded offset into the array). | |
| 141 In practice, before decoding a mojom struct into a usable C struct, it should be | |
| 142 first validated; this function may crash on invalid encoded data. | |
| 143 | |
| 144 The generated `example_Person_Validate(..)` validates an encoded `struct | |
| 145 example_Person`. If valid, returns `MOJOM_VALIDATION_ERROR_NONE`, and can be | |
| 146 decoded. See `bindings/validation.h` for more error codes. | |
| 147 | |
| 148 ## Interfaces | |
| 149 | |
| 150 It isn't enough to talk to other mojo applications by encoding structs and | |
| 151 referenced objects alone; communication happens via interface calls, so we need | |
| 152 to frame our structs this way. The following example describes what's generated | |
| 153 for interfaces. Consider an interface `Population` with a method `GetPerson()` | |
| 154 that returns a `Person` object given their name: | |
| 155 | |
| 156 ```mojom | |
| 157 module example; | |
| 158 | |
| 159 [ServiceName="example::EmployeeRegistry"] | |
| 160 interface EmployeeRegistry { | |
| 161 GetPerson(string name) => (Person person); | |
| 162 }; | |
| 163 ``` | |
| 164 | |
| 165 The generated code: | |
| 166 ```C | |
| 167 #define example_EmployeeRegistry__ServiceName \ | |
| 168 ((const char*)"example::EmployeeRegistry") | |
| 169 #define example_EmployeeRegistry__CurrentVersion ((uint32_t)0) | |
| 170 | |
| 171 // For message GetPerson: | |
| 172 #define example_EmployeeRegistry_GetPerson__Ordinal ((uint32_t)0) | |
| 173 #define example_EmployeeRegistry_GetPerson__MinVersion ((uint32_t)0) | |
| 174 | |
| 175 // Request struct for GetPerson(): | |
| 176 struct example_EmployeeRegistry_GetPerson_Request { | |
| 177 struct MojomStructHeader header_; | |
| 178 struct MojomStringHeaderPtr name; | |
| 179 }; | |
| 180 | |
| 181 // Response struct for GetPerson(): | |
| 182 struct example_EmployeeRegistry_GetPerson_Response { | |
| 183 struct MojomStructHeader header_; | |
| 184 struct example_PersonPtr person; | |
| 185 }; | |
| 186 ``` | |
| 187 | |
| 188 We see that the parameters (and return value) of the `GetPerson(..)` message are | |
| 189 contained within mojom structs. To send a `GetPerson(..)` request, an interface | |
| 190 request message needs to be constructed. An interface request message for | |
|
viettrungluu
2016/08/11 22:02:42
s/needs to/must/
vardhan
2016/08/12 00:15:14
Done.
| |
| 191 `GetPerson(..)` consists of the following data in the following order: | |
| 192 | |
| 193 1. `struct MojomMessageWithRequestId`. This contains: | |
| 194 - the message ordinal (generated above) which represents which message it is. | |
|
viettrungluu
2016/08/11 22:02:41
nit: 80 cols
vardhan
2016/08/12 00:15:14
oops, these are tabs instead of spaces.
| |
| 195 - flags that say if it's a request or response. | |
| 196 - a request ID, since this message is expecting a response. | |
| 197 - (see `bindings/message.h`) | |
| 198 2. `struct examples_EmployeeRegistry_GetPerson_Request`. This contains the | |
| 199 actual parameters for GetPerson(). | |
| 200 | |
| 201 Since the request parameters are just a mojom struct, all relevant functions for | |
| 202 structs are also generated (see above), e.g, ` void | |
| 203 examples_EmployeeRegistry_GetPerson_Request_EncodePointersAndHandles()`. Once | |
| 204 the request struct has been encoded, the buffer containing the above two structs | |
| 205 can be written to a message pipe. | |
| 206 | |
| 207 On the other hand, when reading message (request or response), | |
| 208 the message header must first be validated using | |
| 209 ``` | |
| 210 MojomValidationResult MojomMessage_ValidateHeader(const void* in_buf, | |
| 211 uint32_t in_buf_size); | |
| 212 ``` | |
| 213 If valid, it is safe to look at the `request_id` in `struct MojomMessage`, and | |
| 214 the `ordinal` describing the message. By checking if it's any of | |
|
viettrungluu
2016/08/11 22:02:41
The sentence "By checking ..." seems a bit strange
vardhan
2016/08/12 00:15:14
Done.
I think there need to be more things genera
| |
| 215 `example_EmployeeRegistry_*__Ordinal`, you can further validate that it is a | |
| 216 request or expects a response. See `bindings/message.h` for more functions that | |
| 217 help validate message headers. Once the message header is fully validated, you | |
| 218 must also validate the request or response mojom struct following the message | |
| 219 header using the generated `*_Validate(..)` function. | |
| 220 | |
| 221 Note that validation is run on encoded messages and structs on the wire -- | |
| 222 decoding a struct without validating it first is dangerous. | |
| 223 | |
| 224 ## Enums and Constants | |
| 225 | |
| 226 Example mojom code: | |
| 227 ``` mojom | |
| 228 module example; | |
| 229 | |
| 230 enum MyEnum { Zero, One, Four = 4, Five }; | |
| 231 const uint64 kMyConst = 34; | |
| 232 ``` | |
| 233 | |
| 234 Generated C code: | |
| 235 ``` C | |
| 236 typedef uint32_t example_MyEnum; | |
| 237 enum example_MyEnum_Enum { | |
| 238 examples_MyEnum_Zero = 0, | |
| 239 examples_MyEnum_One = 1, | |
| 240 examples_MyEnum_Four = 4, | |
| 241 examples_MyEnum_Five = 5, | |
| 242 }; | |
| 243 | |
| 244 #define example_kMyConst ((uint64_t)34) | |
| 245 ``` | |
| 246 | |
| 247 ## Tagged Unions | |
| 248 | |
| 249 Example mojom code: | |
| 250 ``` mojom | |
| 251 module example; | |
| 252 | |
| 253 union MyUnion { | |
| 254 int8 f0; | |
| 255 string f1; | |
| 256 MyUnion f2; | |
| 257 }; | |
| 258 | |
| 259 struct StructWithUnion { | |
| 260 MyUnion u; | |
| 261 } | |
| 262 ``` | |
| 263 Generated C code: | |
| 264 ```C | |
| 265 // Generated code for the Tags enum for |MyUnion|. | |
| 266 typedef uint32_t example_MyUnion_Tag; | |
| 267 enum example_MyUnion_Tag_Enum { | |
| 268 example_MyUnion_Tag_f0 = 0, | |
| 269 example_MyUnion_Tag_f1 = 1, | |
| 270 example_MyUnion_Tag_f2 = 2, | |
| 271 example_MyUnion_Tag__UNKNOWN__ = 0xFFFFFFFF, | |
| 272 }; | |
| 273 | |
| 274 // Generated code for |MyUnion|. | |
| 275 union example_MyUnionPtr { | |
| 276 struct example_MyUnion* ptr; | |
| 277 uint64_t offset; | |
| 278 }; | |
| 279 struct example_MyUnion { | |
| 280 uint32_t size; | |
| 281 example_MyUnion_Tag tag; | |
| 282 union { | |
| 283 int8_t f_f0; | |
| 284 union MojomStringHeaderPtr f_f1; | |
| 285 union example_MyUnionPtr f_f2; | |
| 286 uint64_t unknown; | |
| 287 } data; | |
| 288 }; | |
| 289 | |
| 290 // Snippet of generated code for |StructWithUnion|. | |
| 291 struct example_StructWithUnion { | |
| 292 struct MojomStructHeader header_; | |
| 293 struct example_MyUnion u; | |
| 294 }; | |
| 295 ``` | |
| 296 | |
| 297 Note that the `MyUnion` inside the `MyUnion` is a pointer object, whereas the | |
| 298 `MyUnion` inside `StructWithUnion` is inlined. The only case when unions are | |
| 299 pointer objects are when they are inside another union, otherwise they are | |
| 300 inlined. Unions are initialized by setting their size and their tag. The size is | |
| 301 always 16 bytes if the union is not null (4 for the size field, 4 for the tag, | |
| 302 and 8 for the data). The tag must be set to one defined in the generated enum of | |
| 303 tags. The unknown tag isn't meant to be encoded over the wire, and exists as an | |
|
viettrungluu
2016/08/11 22:02:42
We really should rename "unknown" to "unset" (not
vardhan
2016/08/12 00:15:14
Acknowledged.
| |
| 304 initial value for a union's tag, but the tag should be set to something else | |
|
viettrungluu
2016/08/11 22:02:41
"should be" -> "must be"
"something else" is prob
vardhan
2016/08/12 00:15:14
I think it should be "should be" because the valid
viettrungluu
2016/08/12 16:47:21
As discussed, those tests are trying to test for s
| |
| 305 before being written to wire. A union whose size is 0 is considered null. There | |
| 306 are no functions generated for unions like they are for structs, since unions | |
|
viettrungluu
2016/08/11 22:02:42
"like they are for" -> "unlike for"
| |
| 307 aren't ever encoded as a top-level data type that the programmer should have to | |
|
viettrungluu
2016/08/11 22:02:42
"aren't ever" -> "are never"
Also, "that the prog
vardhan
2016/08/12 00:15:14
I changed it to "never encoded as a top level obje
| |
| 308 write to wire. | |
| 309 | |
| 310 ## Arrays and Strings | |
| 311 | |
| 312 Arrays and strings (which are just arrays of characters) are not top-level data | |
| 313 types; they can only be defined within a struct, union or interface method. | |
| 314 Arrays inside structs are pointers to an array object. The array object's byte | |
| 315 layout is as follow: | |
| 316 1. `struct MojomArrayHeader`. This contains: | |
| 317 - Number of bytes in the array (this includes the header and the data | |
| 318 following the array header; see `2.`) | |
| 319 - Number of elements in the array. | |
| 320 - (see `bindings/array.h` for more details) | |
| 321 2. The contents of the array (the size of this is accounted for in the number | |
| 322 of bytes specified in the array header). | |
| 323 | |
| 324 Note that if the array contains pointer objects (structs, arrays, maps), the | |
| 325 array contains only the 8-byte pointers (or offsets in its encoded form) -- the | |
| 326 objects' data follow the array contents, and their size is not accounted for in | |
| 327 the array header. | |
| 328 | |
| 329 Example of how to allocate and initialize a new array of 5 int32s, and set each | |
| 330 one: | |
| 331 ```C | |
| 332 ... | |
| 333 struct MojomArrayHeader* int32_array = MojomArray_New(&buf, 5, sizeof(int32_t)); | |
| 334 *MOJOM_ARRAY_INDEX(int32_array, int32_t, 0) = 10; | |
| 335 *MOJOM_ARRAY_INDEX(int32_array, int32_t, 1) = 20; | |
| 336 *MOJOM_ARRAY_INDEX(int32_array, int32_t, 2) = 30; | |
| 337 *MOJOM_ARRAY_INDEX(int32_array, int32_t, 3) = 40; | |
| 338 *MOJOM_ARRAY_INDEX(int32_array, int32_t, 4) = 50; | |
| 339 ``` | |
| 340 | |
| 341 Here, `MojomArray_New(..)` allocates space for the buffer and initializes the | |
| 342 header, while the `MOJOM_ARRAY_INDEX(.., i)` macro returns the address of the | |
| 343 `i`th element. | |
| 344 | |
| 345 TODO(vardhan): Explain how to make an array of bools. | |
| 346 | |
| 347 Since a mojom string is an array of UTF-8 encoded characters, you can use | |
| 348 `MojomArray_New(&buf, NUM_CHARACTERS, sizeof(uint8_t))` if they are ASCII | |
| 349 characters. Otherwise, since UTF-8 characters may be variable-sized, you must be | |
| 350 careful to set the number of characters appropriately, as it may not be the same | |
| 351 as the number of bytes (minus the header). By convention, mojom strings are not | |
| 352 null-terminated. | |
| 353 | |
| 354 ## Maps | |
| 355 | |
| 356 Maps on the wire are mojom structs with two arrays; one for the keys, and one | |
| 357 for the values. The `i`th element in the keys array corresponds to the `i`th | |
| 358 element in the values array. As such, both arrays must have the same number | |
| 359 of elements, and neither arrays can be null. | |
|
viettrungluu
2016/08/11 22:02:41
s/arrays/array/ (or just write it as "neither may
vardhan
2016/08/12 00:15:14
Done.
| |
| 360 | |
| 361 # Numbers on generated bindings. | |
| 362 | |
| 363 TODO(vardhan): Probably as a separate doc? | |
| OLD | NEW |