static unsafe bool BlockCopyInternal(byte *src, int srcOffset, byte *dest, int destOffset, int count) { /* Return false on overflow within src or dest */ int srcElemSize = *(int *)(src + ArrayOperations.GetElemSizeOffset()); //int srcIALength = *(int*)(src + ArrayOperations.GetInnerArrayLengthOffset()); int srcIALength = 0; throw new NotImplementedException(); int srcByteSize = srcElemSize * srcIALength; if ((srcOffset + count) > srcByteSize) { return(false); } int destElemSize = *(int *)(dest + ArrayOperations.GetElemSizeOffset()); //int destIALength = *(int*)(dest + ArrayOperations.GetInnerArrayLengthOffset()); int destIALength = 0; int destByteSize = destElemSize * destIALength; if ((destOffset + count) > destByteSize) { return(false); } /* Get source and dest addresses */ byte *srcAddr = *(byte **)(src + ArrayOperations.GetInnerArrayOffset()) + srcOffset; byte *destAddr = *(byte **)(dest + ArrayOperations.GetInnerArrayOffset()) + destOffset; /* Execute a memmove */ MemoryOperations.MemMove((void *)destAddr, (void *)srcAddr, count); return(true); }
static void push_ehdr(void *eh, void *fp) { if (start == null) { void **new_start = (void **)MemoryOperations.GcMalloc(stack_length * sizeof(void *)); fixed(void ***start_addr = &start) { if (OtherOperations.CompareExchange((void **)start_addr, (void *)new_start) == null) { end = start + stack_length; cur = start; System.Diagnostics.Debugger.Log(0, "libsupcs", "new ehdr stack at " + ((ulong)start).ToString("X") + "-" + ((ulong)end).ToString("X")); } } } if ((cur + 1) >= end) { System.Diagnostics.Debugger.Break(); throw new OutOfMemoryException("exception header stack overflowed"); } //if(System.Threading.Thread.CurrentThread.ManagedThreadId != 1) // System.Diagnostics.Debugger.Log(0, "libsupcs", "exceptions: push_ehdr: " + ((ulong)eh).ToString("X")); // the following should be atomic for the current stack only, so disabling interrupts is // sufficient var state = OtherOperations.EnterUninterruptibleSection(); *cur++ = eh; *cur++ = fp; OtherOperations.ExitUninterruptibleSection(state); }
static unsafe bool InternalStrcpy(byte *dest, int destPos, byte *src, int srcPos, int count) { /* Get size of src and dest */ int srcLength = *(int *)(src + StringOperations.GetLengthOffset()); int destLength = *(int *)(dest + StringOperations.GetLengthOffset()); /* Ensure the source and destination are big enough */ if (destPos < 0) { return(false); } if (srcPos < 0) { return(false); } if (count < 0) { return(false); } if (destPos + count > destLength) { return(false); } if (srcPos + count > srcLength) { return(false); } /* Do the copy */ MemoryOperations.MemCpy((void *)(dest + StringOperations.GetDataOffset() + destPos * 2), (void *)(src + StringOperations.GetDataOffset() + srcPos * 2), count * 2); return(true); }
static unsafe object to_object(TysosType enum_type, object value) { /* Convert value (of an integral type) to an enum of type 'enum_type' * and return its boxed value */ if (enum_type == null) { throw new ArgumentNullException("enumType"); } if (value == null) { throw new ArgumentException("value"); } /* The incoming value type 'value' is already boxed. * * We can therefore just copy its data to a new enum object */ void *value_obj = CastOperations.ReinterpretAsPointer(value); void *value_vtbl = *(void **)value_obj; int value_csize = *(int *)((byte *)value_vtbl + ClassOperations.GetVtblTypeSizeOffset()); void *ret_obj = MemoryOperations.GcMalloc(value_csize); MemoryOperations.MemCpy(ret_obj, value_obj, value_csize); void *ret_vtbl = *(void **)((byte *)CastOperations.ReinterpretAsPointer(enum_type) + ClassOperations.GetSystemTypeImplOffset()); *(void **)ret_obj = ret_vtbl; *(void **)((byte *)ret_obj + ClassOperations.GetMutexLockOffset()) = null; return(CastOperations.ReinterpretAsObject(ret_obj)); }
static unsafe void *InternalToObject(void *typedref) { // get the type from the typed reference to see if we need to box the object // or simply return the address as a reference type void *et = *(void **)((byte *)typedref + ClassOperations.GetTypedReferenceTypeOffset()); void *src = *(void **)((byte *)typedref + ClassOperations.GetTypedReferenceValueOffset()); void *etextends = *(void **)((byte *)et + ClassOperations.GetVtblExtendsVtblPtrOffset()); if (etextends == OtherOperations.GetStaticObjectAddress("_Zu1L") || etextends == OtherOperations.GetStaticObjectAddress("_ZW6System4Enum")) { // this is a boxed value type. Get its size var vt_size = TysosType.GetValueTypeSize(et); // build a new boxed type var ret = MemoryOperations.GcMalloc(*(int *)((byte *)et + ClassOperations.GetVtblTypeSizeOffset())); // dst ptr var dst = (byte *)ret + ClassOperations.GetBoxedTypeDataOffset(); CopyMem(dst, (byte *)src, vt_size); return(ret); } else { // simply copy the reference return(*(void **)src); } }
static unsafe void InitializeArray(void *arr, void *fld_handle) { System.Diagnostics.Debugger.Log(0, "libsupcs", "InitializeArray: arr: " + ((ulong)arr).ToString("X16") + ", fld_handle: " + ((ulong)fld_handle).ToString("X16")); void *dst = *(void **)((byte *)arr + ArrayOperations.GetInnerArrayOffset()); /* Get total number of elements, and hence data size */ int *sizes = *(int **)((byte *)arr + ArrayOperations.GetSizesOffset()); int rank = *(int *)((byte *)arr + ArrayOperations.GetRankOffset()); if (rank == 0) { return; } int size = sizes[0]; for (int i = 1; i < rank; i++) { size *= sizes[i]; } int len = size * *(int *)((byte *)arr + ArrayOperations.GetElemSizeOffset()); /* Field Typeinfos hava a pointer to the stored data as their third element */ void *src = ((void **)fld_handle)[2]; System.Diagnostics.Debugger.Log(0, "libsupcs", "InitializeArray: src: " + ((ulong)src).ToString("X16") + ", dest: " + ((ulong)dst).ToString("X16") + ", len: " + len.ToString()); MemoryOperations.MemCpy(dst, src, len); }
public static void *InternalInvoke(void *mptr, object[] args, void *rtype, uint flags) { int p_length = (args == null) ? 0 : args.Length; // See InternalStrCpy for the rationale here int max_stack_alloc = p_length > 512 ? 512 : p_length; IntPtr *pstack = stackalloc IntPtr[max_stack_alloc]; IntPtr *tstack = stackalloc IntPtr[max_stack_alloc]; void **ps, ts; if (max_stack_alloc <= 512) { ps = (void **)pstack; ts = (void **)tstack; } else { ps = (void **)MemoryOperations.GcMalloc(p_length * sizeof(void *)); ts = (void **)MemoryOperations.GcMalloc(p_length * sizeof(void *)); } // Build a new params array and a tysos type array if (args != null) { for (int i = 0; i < args.Length; i++) { var cp = CastOperations.ReinterpretAsPointer(args[i]); ps[i] = cp; ts[i] = *(void **)cp; } } return(InternalInvoke(mptr, args.Length, ps, ts, rtype, flags)); }
static unsafe void StringCtor(byte *str, char[] srcArr) { void *src = MemoryOperations.GetInternalArray(srcArr); int len = srcArr.Length * sizeof(char); void *dst = str + StringOperations.GetDataOffset(); MemoryOperations.MemCpy(dst, src, len); }
static unsafe void StringCtor(byte *str, char[] srcArr, int startIndex, int length) { void *src = (byte *)MemoryOperations.GetInternalArray(srcArr) + sizeof(char) * startIndex; int len = length * sizeof(char); void *dst = str + StringOperations.GetDataOffset(); MemoryOperations.MemCpy(dst, src, len); }
static unsafe void StringCtor(byte *str, char *value, int startIndex, int length) { void *src = value + startIndex; int len = length * sizeof(char); void *dst = str + StringOperations.GetDataOffset(); MemoryOperations.MemCpy(dst, src, len); }
static unsafe void CopyToNative(void *srcArr, int startIndex, void *dst, int length) { var esize = *(int *)((byte *)srcArr + ArrayOperations.GetElemSizeOffset()); int *lovals = *(int **)((byte *)srcArr + ArrayOperations.GetLoboundsOffset()); var src = *(byte **)((byte *)srcArr + ArrayOperations.GetInnerArrayOffset()) + (startIndex - lovals[0]) * esize; MemoryOperations.MemCpy(dst, src, length * esize); }
static unsafe void Clear(void *arr, int index, int length) { /* Get a pointer to the source data */ var elem_size = *(int *)((byte *)arr + ArrayOperations.GetElemSizeOffset()); void *ia = *(void **)((byte *)arr + ArrayOperations.GetInnerArrayOffset()); void *sptr = (void *)((byte *)ia + index * elem_size); var mem_size = length * elem_size; MemoryOperations.MemSet(sptr, 0, mem_size); }
static unsafe void CopyToManaged(void *src, void *dstArr, int startIndex, int length) { var esize = *(int *)((byte *)dstArr + ArrayOperations.GetElemSizeOffset()); int *lovals = *(int **)((byte *)dstArr + ArrayOperations.GetLoboundsOffset()); var dst = *(byte **)((byte *)dstArr + ArrayOperations.GetInnerArrayOffset()) + (startIndex - lovals[0]) * esize; System.Diagnostics.Debugger.Log(0, null, "libsupcs: Array.CopyToManaged: src: " + ((ulong)src).ToString("X16") + ", dstArr: " + ((ulong)dstArr).ToString("X16") + ", startIndex: " + startIndex.ToString() + ", length: " + length.ToString() + ", esize: " + esize.ToString() + ", lovals[0]: " + lovals[0].ToString() + ", dst: " + ((ulong)dst).ToString("X16")); MemoryOperations.MemCpy(dst, src, length * esize); }
/* Build an array of a particular type */ public static unsafe T[] CreateSZArray <T>(int nitems, void *data_addr) { TysosType arrtt = (TysosType)typeof(T[]); TysosType elemtt = (TysosType)typeof(T); int elemsize; if (elemtt.IsValueType) { elemsize = elemtt.GetClassSize() - ClassOperations.GetBoxedTypeDataOffset(); } else { elemsize = OtherOperations.GetPointerSize(); } if (data_addr == null) { data_addr = MemoryOperations.GcMalloc(elemsize * nitems); } byte *ret = (byte *)MemoryOperations.GcMalloc(ArrayOperations.GetArrayClassSize() + 8); // extra space for lobounds and length array void *vtbl = *(void **)((byte *)CastOperations.ReinterpretAsPointer(arrtt) + ClassOperations.GetSystemTypeImplOffset()); *(void **)(ret + ClassOperations.GetVtblFieldOffset()) = vtbl; *(ulong *)(ret + ClassOperations.GetMutexLockOffset()) = 0; *(void **)(ret + ArrayOperations.GetElemTypeOffset()) = *(void **)((byte *)CastOperations.ReinterpretAsPointer(elemtt) + ClassOperations.GetSystemTypeImplOffset()); *(int *)(ret + ArrayOperations.GetElemSizeOffset()) = elemsize; *(void **)(ret + ArrayOperations.GetInnerArrayOffset()) = data_addr; *(void **)(ret + ArrayOperations.GetLoboundsOffset()) = ret + ArrayOperations.GetArrayClassSize(); *(void **)(ret + ArrayOperations.GetSizesOffset()) = ret + ArrayOperations.GetArrayClassSize() + 4; *(int *)(ret + ArrayOperations.GetRankOffset()) = 1; *(int *)(ret + ArrayOperations.GetArrayClassSize()) = 0; // lobounds[0] *(int *)(ret + ArrayOperations.GetArrayClassSize() + 4) = nitems; // sizes[0] return((T[])CastOperations.ReinterpretAsObject(ret)); }
static unsafe void *get_value(void *obj) { /* we can copy the entire enum object to an instance * of the underlying type and just change the * vtbl */ byte *enum_vtbl = *(byte **)obj; int enum_size = *(int *)(enum_vtbl + ClassOperations.GetVtblTypeSizeOffset()); void **enum_ti = *(void ***)enum_vtbl; void * ut_vtbl = *(enum_ti + 1); void *new_obj = MemoryOperations.GcMalloc(enum_size); MemoryOperations.MemCpy(new_obj, obj, enum_size); *(void **)new_obj = ut_vtbl; *(void **)((byte *)new_obj + ClassOperations.GetMutexLockOffset()) = null; return(new_obj); }
public override object Invoke(object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, object[] parameters, System.Globalization.CultureInfo culture) { uint flags = 0; if (MethodAddress == null) { var mangled_name = mspec.MangleMethod(); System.Diagnostics.Debugger.Log(0, "libsupcs", "TysosMethod.Invoke: requesting run-time address for " + mangled_name); MethodAddress = JitOperations.GetAddressOfObject(mspec.MangleMethod()); if (MethodAddress == null) { System.Diagnostics.Debugger.Log(0, "libsupcs", "TysosMethod.Invoke: jit compiling method"); MethodAddress = JitOperations.JitCompile(this.mspec); } } if (MethodAddress == null) { throw new System.Reflection.TargetException("Method does not have a defined implementation (" + OwningType.FullName + "." + Name + "())"); } if (!IsStatic && (obj == null)) { throw new System.Reflection.TargetException("Instance method and obj is null (" + OwningType.FullName + "." + Name + "())"); } // TODO: check number and type of parameters is what the method expects // Get total number of parameters int p_length = 0; if (parameters != null) { p_length = parameters.Length; } if (!IsStatic) { p_length++; } // See InternalStrCpy for the rationale here int max_stack_alloc = p_length > 512 ? 512 : p_length; IntPtr *pstack = stackalloc IntPtr[max_stack_alloc]; IntPtr *tstack = stackalloc IntPtr[max_stack_alloc]; void **ps, ts; if (max_stack_alloc <= 512) { ps = (void **)pstack; ts = (void **)tstack; } else { ps = (void **)MemoryOperations.GcMalloc(p_length * sizeof(void *)); ts = (void **)MemoryOperations.GcMalloc(p_length * sizeof(void *)); } // Build a new params array to include obj if necessary, and a tysos type array int curptr = 0; if (!IsStatic) { ps[0] = CastOperations.ReinterpretAsPointer(obj); ts[0] = OtherOperations.GetStaticObjectAddress("_Zu1O"); curptr++; } if (parameters != null) { for (int i = 0; i < parameters.Length; i++, curptr++) { var cp = CastOperations.ReinterpretAsPointer(parameters[i]); ps[curptr] = cp; ts[curptr] = *(void **)cp; } } if (!IsStatic) { flags |= invoke_flag_instance; } if (OwningType.IsValueType) { flags |= invoke_flag_vt; } if (ReturnType != null && ReturnType.IsValueType) { flags |= invoke_flag_vt_ret; } return(CastOperations.ReinterpretAsObject(InternalInvoke(MethodAddress, p_length, ps, ts, (ReturnType != null) ? TysosType.ReinterpretAsType(ReturnType)._impl : null, flags))); }
static unsafe string InternalReplace(string str, string old_value, string new_value) { /* maximum size of new string is str if new_value <= old_value or * if new_value is larger then assume str is made up of all old_value * in which case it is this count * new_value + remainder (or count + 1 * new_value) */ int max_new_str; if (new_value.Length > old_value.Length) { max_new_str = ((str.Length / old_value.Length) + 1) * new_value.Length; } else { max_new_str = str.Length; } //System.Diagnostics.Debugger.Log(0, "libsupcs", "InternalReplace: str: " + str + ", old_value: " + old_value + // ", new_value: " + new_value + ", max_new_str: " + max_new_str.ToString()); /* limit stack alloc to 1024 chars to reduce risk of stack overflows. Implementations * should have a stack guard page anyway to catch overflows of this size whilst larger * stack allocs could be used to introduce code beyond the guard page. * * We do it this way because C# does not support conditional stack alloc assigns */ int max_stack_new_str = max_new_str > 1024 ? 1024 : max_new_str; char *nstack = stackalloc char[max_stack_new_str]; char *ns; if (max_new_str <= 1024) { ns = nstack; } else { ns = (char *)MemoryOperations.GcMalloc(max_new_str * sizeof(char)); } char *ptr = ns; for (int i = 0; i < str.Length; i++) { bool found = true; for (int j = 0; j < old_value.Length; j++) { if (((i + j) >= str.Length) || (str[i + j] != old_value[j])) { found = false; break; } } if (found) { for (int j = 0; j < new_value.Length; j++) { *ptr++ = new_value[j]; } i += old_value.Length; i--; // the for loop adds a 1 for us } else { *ptr++ = str[i]; } } var str_len = ptr - ns; //var ret = new string(ns, 0, (int)str_len); //System.Diagnostics.Debugger.Log(0, "libsupcs", "InternalReplace: ret: " + ret); //return ret; return(new string(ns, 0, (int)str_len)); }
static unsafe void Copy(void *srcArr, int srcIndex, void *dstArr, int dstIndex, int length, bool reliable) { /* Ensure arrays are valid */ if (srcArr == null || dstArr == null) { throw new ArgumentNullException(); } /* Ensure length is valid */ if (length < 0) { throw new ArgumentOutOfRangeException(); } /* Ensure both arrays are of the same rank */ var srcRank = *(int *)((byte *)srcArr + ArrayOperations.GetRankOffset()); var dstRank = *(int *)((byte *)dstArr + ArrayOperations.GetRankOffset()); if (srcRank != dstRank) { throw new RankException(); } /* Get source and dest element types */ var srcET = *(void **)((byte *)srcArr + ArrayOperations.GetElemTypeOffset()); var dstET = *(void **)((byte *)dstArr + ArrayOperations.GetElemTypeOffset()); /* See if we can do a quick copy */ bool can_quick_copy = false; if (srcET == dstET) { can_quick_copy = true; } else if (TysosType.CanCast(srcET, dstET)) { can_quick_copy = true; } else if (reliable) { throw new ArrayTypeMismatchException(); /* ConstrainedCopy requires types to be the same or derived */ } else { can_quick_copy = false; } /* For now we don't handle arrays with lobounds != 0 */ var srcLobounds = *(int **)((byte *)srcArr + ArrayOperations.GetLoboundsOffset()); var dstLobounds = *(int **)((byte *)dstArr + ArrayOperations.GetLoboundsOffset()); for (int i = 0; i < srcRank; i++) { if (srcLobounds[i] != 0) { throw new NotImplementedException("srcLobounds != 0"); } if (dstLobounds[i] != 0) { throw new NotImplementedException("dstLobounds != 0"); } } if (srcIndex < 0 || dstIndex < 0) { throw new ArgumentOutOfRangeException(); } /* Ensure we don't overflow */ var src_len = GetLength(srcArr); var dst_len = GetLength(dstArr); if (srcIndex + length > src_len) { System.Diagnostics.Debugger.Log(0, "libsupcs", "Array.Copy srcIndex/length out of range"); System.Diagnostics.Debugger.Log(length, "libsupcs", "length"); System.Diagnostics.Debugger.Log(srcIndex, "libsupcs", "srcIndex"); System.Diagnostics.Debugger.Log(src_len, "libsupcs", "src_len"); throw new ArgumentOutOfRangeException(); } if (dstIndex + length > dst_len) { System.Diagnostics.Debugger.Log(0, "libsupcs", "Array.Copy dstIndex/length out of range"); System.Diagnostics.Debugger.Log(length, "libsupcs", "length"); System.Diagnostics.Debugger.Log(dstIndex, "libsupcs", "dstIndex"); System.Diagnostics.Debugger.Log(dst_len, "libsupcs", "dst_len"); throw new ArgumentOutOfRangeException(); } if (can_quick_copy) { /* Elem size of both arrays is guaranteed to be the same and we can do a shallow copy */ var e_size = *(int *)((byte *)srcArr + ArrayOperations.GetElemSizeOffset()); var src_ia = *(byte **)((byte *)srcArr + ArrayOperations.GetInnerArrayOffset()); var dst_ia = *(byte **)((byte *)dstArr + ArrayOperations.GetInnerArrayOffset()); MemoryOperations.MemMove(dst_ia + dstIndex * e_size, src_ia + srcIndex * e_size, length * e_size); } else { /* TODO: implement as per System.Array.Copy() semantics */ throw new NotImplementedException("Cannot quick copy " + ((ulong)srcET).ToString("X") + " to " + ((ulong)dstET).ToString("X")); } }
static unsafe void *GetValueImpl(void *arr, int pos) { /* Get the element type of the array */ void *et = *(void **)((byte *)arr + ArrayOperations.GetElemTypeOffset()); /* Get a pointer to the source data */ var elem_size = *(int *)((byte *)arr + ArrayOperations.GetElemSizeOffset()); void *ia = *(void **)((byte *)arr + ArrayOperations.GetInnerArrayOffset()); void *sptr = (void *)((byte *)ia + pos * elem_size); /* Is this a value type? In which case we need to return a boxed value */ void *extends = *(void **)((byte *)et + ClassOperations.GetVtblExtendsVtblPtrOffset()); if (extends == OtherOperations.GetStaticObjectAddress("_Zu1L") || extends == OtherOperations.GetStaticObjectAddress("_ZW6System4Enum")) { /* This is a value type. We need to read the size of the element, * create a new object of the appropriate size and copy the data * into it */ byte *ret = (byte *)MemoryOperations.GcMalloc(elem_size + ClassOperations.GetBoxedTypeDataOffset()); *(void **)(ret + ClassOperations.GetVtblFieldOffset()) = et; *(ulong *)(ret + ClassOperations.GetMutexLockOffset()) = 0; /* Avoid calls to memcpy if possible */ switch (elem_size) { case 1: *(byte *)(ret + ClassOperations.GetBoxedTypeDataOffset()) = *(byte *)sptr; return(ret); case 2: *(ushort *)(ret + ClassOperations.GetBoxedTypeDataOffset()) = *(ushort *)sptr; return(ret); case 4: *(uint *)(ret + ClassOperations.GetBoxedTypeDataOffset()) = *(uint *)sptr; return(ret); case 8: if (OtherOperations.GetPointerSize() >= 8) { *(ulong *)(ret + ClassOperations.GetBoxedTypeDataOffset()) = *(ulong *)sptr; return(ret); } else { *(uint *)(ret + ClassOperations.GetBoxedTypeDataOffset()) = *(uint *)sptr; *(uint *)(ret + ClassOperations.GetBoxedTypeDataOffset() + 4) = *(uint *)((byte *)sptr + 4); return(ret); } case 16: if (OtherOperations.GetPointerSize() >= 8) { *(ulong *)(ret + ClassOperations.GetBoxedTypeDataOffset()) = *(ulong *)sptr; *(ulong *)(ret + ClassOperations.GetBoxedTypeDataOffset() + 8) = *(ulong *)((byte *)sptr + 8); return(ret); } else { *(uint *)(ret + ClassOperations.GetBoxedTypeDataOffset()) = *(uint *)sptr; *(uint *)(ret + ClassOperations.GetBoxedTypeDataOffset() + 4) = *(uint *)((byte *)sptr + 4); *(uint *)(ret + ClassOperations.GetBoxedTypeDataOffset() + 8) = *(uint *)((byte *)sptr + 8); *(uint *)(ret + ClassOperations.GetBoxedTypeDataOffset() + 12) = *(uint *)((byte *)sptr + 12); return(ret); } } /* Do data copy via memcpy */ MemoryOperations.MemCpy(ret + ClassOperations.GetBoxedTypeDataOffset(), sptr, elem_size); return(ret); } else { /* Its a reference type, so just return the pointer */ return(*(void **)sptr); } }
static unsafe bool FastCopy(void *srcArr, int srcIndex, void *dstArr, int dstIndex, int length) { /* This is often called with length == 0 when the * first item is added to a List<T> as the default * array within the list has length 0. */ if (length == 0) { return(true); } // Ensure both arrays are of rank 1 int srcRank = *(int *)((byte *)srcArr + ArrayOperations.GetRankOffset()); int dstRank = *(int *)((byte *)dstArr + ArrayOperations.GetRankOffset()); if (srcRank != 1) { return(false); } if (dstRank != 1) { return(false); } // Ensure srcIndex is valid int srcLobound = **(int **)((byte *)srcArr + ArrayOperations.GetLoboundsOffset()); int srcSize = **(int **)((byte *)srcArr + ArrayOperations.GetSizesOffset()); srcIndex -= srcLobound; if (srcIndex + length > srcSize) { return(false); } // Ensure destIndex is valid int dstLobound = **(int **)((byte *)dstArr + ArrayOperations.GetLoboundsOffset()); int dstSize = **(int **)((byte *)dstArr + ArrayOperations.GetSizesOffset()); dstIndex -= dstLobound; if (dstIndex + length > dstSize) { return(false); } // Ensure both have same element type void *srcET = *(void **)((byte *)srcArr + ArrayOperations.GetElemTypeOffset()); void *dstET = *(void **)((byte *)dstArr + ArrayOperations.GetElemTypeOffset()); if (srcET != dstET) { return(false); } // Get element size int elemSize = *(int *)((byte *)srcArr + ArrayOperations.GetElemSizeOffset()); srcIndex *= elemSize; dstIndex *= elemSize; byte *srcAddr = *(byte **)((byte *)srcArr + ArrayOperations.GetInnerArrayOffset()) + srcIndex; byte *dstAddr = *(byte **)((byte *)dstArr + ArrayOperations.GetInnerArrayOffset()) + dstIndex; length *= elemSize; MemoryOperations.MemMove(dstAddr, srcAddr, length); return(true); }