/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using com.sun.star.uno.helper;
namespace com.sun.star.uno.native
{
internal class Marshaller
{
private readonly NetEnvironment _env;
public Marshaller(NetEnvironment env) => _env = env;
public unsafe void MarshalObject(Type type,
object src, InteropValue* value,
bool destruct
Value)
=> MarshalObject(type, src, (void *)value, destructValue);
public unsafe void UnmarshalObject(Type type, ref object dst, InteropValue* value, bool assignObject)
=> UnmarshalObject(type, ref dst, (void *)value, assignObject);
private unsafe void MarshalObject(Type type, object source, void * value, bool destructValue)
{
switch (Type.GetTypeCode(type))
{
case TypeCode.Boolean:
*(byte *)value = (byte )((bool )source ? 1 : 0);
break ;
case TypeCode.SByte :
*(sbyte *)value = (sbyte )source;
break ;
case TypeCode.Char :
*(char *)value = (char )source;
break ;
case TypeCode.Int16:
*(short *)value = (short )source;
break ;
case TypeCode.UInt16:
*(ushort *)value = (ushort )source;
break ;
case TypeCode.Int32:
*(int *)value = (int )source;
break ;
case TypeCode.UInt32:
*(uint *)value = (uint )source;
break ;
case TypeCode.Int64:
*(long *)value = (long )source;
break ;
case TypeCode.UInt64:
*(ulong *)value = (ulong )source;
break ;
case TypeCode.Single:
*(float *)value = (float )source;
break ;
case TypeCode.Double :
*(double *)value = (double )source;
break ;
case TypeCode.String :
{
IntPtr* valueStr = (IntPtr*)value;
string netStr = (string )source;
if (destructValue && *valueStr != IntPtr.Zero)
InteropMethods.freeMemory(*valueStr);
*valueStr = MarshalString(netStr);
break ;
}
default :
if (type == typeof (void ))
{
*(IntPtr*)value = IntPtr.Zero;
}
else if (type == typeof (Type))
{
IntPtr* valueType = (IntPtr*)value;
Type netType = (Type)source;
if (destructValue && *valueType != IntPtr.Zero)
InteropMethods.freeMemory(*valueType);
*valueType = MarshalString(netType.FullName);
}
else if (type == typeof (Any))
{
InteropValue.Any* valueAny = (InteropValue.Any*)value;
Any netAny = (Any)source;
switch (Type.GetTypeCode(netAny.Type))
{
case TypeCode.Boolean:
case TypeCode.SByte :
case TypeCode.Char :
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Single:
MarshalObject(netAny.Type, netAny.Value, (void *)&valueAny->data, destructValue);
break ;
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Double :
{
int size = SizeOf (netAny.Type);
if (size <= IntPtr.Size)
MarshalObject(netAny.Type, netAny.Value, (void *)&valueAny->data, destructValue);
else
{
IntPtr mem = InteropMethods.allocateMemory(size);
MarshalObject(netAny.Type, netAny.Value, (void *)mem, false );
if (destructValue)
InteropMethods.freeMemory(valueAny->data);
valueAny->data = mem;
}
break ;
}
default :
if (netAny.Type == typeof (Any) || netAny.Type.IsArray)
{
IntPtr mem = InteropMethods.allocateMemory(SizeOf (netAny.Type));
MarshalObject(netAny.Type, netAny.Value, (void *)mem, false );
if (destructValue)
InteropMethods.freeMemory(valueAny->data);
valueAny->data = mem;
}
else
{
MarshalObject(netAny.Type, netAny.Value, (void *)&valueAny->data, destructValue);
}
break ;
}
MarshalObject(typeof (Type), netAny.Type, (void *)&valueAny->type, destructValue);
}
else if (type.IsEnum)
{
*(int *)value = (int )source;
}
else if (type.IsArray)
{
InteropValue.Sequence* valueSeq = (InteropValue.Sequence*)value;
Array netArray = (Array)source;
Type elemType = type.GetElementType();
int elemSize = SizeOf (elemType);
int memSize = elemSize * netArray.Length;
IntPtr mem = InteropMethods.allocateMemory(memSize);
switch (Type.GetTypeCode(elemType))
{
case TypeCode.Boolean:
fixed (void * src = (bool [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
case TypeCode.SByte :
fixed (void * src = (sbyte [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
case TypeCode.Char :
fixed (void * src = (char [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
case TypeCode.Int16:
fixed (void * src = (short [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
case TypeCode.UInt16:
fixed (void * src = (ushort [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
case TypeCode.Int32:
fixed (void * src = (int [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
case TypeCode.UInt32:
fixed (void * src = (uint [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
case TypeCode.Int64:
fixed (void * src = (long [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
case TypeCode.UInt64:
fixed (void * src = (ulong [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
case TypeCode.Single:
fixed (void * src = (float [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
case TypeCode.Double :
fixed (void * src = (double [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
break ;
default :
if (elemType.IsEnum)
{
fixed (void * src = (int [])netArray)
Buffer.MemoryCopy(src, (void *)mem, memSize, memSize);
}
else
{
for (int i = 0; i < netArray.Length; i++)
MarshalObject(elemType, netArray.GetValue(i), (void *)(mem + elemSize * i), destructValue);
}
break ;
}
if (destructValue && valueSeq->data != IntPtr.Zero)
InteropMethods.freeMemory(valueSeq->data);
valueSeq->data = mem;
valueSeq->length = netArray.Length;
}
else if (typeof (IQueryInterface).IsAssignableFrom(type))
{
*(IntPtr*)value = source is NativeUnoProxy nup
? _env.LookupInterface(nup.Oid, nup.Type)
: _env.RegisterLocal(source, type);
}
else
{
IntPtr* valueStruct = (IntPtr*)value;
(Action<object , object []> dtor, Type[] fieldTypes) = StructHelper.GetDeconstructor(type);
object [] fields = new object [fieldTypes.Length];
dtor.Invoke(source, fields);
IntPtr mem = InteropMethods.allocateMemory(fieldTypes.Sum(SizeOf ));
int offset = 0;
for (int i = 0; i < fieldTypes.Length; i++)
{
MarshalObject(fieldTypes[i], fields[i], (void *)(mem + offset), destructValue);
offset += SizeOf (fieldTypes[i]);
}
if (destructValue && *valueStruct != IntPtr.Zero)
InteropMethods.freeMemory(*valueStruct);
*valueStruct = mem;
}
break ;
}
}
private unsafe void UnmarshalObject(Type type, ref object target, void * value, bool assignObject)
{
switch (Type.GetTypeCode(type))
{
case TypeCode.Boolean:
if (assignObject)
target = *(byte *)value != 0;
break ;
case TypeCode.SByte :
if (assignObject)
target = *(byte *)value;
break ;
case TypeCode.Char :
if (assignObject)
target = *(char *)value;
break ;
case TypeCode.Int16:
if (assignObject)
target = *(short *)value;
break ;
case TypeCode.UInt16:
if (assignObject)
target = *(ushort *)value;
break ;
case TypeCode.Int32:
if (assignObject)
target = *(int *)value;
break ;
case TypeCode.UInt32:
if (assignObject)
target = *(uint *)value;
break ;
case TypeCode.Int64:
if (assignObject)
target = *(long *)value;
break ;
case TypeCode.UInt64:
if (assignObject)
target = *(ulong *)value;
break ;
case TypeCode.Single:
if (assignObject)
target = *(float *)value;
break ;
case TypeCode.Double :
if (assignObject)
target = *(double *)value;
break ;
case TypeCode.String :
{
IntPtr valueStr = *(IntPtr*)value;
if (assignObject)
target = UnmarshalString(valueStr);
else
InteropMethods.freeMemory(valueStr);
}
break ;
default :
if (type == typeof (void ))
{
if (assignObject)
target = null ;
}
else if (type == typeof (Type))
{
IntPtr valueType = *(IntPtr*)value;
if (assignObject)
target = TypeHelper.ParseType(UnmarshalString(valueType));
else
InteropMethods.freeMemory(valueType);
}
else if (type == typeof (Any))
{
InteropValue.Any* valueAny = (InteropValue.Any*)value;
object contents = null ;
Type containedType = TypeHelper.ParseType(UnmarshalString(valueAny->type));
if (assignObject)
{
switch (Type.GetTypeCode(containedType))
{
case TypeCode.Boolean:
case TypeCode.SByte :
case TypeCode.Char :
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Single:
UnmarshalObject(containedType, ref contents, (void *)&valueAny->data, true );
break ;
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Double :
{
int size = SizeOf (containedType);
if (size <= IntPtr.Size)
{
UnmarshalObject(containedType, ref contents, (void *)&valueAny->data, true );
}
else
{
UnmarshalObject(containedType, ref contents, (void *)valueAny->data, true );
InteropMethods.freeMemory(valueAny->data);
}
break ;
}
default :
if (containedType == typeof (Any) || containedType.IsArray)
{
UnmarshalObject(containedType, ref contents, (void *)valueAny->data, true );
InteropMethods.freeMemory(valueAny->data);
}
else
{
UnmarshalObject(containedType, ref contents, (void *)&valueAny->data, true );
}
break ;
}
target = new Any(containedType, contents);
}
else
{
switch (Type.GetTypeCode(containedType))
{
case TypeCode.Boolean:
case TypeCode.SByte :
case TypeCode.Char :
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Single:
break ;
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Double :
if (SizeOf (containedType) > IntPtr.Size)
InteropMethods.freeMemory(valueAny->data);
break ;
default :
if (containedType == typeof (Any) || containedType.IsArray)
{
UnmarshalObject(containedType, ref contents, (void *)valueAny->data, false );
InteropMethods.freeMemory(valueAny->data);
}
else
{
UnmarshalObject(containedType, ref contents, (void *)&valueAny->data, false );
}
break ;
}
}
}
else if (type.IsEnum)
{
target = *(int *)value;
}
else if (type.IsArray)
{
InteropValue.Sequence* valueSeq = (InteropValue.Sequence*)value;
Type elemType = type.GetElementType();
int elemSize = SizeOf (elemType);
if (assignObject)
{
int memSize = elemSize * valueSeq->length;
Array netArray = Array.CreateInstance(elemType, valueSeq->length);
switch (Type.GetTypeCode(elemType))
{
case TypeCode.Boolean:
fixed (void * dst = (bool [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
case TypeCode.SByte :
fixed (void * dst = (sbyte [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
case TypeCode.Char :
fixed (void * dst = (char [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
case TypeCode.Int16:
fixed (void * dst = (short [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
case TypeCode.UInt16:
fixed (void * dst = (ushort [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
case TypeCode.Int32:
fixed (void * dst = (int [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
case TypeCode.UInt32:
fixed (void * dst = (uint [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
case TypeCode.Int64:
fixed (void * dst = (long [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
case TypeCode.UInt64:
fixed (void * dst = (ulong [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
case TypeCode.Single:
fixed (void * dst = (float [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
case TypeCode.Double :
fixed (void * dst = (double [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
break ;
default :
if (elemType.IsEnum)
{
fixed (void * dst = (int [])netArray)
Buffer.MemoryCopy((void *)valueSeq->data, dst, memSize, memSize);
}
else
{
object inner = null ;
for (int i = 0; i < netArray.Length; i++)
{
UnmarshalObject(elemType, ref inner, (void *)(valueSeq->data + elemSize * i), true );
netArray.SetValue(inner, i);
}
}
break ;
}
target = netArray;
}
else
{
switch (Type.GetTypeCode(elemType))
{
case TypeCode.Boolean:
case TypeCode.SByte :
case TypeCode.Char :
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double :
break ;
default :
if (elemType.IsEnum || typeof (IQueryInterface).IsAssignableFrom(type))
{
break ;
}
else
{
object dummy = null ;
for (int i = 0; i < valueSeq->length; i++)
UnmarshalObject(elemType, ref dummy, (void *)(valueSeq->data + elemSize * i), false );
}
break ;
}
}
InteropMethods.freeMemory(valueSeq->data);
}
else if (typeof (IQueryInterface).IsAssignableFrom(type))
{
if (assignObject)
target = _env.GetRegisteredObject(*(IntPtr*)value);
}
else
{
IntPtr valueStruct = *(IntPtr*)value;
(Func<object [], object > ctor, Type[] paramTypes) = StructHelper.GetConstructor(type);
if (assignObject)
{
object [] args = new object [paramTypes.Length];
int offset = 0;
for (int i = 0; i < paramTypes.Length; i++)
{
UnmarshalObject(paramTypes[i], ref args[i], (void *)(valueStruct + offset), true );
offset += SizeOf (paramTypes[i]);
}
target = ctor(args);
}
else
{
object dummy = null ;
int offset = 0;
for (int i = 0; i < paramTypes.Length; i++)
{
UnmarshalObject(paramTypes[i], ref dummy, (void *)(valueStruct + offset), false );
offset += SizeOf (paramTypes[i]);
}
}
InteropMethods.freeMemory(valueStruct);
}
break ;
}
}
private unsafe IntPtr MarshalString(string str)
{
char * buffer = (char *)InteropMethods.allocateMemory((str.Length + 1) * sizeof (char ));
fixed (char * data = str)
{
Buffer.MemoryCopy(data, buffer, str.Length * sizeof (char ), str.Length * sizeof (char ));
buffer[str.Length] = '\0' ;
}
return (IntPtr)buffer;
}
private string UnmarshalString(IntPtr ptr)
{
string s = Marshal.PtrToStringUni(ptr);
InteropMethods.freeMemory(ptr);
return s;
}
private int SizeOf (Type type)
{
switch (Type.GetTypeCode(type))
{
case TypeCode.Boolean: return sizeof (bool );
case TypeCode.SByte : return sizeof (sbyte );
case TypeCode.Char : return sizeof (char );
case TypeCode.Int16: return sizeof (short );
case TypeCode.UInt16: return sizeof (ushort );
case TypeCode.Int32: return sizeof (int );
case TypeCode.UInt32: return sizeof (uint );
case TypeCode.Int64: return sizeof (long );
case TypeCode.UInt64: return sizeof (ulong );
case TypeCode.Single: return sizeof (float );
case TypeCode.Double : return sizeof (double );
}
if (type.IsEnum)
return sizeof (int );
else if (type.IsArray)
return IntPtr.Size + sizeof (int );
else if (type == typeof (Any))
return IntPtr.Size * 2;
return IntPtr.Size;
}
}
}
Messung V0.5 C=100 H=97 G=98
¤ Dauer der Verarbeitung: 0.51 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland