2 * Copyright 2005-2015, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
7 * Michael Lotz <mmlr@mlotz.ch>
11 #include <MessageAdapter.h>
12 #include <MessagePrivate.h>
13 #include <MessageUtils.h>
20 #define R5_MESSAGE_FLAG_VALID 0x01
21 #define R5_MESSAGE_FLAG_INCLUDE_TARGET 0x02
22 #define R5_MESSAGE_FLAG_INCLUDE_REPLY 0x04
23 #define R5_MESSAGE_FLAG_SCRIPT_MESSAGE 0x08
25 #define R5_FIELD_FLAG_VALID 0x01
26 #define R5_FIELD_FLAG_MINI_DATA 0x02
27 #define R5_FIELD_FLAG_FIXED_SIZE 0x04
28 #define R5_FIELD_FLAG_SINGLE_ITEM 0x08
32 SECTION_MESSAGE_HEADER
= 'FOB2',
33 SECTION_OFFSET_TABLE
= 'STof',
34 SECTION_TARGET_INFORMATION
= 'ENwh',
35 SECTION_SINGLE_ITEM_DATA
= 'SGDa',
36 SECTION_FIXED_SIZE_ARRAY_DATA
= 'FADa',
37 SECTION_VARIABLE_SIZE_ARRAY_DATA
= 'VADa',
38 SECTION_SORTED_INDEX_TABLE
= 'DXIn',
39 SECTION_END_OF_DATA
= 'DDEn'
43 struct r5_message_header
{
52 struct dano_section_header
{
59 struct dano_message_header
{
65 typedef struct offset_table_s
{
72 struct dano_single_item
{
80 struct dano_fixed_size_array
{
88 struct dano_variable_size_array
{
99 return (value
+ 7) & ~7;
104 MessageAdapter::FlattenedSize(uint32 format
, const BMessage
*from
)
107 case MESSAGE_FORMAT_R5
:
108 case MESSAGE_FORMAT_R5_SWAPPED
:
109 return _R5FlattenedSize(from
);
117 MessageAdapter::Flatten(uint32 format
, const BMessage
*from
, char *buffer
,
121 case MESSAGE_FORMAT_R5
:
122 case MESSAGE_FORMAT_R5_SWAPPED
:
123 return _FlattenR5Message(format
, from
, buffer
, size
);
131 MessageAdapter::Flatten(uint32 format
, const BMessage
*from
, BDataIO
*stream
,
135 case MESSAGE_FORMAT_R5
:
136 case MESSAGE_FORMAT_R5_SWAPPED
:
138 ssize_t flattenedSize
= _R5FlattenedSize(from
);
139 char *buffer
= (char *)malloc(flattenedSize
);
143 status_t result
= _FlattenR5Message(format
, from
, buffer
,
150 ssize_t written
= stream
->Write(buffer
, flattenedSize
);
151 if (written
!= flattenedSize
) {
153 return (written
>= 0 ? B_ERROR
: written
);
157 *size
= flattenedSize
;
169 MessageAdapter::Unflatten(uint32 format
, BMessage
*into
, const char *buffer
)
171 if (format
== KMessage::kMessageHeaderMagic
) {
173 status_t result
= message
.SetTo(buffer
,
174 ((KMessage::Header
*)buffer
)->size
);
178 return _ConvertFromKMessage(&message
, into
);
183 case MESSAGE_FORMAT_R5
:
185 r5_message_header
*header
= (r5_message_header
*)buffer
;
186 BMemoryIO
stream(buffer
+ sizeof(uint32
),
187 header
->flattened_size
- sizeof(uint32
));
188 return _UnflattenR5Message(format
, into
, &stream
);
191 case MESSAGE_FORMAT_R5_SWAPPED
:
193 r5_message_header
*header
= (r5_message_header
*)buffer
;
194 BMemoryIO
stream(buffer
+ sizeof(uint32
),
195 __swap_int32(header
->flattened_size
) - sizeof(uint32
));
196 return _UnflattenR5Message(format
, into
, &stream
);
199 case MESSAGE_FORMAT_DANO
:
200 case MESSAGE_FORMAT_DANO_SWAPPED
:
202 dano_section_header
*header
= (dano_section_header
*)buffer
;
203 ssize_t size
= header
->size
;
204 if (header
->code
== MESSAGE_FORMAT_DANO_SWAPPED
)
205 size
= __swap_int32(size
);
207 BMemoryIO
stream(buffer
+ sizeof(uint32
), size
- sizeof(uint32
));
208 return _UnflattenDanoMessage(format
, into
, &stream
);
211 } catch (status_t error
) {
216 return B_NOT_A_MESSAGE
;
221 MessageAdapter::Unflatten(uint32 format
, BMessage
*into
, BDataIO
*stream
)
225 case MESSAGE_FORMAT_R5
:
226 case MESSAGE_FORMAT_R5_SWAPPED
:
227 return _UnflattenR5Message(format
, into
, stream
);
229 case MESSAGE_FORMAT_DANO
:
230 case MESSAGE_FORMAT_DANO_SWAPPED
:
231 return _UnflattenDanoMessage(format
, into
, stream
);
233 } catch (status_t error
) {
238 return B_NOT_A_MESSAGE
;
243 MessageAdapter::ConvertToKMessage(const BMessage
* from
, KMessage
& to
)
248 BMessage::Private
fromPrivate(const_cast<BMessage
*>(from
));
249 BMessage::message_header
* header
= fromPrivate
.GetMessageHeader();
250 uint8
* data
= fromPrivate
.GetMessageData();
252 // Iterate through the fields and import them in the target message
253 BMessage::field_header
* field
= fromPrivate
.GetMessageFields();
254 for (uint32 i
= 0; i
< header
->field_count
; i
++, field
++) {
255 const char* name
= (const char*)data
+ field
->offset
;
256 const uint8
* fieldData
= data
+ field
->offset
+ field
->name_length
;
257 bool fixedSize
= (field
->flags
& FIELD_FLAG_FIXED_SIZE
) != 0;
260 status_t status
= to
.AddArray(name
, field
->type
, fieldData
,
261 field
->data_size
/ field
->count
, field
->count
);
265 for (uint32 i
= 0; i
< field
->count
; i
++) {
266 uint32 itemSize
= *(uint32
*)fieldData
;
267 fieldData
+= sizeof(uint32
);
268 status_t status
= to
.AddData(name
, field
->type
, fieldData
,
272 fieldData
+= itemSize
;
281 MessageAdapter::_ConvertFromKMessage(const KMessage
*fromMessage
,
284 if (!fromMessage
|| !toMessage
)
287 // make empty and init what of the target message
288 toMessage
->MakeEmpty();
289 toMessage
->what
= fromMessage
->What();
291 BMessage::Private
toPrivate(toMessage
);
292 toPrivate
.SetTarget(fromMessage
->TargetToken());
293 toPrivate
.SetReply(B_SYSTEM_TEAM
, fromMessage
->ReplyPort(),
294 fromMessage
->ReplyToken());
295 if (fromMessage
->ReplyPort() >= 0) {
296 toPrivate
.GetMessageHeader()->flags
|= MESSAGE_FLAG_REPLY_AS_KMESSAGE
297 | MESSAGE_FLAG_REPLY_REQUIRED
;
300 // Iterate through the fields and import them in the target message
302 while (fromMessage
->GetNextField(&field
) == B_OK
) {
303 int32 elementCount
= field
.CountElements();
304 if (elementCount
> 0) {
305 for (int32 i
= 0; i
< elementCount
; i
++) {
307 const void *data
= field
.ElementAt(i
, &size
);
310 if (field
.TypeCode() == B_MESSAGE_TYPE
) {
311 // message type: if it's a KMessage, convert it
313 if (message
.SetTo(data
, size
) == B_OK
) {
315 result
= _ConvertFromKMessage(&message
, &bMessage
);
319 result
= toMessage
->AddMessage(field
.Name(), &bMessage
);
322 result
= toMessage
->AddData(field
.Name(),
323 field
.TypeCode(), data
, size
,
324 field
.HasFixedElementSize(), 1);
327 result
= toMessage
->AddData(field
.Name(), field
.TypeCode(),
328 data
, size
, field
.HasFixedElementSize(), 1);
342 MessageAdapter::_R5FlattenedSize(const BMessage
*from
)
344 BMessage::Private
messagePrivate((BMessage
*)from
);
345 BMessage::message_header
* header
= messagePrivate
.GetMessageHeader();
347 // header size (variable, depending on the flags)
349 ssize_t flattenedSize
= sizeof(r5_message_header
);
351 if (header
->target
!= B_NULL_TOKEN
)
352 flattenedSize
+= sizeof(int32
);
354 if (header
->reply_port
>= 0 && header
->reply_target
!= B_NULL_TOKEN
355 && header
->reply_team
>= 0) {
356 // reply info + big flags
357 flattenedSize
+= sizeof(port_id
) + sizeof(int32
) + sizeof(team_id
) + 4;
362 uint8
*data
= messagePrivate
.GetMessageData();
363 BMessage::field_header
*field
= messagePrivate
.GetMessageFields();
364 for (uint32 i
= 0; i
< header
->field_count
; i
++, field
++) {
366 flattenedSize
+= 1 + sizeof(type_code
);
369 bool miniData
= field
->dataSize
<= 255 && field
->count
<= 255;
371 // TODO: we don't know the R5 dataSize yet (padding)
372 bool miniData
= false;
376 if (field
->count
> 1)
377 flattenedSize
+= (miniData
? sizeof(uint8
) : sizeof(uint32
));
380 flattenedSize
+= (miniData
? sizeof(uint8
) : sizeof(size_t));
382 // name length and name
383 flattenedSize
+= 1 + min_c(field
->name_length
- 1, 255);
386 if (field
->flags
& FIELD_FLAG_FIXED_SIZE
)
387 flattenedSize
+= field
->data_size
;
389 uint8
*source
= data
+ field
->offset
+ field
->name_length
;
391 for (uint32 i
= 0; i
< field
->count
; i
++) {
392 ssize_t itemSize
= *(ssize_t
*)source
+ sizeof(ssize_t
);
393 flattenedSize
+= pad_to_8(itemSize
);
399 // pseudo field with flags 0
400 return flattenedSize
+ 1;
405 MessageAdapter::_FlattenR5Message(uint32 format
, const BMessage
*from
,
406 char *buffer
, ssize_t
*size
)
408 BMessage::Private
messagePrivate((BMessage
*)from
);
409 BMessage::message_header
*header
= messagePrivate
.GetMessageHeader();
410 uint8
*data
= messagePrivate
.GetMessageData();
412 r5_message_header
*r5header
= (r5_message_header
*)buffer
;
413 uint8
*pointer
= (uint8
*)buffer
+ sizeof(r5_message_header
);
415 r5header
->magic
= MESSAGE_FORMAT_R5
;
416 r5header
->what
= from
->what
;
417 r5header
->checksum
= 0;
419 uint8 flags
= R5_MESSAGE_FLAG_VALID
;
420 if (header
->target
!= B_NULL_TOKEN
) {
421 *(int32
*)pointer
= header
->target
;
422 pointer
+= sizeof(int32
);
423 flags
|= R5_MESSAGE_FLAG_INCLUDE_TARGET
;
426 if (header
->reply_port
>= 0 && header
->reply_target
!= B_NULL_TOKEN
427 && header
->reply_team
>= 0) {
429 *(port_id
*)pointer
= header
->reply_port
;
430 pointer
+= sizeof(port_id
);
431 *(int32
*)pointer
= header
->reply_target
;
432 pointer
+= sizeof(int32
);
433 *(team_id
*)pointer
= header
->reply_team
;
434 pointer
+= sizeof(team_id
);
437 *pointer
= (header
->reply_target
== B_PREFERRED_TOKEN
? 1 : 0);
440 *pointer
= (header
->flags
& MESSAGE_FLAG_REPLY_REQUIRED
? 1 : 0);
443 *pointer
= (header
->flags
& MESSAGE_FLAG_REPLY_DONE
? 1 : 0);
446 *pointer
= (header
->flags
& MESSAGE_FLAG_IS_REPLY
? 1 : 0);
449 flags
|= R5_MESSAGE_FLAG_INCLUDE_REPLY
;
452 if (header
->flags
& MESSAGE_FLAG_HAS_SPECIFIERS
)
453 flags
|= R5_MESSAGE_FLAG_SCRIPT_MESSAGE
;
455 r5header
->flags
= flags
;
457 // store the header size - used for the checksum later
458 ssize_t headerSize
= (addr_t
)pointer
- (addr_t
)buffer
;
460 // collect and add the data
461 BMessage::field_header
*field
= messagePrivate
.GetMessageFields();
462 for (uint32 i
= 0; i
< header
->field_count
; i
++, field
++) {
463 flags
= R5_FIELD_FLAG_VALID
;
465 if (field
->count
== 1)
466 flags
|= R5_FIELD_FLAG_SINGLE_ITEM
;
467 // TODO: we don't really know the data size now (padding missing)
468 // if (field->data_size <= 255 && field->count <= 255)
469 // flags |= R5_FIELD_FLAG_MINI_DATA;
470 if (field
->flags
& FIELD_FLAG_FIXED_SIZE
)
471 flags
|= R5_FIELD_FLAG_FIXED_SIZE
;
476 *(type_code
*)pointer
= field
->type
;
477 pointer
+= sizeof(type_code
);
479 if (!(flags
& R5_FIELD_FLAG_SINGLE_ITEM
)) {
480 if (flags
& R5_FIELD_FLAG_MINI_DATA
) {
481 *pointer
= (uint8
)field
->count
;
484 *(int32
*)pointer
= field
->count
;
485 pointer
+= sizeof(int32
);
489 // we may have to adjust this to account for padding later
490 uint8
*fieldSize
= pointer
;
491 if (flags
& R5_FIELD_FLAG_MINI_DATA
) {
492 *pointer
= (uint8
)field
->data_size
;
495 *(ssize_t
*)pointer
= field
->data_size
;
496 pointer
+= sizeof(ssize_t
);
500 int32 nameLength
= min_c(field
->name_length
- 1, 255);
501 *pointer
= (uint8
)nameLength
;
504 strncpy((char *)pointer
, (char *)data
+ field
->offset
, nameLength
);
505 pointer
+= nameLength
;
508 uint8
*source
= data
+ field
->offset
+ field
->name_length
;
509 if (flags
& R5_FIELD_FLAG_FIXED_SIZE
) {
510 memcpy(pointer
, source
, field
->data_size
);
511 pointer
+= field
->data_size
;
513 uint8
*previous
= pointer
;
514 for (uint32 i
= 0; i
< field
->count
; i
++) {
515 ssize_t itemSize
= *(ssize_t
*)source
+ sizeof(ssize_t
);
516 memcpy(pointer
, source
, itemSize
);
517 ssize_t paddedSize
= pad_to_8(itemSize
);
518 memset(pointer
+ itemSize
, 0, paddedSize
- itemSize
);
519 pointer
+= paddedSize
;
523 // adjust the field size to the padded value
524 if (flags
& R5_FIELD_FLAG_MINI_DATA
)
525 *fieldSize
= (uint8
)(pointer
- previous
);
527 *(ssize_t
*)fieldSize
= (pointer
- previous
);
531 // terminate the fields with a pseudo field with flags 0 (not valid)
535 // calculate the flattened size from the pointers
536 r5header
->flattened_size
= (addr_t
)pointer
- (addr_t
)buffer
;
537 r5header
->checksum
= CalculateChecksum((uint8
*)(buffer
+ 8),
541 *size
= r5header
->flattened_size
;
548 MessageAdapter::_UnflattenR5Message(uint32 format
, BMessage
*into
,
553 BMessage::Private
messagePrivate(into
);
554 BMessage::message_header
*header
= messagePrivate
.GetMessageHeader();
556 TReadHelper
reader(stream
);
557 if (format
== MESSAGE_FORMAT_R5_SWAPPED
)
558 reader
.SetSwap(true);
560 // the stream is already advanced by the size of the "format"
561 r5_message_header r5header
;
562 reader(((uint8
*)&r5header
) + sizeof(uint32
),
563 sizeof(r5header
) - sizeof(uint32
));
565 header
->what
= into
->what
= r5header
.what
;
566 if (r5header
.flags
& R5_MESSAGE_FLAG_INCLUDE_TARGET
)
567 reader(&header
->target
, sizeof(header
->target
));
569 if (r5header
.flags
& R5_MESSAGE_FLAG_INCLUDE_REPLY
) {
571 reader(&header
->reply_port
, sizeof(header
->reply_port
));
572 reader(&header
->reply_target
, sizeof(header
->reply_target
));
573 reader(&header
->reply_team
, sizeof(header
->reply_team
));
579 header
->reply_target
= B_PREFERRED_TOKEN
;
583 header
->flags
|= MESSAGE_FLAG_REPLY_REQUIRED
;
587 header
->flags
|= MESSAGE_FLAG_REPLY_DONE
;
591 header
->flags
|= MESSAGE_FLAG_IS_REPLY
;
594 if (r5header
.flags
& R5_MESSAGE_FLAG_SCRIPT_MESSAGE
)
595 header
->flags
|= MESSAGE_FLAG_HAS_SPECIFIERS
;
599 while ((flags
& R5_FIELD_FLAG_VALID
) != 0) {
600 bool fixedSize
= flags
& R5_FIELD_FLAG_FIXED_SIZE
;
601 bool miniData
= flags
& R5_FIELD_FLAG_MINI_DATA
;
602 bool singleItem
= flags
& R5_FIELD_FLAG_SINGLE_ITEM
;
612 itemCount
= miniCount
;
633 char nameBuffer
[256];
634 reader(nameBuffer
, nameLength
);
635 nameBuffer
[nameLength
] = '\0';
637 uint8
*buffer
= (uint8
*)malloc(dataSize
);
638 uint8
*pointer
= buffer
;
639 reader(buffer
, dataSize
);
641 status_t result
= B_OK
;
644 itemSize
= dataSize
/ itemCount
;
646 if (format
== MESSAGE_FORMAT_R5
) {
647 for (int32 i
= 0; i
< itemCount
; i
++) {
649 itemSize
= *(int32
*)pointer
;
650 pointer
+= sizeof(int32
);
653 result
= into
->AddData(nameBuffer
, type
, pointer
, itemSize
,
654 fixedSize
, itemCount
);
664 pointer
+= pad_to_8(itemSize
+ sizeof(int32
))
669 for (int32 i
= 0; i
< itemCount
; i
++) {
671 itemSize
= __swap_int32(*(int32
*)pointer
);
672 pointer
+= sizeof(int32
);
675 swap_data(type
, pointer
, itemSize
, B_SWAP_ALWAYS
);
676 result
= into
->AddData(nameBuffer
, type
, pointer
, itemSize
,
677 fixedSize
, itemCount
);
687 pointer
+= pad_to_8(itemSize
+ sizeof(int32
))
695 // flags of next field or termination byte
704 MessageAdapter::_UnflattenDanoMessage(uint32 format
, BMessage
*into
,
709 TReadHelper
reader(stream
);
710 if (format
== MESSAGE_FORMAT_DANO_SWAPPED
)
711 reader
.SetSwap(true);
716 dano_message_header header
;
718 into
->what
= header
.what
;
720 size
-= sizeof(dano_section_header
) + sizeof(dano_message_header
);
723 while (offset
< size
) {
724 dano_section_header sectionHeader
;
725 reader(sectionHeader
);
727 // be safe. this shouldn't be necessary but in some testcases it was.
728 sectionHeader
.size
= pad_to_8(sectionHeader
.size
);
730 if (offset
+ sectionHeader
.size
> size
|| sectionHeader
.size
< 0)
733 ssize_t fieldSize
= sectionHeader
.size
- sizeof(dano_section_header
);
734 uint8
*fieldBuffer
= NULL
;
735 if (fieldSize
<= 0) {
736 // there may be no data. we shouldn't fail because of that
737 offset
+= sectionHeader
.size
;
741 fieldBuffer
= (uint8
*)malloc(fieldSize
);
742 if (fieldBuffer
== NULL
)
743 throw (status_t
)B_NO_MEMORY
;
745 reader(fieldBuffer
, fieldSize
);
747 switch (sectionHeader
.code
) {
748 case SECTION_OFFSET_TABLE
:
749 case SECTION_TARGET_INFORMATION
:
750 case SECTION_SORTED_INDEX_TABLE
:
751 case SECTION_END_OF_DATA
:
755 case SECTION_SINGLE_ITEM_DATA
:
757 dano_single_item
*field
= (dano_single_item
*)fieldBuffer
;
759 int32 dataOffset
= sizeof(dano_single_item
)
760 + field
->name_length
+ 1;
761 dataOffset
= pad_to_8(dataOffset
);
763 if (offset
+ dataOffset
+ field
->item_size
> size
)
766 // support for fixed size is not possible with a single item
767 bool fixedSize
= false;
768 switch (field
->type
) {
779 case B_MESSENGER_TYPE
:
786 status_t result
= into
->AddData(field
->name
, field
->type
,
787 fieldBuffer
+ dataOffset
, field
->item_size
, fixedSize
);
789 if (result
!= B_OK
) {
796 case SECTION_FIXED_SIZE_ARRAY_DATA
: {
797 dano_fixed_size_array
*field
798 = (dano_fixed_size_array
*)fieldBuffer
;
800 int32 dataOffset
= sizeof(dano_fixed_size_array
)
801 + field
->name_length
+ 1;
802 dataOffset
= pad_to_8(dataOffset
);
803 int32 count
= *(int32
*)(fieldBuffer
+ dataOffset
);
804 dataOffset
+= 8; /* count and padding */
806 if (offset
+ dataOffset
+ count
* field
->size_per_item
> size
)
809 status_t result
= B_OK
;
810 for (int32 i
= 0; i
< count
; i
++) {
811 result
= into
->AddData(field
->name
, field
->type
,
812 fieldBuffer
+ dataOffset
, field
->size_per_item
, true,
815 if (result
!= B_OK
) {
820 dataOffset
+= field
->size_per_item
;
825 case SECTION_VARIABLE_SIZE_ARRAY_DATA
: {
826 dano_variable_size_array
*field
827 = (dano_variable_size_array
*)fieldBuffer
;
829 int32 dataOffset
= sizeof(dano_variable_size_array
)
830 + field
->name_length
+ 1;
831 dataOffset
= pad_to_8(dataOffset
);
832 int32 count
= *(int32
*)(fieldBuffer
+ dataOffset
);
833 dataOffset
+= sizeof(int32
);
834 ssize_t totalSize
= *(ssize_t
*)(fieldBuffer
+ dataOffset
);
835 dataOffset
+= sizeof(ssize_t
);
837 int32
*endPoints
= (int32
*)(fieldBuffer
+ dataOffset
840 status_t result
= B_OK
;
841 for (int32 i
= 0; i
< count
; i
++) {
842 int32 itemOffset
= (i
> 0 ? pad_to_8(endPoints
[i
- 1]) : 0);
844 result
= into
->AddData(field
->name
, field
->type
,
845 fieldBuffer
+ dataOffset
+ itemOffset
,
846 endPoints
[i
] - itemOffset
, false, count
);
848 if (result
!= B_OK
) {
858 offset
+= sectionHeader
.size
;
865 } // namespace BPrivate