[Mono-list] Deep Marshaling
Giuseppe Greco
giuseppe.greco at agamura.com
Sun Oct 23 15:09:00 EDT 2005
Thanks Jon!
Another alternative would be the following:
[StructLayout(LayoutKind.Sequential)]
internal struct MyStruct
{
public int Id;
public unsafe byte* Data;
}
public unsafe void FillMyStruct(string s)
{
//
// convert the given string into a byte array
//
byte[] data = Encoding.UTF8.GetBytes(s);
//
// create another byte array for storing
// a new instance of MyStruct
//
byte[] buffer = new byte[
sizeof(int) // Size of MyStruct.Id
+ data.Length]; // Length of MyStruct.Data
//
// fill the buffer
//
fixed (byte* pBuffer = buffer, pData data) {
MyStruct* pMyStruct = (MyStruct*) pBuffer;
pMyStruct->Id = 100;
pMyStruct->Data = pData + sizeof(int);
UnsafeCopy(
pData, 0, // source buffer
pMyStruct->Data, 0, // destination buffer
data.Length); // number of bytes to copy
}
}
public static unsafe void UnsafeCopy(
byte* pSource, int sourceOffset,
byte* pDest, int destOffset, int count)
{
byte* ps = pSource + sourceOffset;
byte* pd = pDest + destOffset;
//
// loop over the count in blocks of 8 bytes, copying a
// long integer (8 bytes) at a time
//
int c = count / sizeof(long);
for (int i = 0 ; i < c; i++) {
*((long*)pd) = *((long*)ps);
pd += sizeof(long);
ps += sizeof(long);
}
//
// complete the copy by moving any bytes that were not
// moved in blocks of 8
//
c = count % sizeof(long);
for (int i = 0; i < c; i++) {
*pd = *ps;
pd++;
ps++;
}
}
That works fine and performance is quite good. Of course,
the method UnsafeCopy() is really 'unsafe', but it defined
in an internal class...
j3d.
Jonathan Pryor wrote:
> On Sun, 2005-10-23 at 15:21 +0200, Giuseppe Greco wrote:
>
>>Jon,
>>
>>that you said is really interesting... and I'm wondering how
>>can I solve another similar problem. Giving the following type:
>>
>>[StructLayout(LayoutKind.Sequential)]
>>internal struct MyStruct
>>{
>> public int Id;
>> public byte[] Data; // of course, that's wrong!!!
>>}
>>
>>... I'd like to fill it like this:
>>
>>public unsafe void FillMyStruct(string s)
>>{
>> //
>> // convert the given string into a byte array
>> //
>> byte[] data = Encoding.UTF8.GetBytes(s);
>>
>> //
>> // create another byte array for storing
>> // a new instance of MyStruct
>> //
>> byte[] buffer = new byte[
>> sizeof(int) // Size of MyStruct.Id
>> + data.Length]; // Length of MyStruct.Data
>>
>> //
>> // fill the buffer
>> //
>> fixed (byte* pBuffer = buffer) {
>> MyStruct* pMyStruct = (MyStruct*) pBuffer;
>> pMyStruct->Id = 100; // OK
>> pMyStruct->Data = data; // ERROR
>> }
>>}
>>
>>Any help would be really appreciated,
>>j3d.
>
>
> This has a fundamental problem associated with it: the Common Language
> Infrastructure is *not* C or C++. You can't subvert the type system
> with random casting, as the runtime will *ensure* that all managed types
> are used correctly. This means that such things as C++ "placement new"
> aren't possible.
>
> For your particular example, it means that you can't take a pointer to a
> type containing non-blittable types. Thus declaring a `byte*' is safe,
> but declaring a `System.Collections.ArrayList*' will yield this:
>
> error CS0208: Cannot take the address of, get the size of, or
> declare a pointer to a managed type
> `System.Collections.ArrayList'
>
> The reason for this restriction is primarily the garbage collector.
> ArrayList, and your MyStruct structure, contain managed arrays (object[]
> for ArrayList, and byte[] for MyStruct). The managed array needs to be
> tracked by the garbage collector, lest Bad Things happen. Consequently,
> you cannot use these types in pointer context, as the GC can't safely
> track it.
>
> A solution would be to use only blittable types within your structure,
> though since you seem to want to make Data a variable length array,
> inline with the structure memory, this isn't entirely feasible.
> Instead, you'd have to use a named "first" element of the array, and
> then index off that, e.g.:
>
> struct VaribleLengthStruct {
> public int Size;
> public byte data_begin;
> }
>
> void Test ()
> {
> byte* buffer = stackalloc byte [
> Marshal.SizeOf (VariableLengthStruct)+10
> ];
> VariableLengthStruct *p = (VariableLengthStruct*)
> buffer;
> p->Size = 10;
> byte* data = &p->data_begin;
> for (int i = 0; i < 10; ++i)
> data [i] = (byte) i;
> }
>
> The above is in fact how Mono's System.String handles things:
>
> public sealed class String // : ...
> {
> [NonSerialized] private int length;
> [NonSerialized] private char start_char;
> }
>
> Can you provide more information about the problem you're *actually*
> trying to solve?
>
> - Jon
>
>
--
----------------------------------------
Giuseppe Greco
::agamura::
call giuseppe.greco via Skype
phone: +41 (0)91 604 67 65
mobile: +41 (0)79 590 33 06
email: giuseppe.greco at agamura.com
web: www.agamura.com
----------------------------------------
More information about the Mono-list
mailing list