OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 // HYPOTHETICAL IDL: | |
3 | |
4 struct Bar { | |
5 alpha @0 :uint8; | |
6 beta @1 :uint8; | |
7 gamma @2 :uint8; | |
8 }; | |
9 | |
10 struct Foo { | |
11 x @0 :int32; | |
12 y @1 :int32; | |
13 a @2 :bool; | |
14 b @3 :bool; | |
15 c @4 :bool; | |
16 bar @5 :Bar; | |
17 extra_bars @7 :array(Bar) [optional]; | |
18 data @6 :array(uint8); | |
19 }; | |
20 | |
21 interface Blah { | |
22 Frobinate @0 (foo @0 :Foo, baz @1 :bool); | |
23 }; | |
24 | |
25 */ | |
26 | |
27 #include <assert.h> | |
28 #include <stddef.h> | |
29 #include <stdint.h> | |
30 #include <stdio.h> | |
31 #include <stdlib.h> | |
32 #include <string.h> | |
33 | |
34 #include <new> | |
35 #include <vector> | |
36 | |
37 // This would be part of some header file we'd provide. | |
38 | |
39 typedef uint32_t Handle; | |
40 | |
41 struct Message { | |
42 uint32_t name; | |
43 uint8_t* data_start; | |
44 uint8_t* data_end; | |
45 Handle* handles_start; | |
46 Handle* handles_end; | |
47 }; | |
48 | |
49 struct MessageHeader { | |
50 uint32_t num_bytes; | |
51 uint32_t name; | |
52 }; | |
53 | |
54 struct StructHeader { | |
55 uint32_t num_bytes; | |
56 uint32_t num_fields; | |
57 }; | |
58 | |
59 struct ArrayHeader { | |
60 uint32_t num_bytes; | |
61 uint32_t num_elements; | |
62 }; | |
63 | |
64 template <typename T> class Array; | |
65 | |
66 template <typename T> | |
67 union StructPointer { | |
68 uint64_t offset; | |
69 T* ptr; | |
70 }; | |
71 | |
72 template <typename T> | |
73 union ArrayPointer { | |
74 uint64_t offset; | |
75 Array<T>* ptr; | |
76 }; | |
77 | |
78 inline void EncodePointer(void* address, uint64_t* offset) { | |
79 if (!address) { | |
80 *offset = 0; | |
81 return; | |
82 } | |
83 uint8_t* p_obj = reinterpret_cast<uint8_t*>(address); | |
84 uint8_t* p_slot = reinterpret_cast<uint8_t*>(offset); | |
85 assert(p_obj > p_slot); | |
86 *offset = p_obj - p_slot; | |
87 } | |
88 | |
89 template <typename T> | |
90 inline void DecodePointer(uint64_t* offset, T** ptr) { | |
91 if (!*offset) { | |
92 *ptr = NULL; | |
93 return; | |
94 } | |
95 uint8_t* p_slot = reinterpret_cast<uint8_t*>(offset); | |
96 *ptr = reinterpret_cast<T*>(p_slot + *offset); | |
piman
2013/09/25 17:26:25
Note: strict aliasing rules only allow reinterpret
| |
97 } | |
98 | |
99 template <typename T> | |
100 struct ArrayTraits { | |
101 typedef T ElementType; | |
102 | |
103 static T& ToElementRef(ElementType& e) { return e; } | |
104 static T const& ToElementConstRef(const ElementType& e) { return e; } | |
105 | |
106 template <typename Allocator> | |
107 static void CloneElements(Allocator* allocator, | |
108 ArrayHeader* header, | |
109 ElementType* elements) { | |
110 } | |
111 | |
112 static void EncodePointersAndHandles(ArrayHeader* header, | |
113 ElementType* elements, | |
114 std::vector<Handle>* handles) { | |
115 } | |
116 static bool DecodePointersAndHandles(ArrayHeader* header, | |
117 ElementType* elements, | |
118 const Message& message) { | |
119 return true; | |
120 } | |
121 }; | |
122 | |
123 template <typename P> | |
124 struct ArrayTraits<P*> { | |
125 typedef StructPointer<P> ElementType; | |
126 | |
127 static P*& ToElementRef(ElementType& e) { return e.ptr; } | |
128 static P* const& ToElementConstRef(const ElementType& e) { return e.ptr; } | |
129 | |
130 template <typename Allocator> | |
131 static void CloneElements(Allocator* allocator, | |
132 ArrayHeader* header, | |
133 ElementType* elements) { | |
134 for (uint32_t i = 0; i < header->num_elements; ++i) { | |
135 if (elements[i].ptr) | |
136 elements[i].ptr = elements[i].ptr->Clone(allocator); | |
137 } | |
138 } | |
139 | |
140 static void EncodePointersAndHandles(ArrayHeader* header, | |
141 ElementType* elements, | |
142 std::vector<Handle>* handles) { | |
143 for (uint32_t i = 0; i < header->num_elements; ++i) { | |
144 if (elements[i].ptr) | |
145 elements[i].ptr->EncodePointersAndHandles(handles); | |
146 EncodePointer(elements[i].ptr, &elements[i].offset); | |
147 } | |
148 } | |
149 static bool DecodePointersAndHandles(ArrayHeader* header, | |
150 ElementType* elements, | |
151 const Message& message) { | |
152 for (uint32_t i = 0; i < header->num_elements; ++i) { | |
153 DecodePointer(&elements[i].offset, &elements[i].ptr); | |
154 if (elements[i].ptr) | |
155 elements[i].ptr->DecodePointersAndHandles(message); | |
156 } | |
157 return true; | |
158 } | |
159 }; | |
160 | |
161 template <typename T> | |
162 class Array { | |
163 public: | |
164 explicit Array(size_t num_elements) { | |
165 // TODO: bools should get packed to a single bit? | |
166 header_.num_bytes = | |
167 sizeof(typename ArrayTraits<T>::ElementType) * num_elements + | |
168 sizeof(ArrayHeader); | |
169 header_.num_elements = num_elements; | |
170 } | |
171 | |
172 template <typename Allocator> | |
173 Array<T>* Clone(Allocator* allocator) const { | |
174 Array<T>* array = | |
175 reinterpret_cast<Array<T>*>(allocator->AllocBytes(header_.num_bytes)); | |
176 memcpy(array, this, header_.num_bytes); | |
177 | |
178 ArrayTraits<T>::CloneElements(allocator, &array->header_, array->elements_); | |
179 return array; | |
180 } | |
181 | |
182 void EncodePointersAndHandles(std::vector<Handle>* handles) { | |
183 ArrayTraits<T>::EncodePointersAndHandles(&header_, elements_, handles); | |
184 } | |
185 bool DecodePointersAndHandles(const Message& message) { | |
186 return ArrayTraits<T>::DecodePointersAndHandles(&header_, elements_, | |
187 message); | |
188 } | |
189 | |
190 size_t size() const { return header_.num_elements; } | |
191 | |
192 T& at(size_t offset) { | |
193 return ArrayTraits<T>::ToElementRef(elements_[offset]); | |
194 } | |
195 | |
196 const T& at(size_t offset) const { | |
197 return ArrayTraits<T>::ToElementConstRef(elements_[offset]); | |
198 } | |
199 | |
200 T& operator[](size_t offset) { | |
201 return ArrayTraits<T>::ToElementRef(elements_[offset]); | |
202 } | |
203 | |
204 const T& operator[](size_t offset) const { | |
205 return ArrayTraits<T>::ToElementConstRef(elements_[offset]); | |
206 } | |
207 | |
208 private: | |
209 ArrayHeader header_; | |
210 typename ArrayTraits<T>::ElementType elements_[1]; // Extra elements follow. | |
211 }; | |
212 | |
213 // The following is a cheezy arena allocator. | |
214 class Buffer { | |
215 public: | |
216 Buffer() : ptr_(NULL), size_(0) { | |
217 } | |
218 ~Buffer() { free(ptr_); } | |
219 | |
220 const uint8_t* data() const { return ptr_; } | |
221 uint8_t* data() { return ptr_; } | |
222 | |
223 size_t size() const { return size_; } | |
224 | |
225 uint8_t* AllocBytes(size_t size) { | |
226 return Grow(size); | |
227 } | |
228 | |
229 template <typename T> | |
230 T* Alloc() { | |
231 size_t size = sizeof(T); | |
232 return new (Grow(size)) T(); | |
233 } | |
234 | |
235 template <typename T> | |
236 Array<T>* AllocArray(size_t count) { | |
237 // (count - 1) because Array<T> has reserved space for the first element. | |
238 size_t size = sizeof(Array<T>) + sizeof(T) * (count - 1); | |
239 return new (Grow(size)) Array<T>(count); | |
240 } | |
241 | |
242 private: | |
243 uint8_t* Grow(size_t delta) { | |
244 // TODO: Align allocations | |
245 size_t old_size = size_; | |
246 size_t new_size = old_size + delta; | |
247 ptr_ = static_cast<uint8_t*>(realloc(ptr_, old_size + delta)); | |
248 size_ = new_size; | |
249 uint8_t* result = ptr_ + old_size; | |
250 memset(result, 0, delta); | |
251 return result; | |
252 } | |
253 | |
254 uint8_t* ptr_; | |
255 size_t size_; | |
256 | |
257 // NOT IMPLEMENTED | |
258 Buffer(const Buffer&); | |
259 void operator=(const Buffer&); | |
260 }; | |
261 | |
262 //---- | |
263 // Begin generated code. | |
264 | |
265 // What follows is class definitions corresponding to the structures indicated | |
266 // by the IDL. | |
267 | |
268 class Bar { | |
269 public: | |
270 Bar() { | |
271 header_.num_bytes = sizeof(*this) + sizeof(StructHeader); | |
272 header_.num_fields = 3; | |
273 } | |
274 | |
275 Bar* Clone(Buffer* buf) const { | |
276 Bar* bar = buf->Alloc<Bar>(); | |
277 memcpy(bar, this, sizeof(*this)); | |
278 return bar; | |
279 } | |
280 | |
281 void EncodePointersAndHandles(std::vector<Handle>* handles) { | |
282 } | |
283 bool DecodePointersAndHandles(const Message& message) { | |
284 return true; | |
285 } | |
286 | |
287 void set_alpha(uint8_t alpha) { d_.alpha = alpha; } | |
288 void set_beta(uint8_t beta) { d_.beta = beta; } | |
289 void set_gamma(uint8_t gamma) { d_.gamma = gamma; } | |
290 | |
291 uint8_t alpha() const { return d_.alpha; } | |
292 uint8_t beta() const { return d_.beta; } | |
293 uint8_t gamma() const { return d_.gamma; } | |
294 | |
295 private: | |
296 StructHeader header_; | |
Hajime Morrita
2013/09/25 20:03:55
One possible alternative is to keep the message cl
| |
297 struct { | |
298 uint8_t alpha; | |
299 uint8_t beta; | |
300 uint8_t gamma; | |
301 } d_; | |
302 | |
303 ~Bar(); // NOT IMPLEMENTED | |
304 }; | |
305 | |
306 class Foo { | |
307 public: | |
308 Foo() { | |
309 header_.num_bytes = sizeof(*this) + sizeof(StructHeader); | |
310 header_.num_fields = 8; | |
311 } | |
312 | |
313 Foo* Clone(Buffer* buf) const { | |
314 Foo* foo = buf->Alloc<Foo>(); | |
315 memcpy(foo, this, sizeof(*this)); | |
316 | |
317 foo->set_bar(foo->bar()->Clone(buf)); | |
318 foo->set_data(foo->data()->Clone(buf)); | |
319 foo->set_extra_bars(foo->extra_bars()->Clone(buf)); | |
320 | |
321 return foo; | |
322 } | |
323 | |
324 void EncodePointersAndHandles(std::vector<Handle>* handles) { | |
325 if (d_.bar.ptr) | |
326 d_.bar.ptr->EncodePointersAndHandles(handles); | |
327 EncodePointer(d_.bar.ptr, &d_.bar.offset); | |
328 | |
329 if (d_.data.ptr) | |
330 d_.data.ptr->EncodePointersAndHandles(handles); | |
331 EncodePointer(d_.data.ptr, &d_.data.offset); | |
332 | |
333 if (d_.extra_bars.ptr) | |
334 d_.extra_bars.ptr->EncodePointersAndHandles(handles); | |
335 EncodePointer(d_.extra_bars.ptr, &d_.extra_bars.offset); | |
336 } | |
337 | |
338 bool DecodePointersAndHandles(const Message& message) { | |
339 DecodePointer(&d_.bar.offset, &d_.bar.ptr); | |
340 if (d_.bar.ptr) { | |
341 if (!d_.bar.ptr->DecodePointersAndHandles(message)) | |
342 return false; | |
343 } | |
344 | |
345 DecodePointer(&d_.data.offset, &d_.data.ptr); | |
346 if (d_.data.ptr) { | |
347 if (!d_.data.ptr->DecodePointersAndHandles(message)) | |
348 return false; | |
349 } | |
350 | |
351 if (header_.num_fields >= 8) { | |
352 DecodePointer(&d_.extra_bars.offset, &d_.extra_bars.ptr); | |
353 if (d_.extra_bars.ptr) { | |
354 if (!d_.extra_bars.ptr->DecodePointersAndHandles(message)) | |
355 return false; | |
356 } | |
357 } | |
358 | |
359 // TODO: validate | |
360 return true; | |
361 } | |
362 | |
363 void set_x(int32_t x) { d_.x = x; } | |
364 void set_y(int32_t y) { d_.y = y; } | |
365 void set_a(bool a) { d_.a = a; } | |
366 void set_b(bool b) { d_.b = b; } | |
367 void set_c(bool c) { d_.c = c; } | |
368 void set_bar(Bar* bar) { d_.bar.ptr = bar; } | |
369 void set_data(Array<uint8_t>* data) { d_.data.ptr = data; } | |
370 void set_extra_bars(Array<Bar*>* extra_bars) { | |
371 d_.extra_bars.ptr = extra_bars; | |
372 } | |
373 | |
374 int32_t x() const { return d_.x; } | |
375 int32_t y() const { return d_.y; } | |
376 bool a() const { return d_.a; } | |
377 bool b() const { return d_.b; } | |
378 bool c() const { return d_.c; } | |
379 const Bar* bar() const { return d_.bar.ptr; } | |
380 const Array<uint8_t>* data() const { return d_.data.ptr; } | |
381 const Array<Bar*>* extra_bars() const { | |
382 // NOTE: extra_bars is an optional field! | |
383 return header_.num_fields >= 8 ? d_.extra_bars.ptr : NULL; | |
384 } | |
385 | |
386 private: | |
387 StructHeader header_; | |
388 struct { | |
389 int32_t x; | |
390 int32_t y; | |
391 uint32_t a : 1; | |
392 uint32_t b : 1; | |
393 uint32_t c : 1; | |
394 StructPointer<Bar> bar; | |
395 ArrayPointer<uint8_t> data; | |
396 ArrayPointer<Bar*> extra_bars; | |
397 } d_; | |
398 | |
399 ~Foo(); // NOT IMPLEMENTED | |
400 }; | |
401 | |
402 class Frobinate_Params { | |
403 public: | |
404 Frobinate_Params() { | |
405 header_.num_bytes = sizeof(*this) + sizeof(StructHeader); | |
406 header_.num_fields = 2; | |
407 } | |
408 | |
409 void EncodePointersAndHandles(std::vector<Handle>* handles) { | |
410 if (d_.foo.ptr) | |
411 d_.foo.ptr->EncodePointersAndHandles(handles); | |
412 EncodePointer(d_.foo.ptr, &d_.foo.offset); | |
413 } | |
414 bool DecodePointersAndHandles(const Message& message) { | |
415 DecodePointer(&d_.foo.offset, &d_.foo.ptr); | |
416 if (d_.foo.ptr) { | |
417 if (!d_.foo.ptr->DecodePointersAndHandles(message)) | |
418 return false; | |
419 } | |
420 // TODO: validate | |
421 return true; | |
422 } | |
423 | |
424 void set_foo(Foo* foo) { d_.foo.ptr = foo; } | |
425 void set_baz(bool baz) { d_.baz = baz; } | |
426 | |
427 const Foo* foo() const { return d_.foo.ptr; } | |
428 bool baz() const { return d_.baz; } | |
429 | |
430 private: | |
431 StructHeader header_; | |
432 struct { | |
433 StructPointer<Foo> foo; | |
434 uint32_t baz : 1; | |
435 } d_; | |
436 | |
437 ~Frobinate_Params(); // NOT IMPLEMENTED | |
438 }; | |
439 | |
440 const uint32_t kMessageID_Frobinate = 1; | |
441 | |
442 //---- | |
443 // The following code would also be generated by our bindings system. | |
444 | |
445 class Blah { | |
446 public: | |
447 virtual void Frobinate(const Foo* foo, bool baz) = 0; | |
448 }; | |
449 | |
450 class BlahStub : public Blah { | |
451 public: | |
452 bool OnMessageReceived(const Message& message) { | |
453 switch (message.name) { | |
454 case kMessageID_Frobinate: { | |
455 Frobinate_Params* params = | |
456 reinterpret_cast<Frobinate_Params*>(message.data_start); | |
457 if (!params->DecodePointersAndHandles(message)) | |
458 return false; | |
459 Frobinate(params->foo(), params->baz()); | |
460 break; | |
461 } | |
462 } | |
463 return true; | |
464 } | |
465 }; | |
466 | |
467 class BlahProxy : public Blah { | |
468 public: | |
469 virtual void SendMessage(const Message& message) = 0; | |
470 | |
471 virtual void Frobinate(const Foo* foo, bool baz) { | |
472 Buffer buf; | |
473 | |
474 // TODO: We should allocate the MessageHeader here to reserve space. | |
475 //MessageHeader* header = buf.Alloc<MessageHeader>(); | |
476 | |
477 // We now go about allocating the anonymous Frobinate_Params struct. It | |
478 // holds the parameters to the Frobinate message. | |
479 // | |
480 // Notice how foo is cloned. This causes a copy of foo to be generated | |
481 // within the same buffer as the Frobinate_Params struct. That's what we | |
482 // need in order to generate a continguous blob of message data. | |
483 | |
484 Frobinate_Params* params = buf.Alloc<Frobinate_Params>(); | |
485 params->set_foo(foo->Clone(&buf)); | |
486 params->set_baz(baz); | |
487 | |
488 // NOTE: If foo happened to be a graph with cycles, then Clone would not | |
489 // have returned. | |
490 | |
491 // Last step before sending the message is to encode pointers and handles | |
492 // so that messages become hermetic. Pointers become offsets and handles | |
493 // becomes indices into the handles array. | |
494 | |
495 std::vector<Handle> handles; | |
496 params->EncodePointersAndHandles(&handles); | |
497 | |
498 Message message; | |
499 message.name = kMessageID_Frobinate; | |
500 message.data_start = buf.data(); | |
501 message.data_end = buf.data() + buf.size(); | |
502 message.handles_start = &handles[0]; | |
503 message.handles_end = &handles[0] + handles.size(); | |
504 | |
505 SendMessage(message); | |
506 } | |
507 }; | |
508 | |
509 //---- | |
510 // User code goes here: | |
511 | |
512 static void PrintSpacer(int depth) { | |
513 for (int i = 0; i < depth; ++i) | |
514 printf(" "); | |
515 } | |
516 | |
517 static void Print(int depth, const char* name, bool value) { | |
518 PrintSpacer(depth); | |
519 printf("%s: %s\n", name, value ? "true" : "false"); | |
520 } | |
521 | |
522 static void Print(int depth, const char* name, int32_t value) { | |
523 PrintSpacer(depth); | |
524 printf("%s: %d\n", name, value); | |
525 } | |
526 | |
527 static void Print(int depth, const char* name, uint8_t value) { | |
528 PrintSpacer(depth); | |
529 printf("%s: %u\n", name, value); | |
530 } | |
531 | |
532 template <typename T> | |
533 static void Print(int depth, const char* name, const Array<T>* array) { | |
534 PrintSpacer(depth); | |
535 printf("%s: %p\n", name, array); | |
536 if (array) { | |
537 ++depth; | |
538 for (size_t i = 0; i < array->size(); ++i) { | |
539 char buf[32]; | |
540 sprintf(buf, "%lu", i); | |
541 Print(depth, buf, array->at(i)); | |
542 } | |
543 --depth; | |
544 } | |
545 } | |
546 | |
547 static void Print(int depth, const char* name, const Bar* bar) { | |
548 PrintSpacer(depth); | |
549 printf("%s: %p\n", name, bar); | |
550 if (bar) { | |
551 ++depth; | |
552 Print(depth, "alpha", bar->alpha()); | |
553 Print(depth, "beta", bar->beta()); | |
554 Print(depth, "gamma", bar->gamma()); | |
555 --depth; | |
556 } | |
557 } | |
558 | |
559 static void Print(int depth, const char* name, const Foo* foo) { | |
560 PrintSpacer(depth); | |
561 printf("%s: %p\n", name, foo); | |
562 if (foo) { | |
563 ++depth; | |
564 Print(depth, "x", foo->x()); | |
565 Print(depth, "y", foo->y()); | |
566 Print(depth, "a", foo->a()); | |
567 Print(depth, "b", foo->b()); | |
568 Print(depth, "c", foo->c()); | |
569 Print(depth, "bar", foo->bar()); | |
570 Print(depth, "data", foo->data()); | |
571 Print(depth, "extra_bars", foo->extra_bars()); | |
572 --depth; | |
573 } | |
574 } | |
575 | |
576 class BlahImpl : public BlahStub { | |
577 public: | |
578 virtual void Frobinate(const Foo* foo, bool baz) { | |
579 // Users code goes here to handle the incoming Frobinate message. | |
580 // We'll just dump the Foo structure and all of its members. | |
581 | |
582 printf("Frobinate:\n"); | |
583 | |
584 int depth = 1; | |
585 Print(depth, "foo", foo); | |
586 Print(depth, "baz", baz); | |
587 } | |
588 }; | |
589 | |
590 //---- | |
591 | |
592 class BlahProxyImpl : public BlahProxy { | |
593 public: | |
594 virtual void SendMessage(const Message& message) { | |
595 // Imagine some IPC happened here. | |
596 | |
597 // In the receiving process, an implementation of BlahStub is known to the | |
598 // system. It receives the incoming message. | |
599 BlahImpl impl; | |
600 BlahStub* stub = &impl; | |
601 | |
602 stub->OnMessageReceived(message); | |
603 } | |
604 }; | |
605 | |
606 int main() { | |
607 // User has a proxy to a Blah somehow. | |
608 Blah* blah = new BlahProxyImpl(); | |
609 | |
610 // User constructs a message to send. | |
611 | |
612 // Notice that it doesn't matter in what order the structs / arrays are | |
613 // allocated. Here, the various members of Foo are allocated before Foo is | |
614 // allocated. | |
615 | |
616 Buffer buf; | |
617 | |
618 Bar* bar = buf.Alloc<Bar>(); | |
619 bar->set_alpha(20); | |
620 bar->set_beta(40); | |
621 bar->set_gamma(60); | |
622 | |
623 const size_t kNumDataElements = 10; | |
624 Array<uint8_t>* data = buf.AllocArray<uint8_t>(kNumDataElements); | |
625 for (size_t i = 0; i < kNumDataElements; ++i) | |
626 (*data)[i] = static_cast<uint8_t>(kNumDataElements - i); | |
627 | |
628 const size_t kNumExtraBarsElements = 3; | |
629 Array<Bar*>* extra_bars = buf.AllocArray<Bar*>(kNumExtraBarsElements); | |
630 for (size_t i = 0; i < kNumExtraBarsElements; ++i) { | |
631 Bar* bar = buf.Alloc<Bar>(); | |
632 bar->set_alpha(i * 100); | |
633 bar->set_beta(i * 100 + 20); | |
634 bar->set_gamma(i * 100 + 40); | |
635 (*extra_bars)[i] = bar; | |
636 } | |
637 | |
638 Foo* foo = buf.Alloc<Foo>(); | |
639 foo->set_x(1); | |
640 foo->set_y(2); | |
641 foo->set_a(false); | |
642 foo->set_b(true); | |
643 foo->set_c(false); | |
644 foo->set_bar(bar); | |
645 foo->set_data(data); | |
646 foo->set_extra_bars(extra_bars); | |
647 | |
648 blah->Frobinate(foo, true); | |
649 | |
650 return 0; | |
651 } | |
OLD | NEW |