Ejemplo n.º 1
0
        /// <summary>
        /// Updates the specified <see cref="target"/> object with new data.
        /// </summary>
        /// <param name="target">The object to update.</param>
        /// <param name="compiledUpdate">The precompiled list of update operations, generated by <see cref="Compile"/>.</param>
        /// <param name="updateData">The data source for blittable struct.</param>
        /// <param name="updateObjects">The data source for objects and non-blittable struct</param>
        public static void Run(object target, CompiledUpdate compiledUpdate, IntPtr updateData, UpdateObjectData[] updateObjects)
        {
            var operations       = compiledUpdate.UpdateOperations;
            var temporaryObjects = compiledUpdate.TemporaryObjects;

            var stack = new Stack <UpdateStackEntry>();

            // Current object being processed
            object currentObj = target;
            object nextObject;

            // This object needs to be pinned since we will have a pointer to its memory
            // Note that the stack don't need to have each of its object pinned since we store entries as object + offset
            Interop.Pin(currentObj);

            // pinned test (this will need to be on a stack somehow)
            IntPtr currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj);

            var operationCount = operations.Length;

            if (operationCount == 0)
            {
                return;
            }

            var operation = Interop.Pin(ref operations[0]);

            for (int index = 0; index < operationCount; index++)
            {
                // Adjust offset
                currentPtr += operation.AdjustOffset;

                switch (operation.Type)
                {
                case UpdateOperationType.EnterObjectProperty:
                {
                    nextObject = ((UpdatableProperty)operation.Member).GetObject(currentPtr);
                    if (nextObject == null && operation.SkipCountIfNull != -1)
                    {
                        index    += operation.SkipCountIfNull;
                        operation = Interop.AddPinned(operation, operation.SkipCountIfNull);
                        break;
                    }

                    // Compute offset and push to stack
                    stack.Push(new UpdateStackEntry(
                                   currentObj,
                                   (int)((byte *)currentPtr - (byte *)UpdateEngineHelper.ObjectToPtr(currentObj))
                                   ));

                    // Get object
                    currentObj = nextObject;
                    currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj);

                    break;
                }

                case UpdateOperationType.EnterStructPropertyBase:
                {
                    // Compute offset and push to stack
                    stack.Push(new UpdateStackEntry(
                                   currentObj,
                                   (int)((byte *)currentPtr - (byte *)UpdateEngineHelper.ObjectToPtr(currentObj))
                                   ));

                    currentObj = temporaryObjects[operation.DataOffset];
                    currentPtr = ((UpdatablePropertyBase)operation.Member).GetStructAndUnbox(currentPtr, currentObj);

                    break;
                }

                case UpdateOperationType.EnterObjectField:
                {
                    nextObject = ((UpdatableField)operation.Member).GetObject(currentPtr);
                    if (nextObject == null && operation.SkipCountIfNull != -1)
                    {
                        index    += operation.SkipCountIfNull;
                        operation = Interop.AddPinned(operation, operation.SkipCountIfNull);
                        break;
                    }

                    // Compute offset and push to stack
                    stack.Push(new UpdateStackEntry(
                                   currentObj,
                                   (int)((byte *)currentPtr - (byte *)UpdateEngineHelper.ObjectToPtr(currentObj))
                                   ));

                    // Get object
                    currentObj = nextObject;
                    currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj);
                    break;
                }

                case UpdateOperationType.EnterObjectCustom:
                {
                    nextObject = ((UpdatableCustomAccessor)operation.Member).GetObject(currentPtr);
                    if (nextObject == null && operation.SkipCountIfNull != -1)
                    {
                        index    += operation.SkipCountIfNull;
                        operation = Interop.AddPinned(operation, operation.SkipCountIfNull);
                        break;
                    }

                    // Compute offset and push to stack
                    stack.Push(new UpdateStackEntry(
                                   currentObj,
                                   (int)((byte *)currentPtr - (byte *)UpdateEngineHelper.ObjectToPtr(currentObj))
                                   ));

                    // Get object
                    currentObj = nextObject;
                    currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj);
                    break;
                }

                case UpdateOperationType.LeaveAndCopyStructPropertyBase:
                {
                    // Save back struct pointer
                    var oldPtr = currentPtr;

                    // Restore currentObj and currentPtr from stack
                    var stackEntry = stack.Pop();
                    currentObj = stackEntry.Object;
                    currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj) + stackEntry.Offset;

                    // Use setter to set back struct
                    ((UpdatablePropertyBase)operation.Member).SetBlittable(currentPtr, oldPtr);

                    break;
                }

                case UpdateOperationType.Leave:
                {
                    // Restore currentObj and currentPtr from stack
                    var stackEntry = stack.Pop();
                    currentObj = stackEntry.Object;
                    currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj) + stackEntry.Offset;
                    break;
                }

                case UpdateOperationType.ConditionalSetObjectProperty:
                {
                    var updateObject = updateObjects[operation.DataOffset];
                    if (updateObject.Condition != 0)     // 0 is 0.0f in float
                    {
                        ((UpdatableProperty)operation.Member).SetObject(currentPtr, updateObject.Value);
                    }
                    break;
                }

                case UpdateOperationType.ConditionalSetBlittablePropertyBase:
                {
                    // TODO: This case can happen quite often (i.e. a float property) and require an extra indirection
                    // We could probably avoid it by having common types as non virtual methods (i.e. object, int, float, maybe even Vector3/4?)
                    var data = (int *)((byte *)updateData + operation.DataOffset);
                    if (*data++ != 0)     // 0 is 0.0f in float
                    {
                        ((UpdatablePropertyBase)operation.Member).SetBlittable(currentPtr, (IntPtr)data);
                    }
                    break;
                }

                case UpdateOperationType.ConditionalSetStructPropertyBase:
                {
                    // TODO: This case can happen quite often (i.e. a float property) and require an extra indirection
                    // We could probably avoid it by having common types as non virtual methods (i.e. object, int, float, maybe even Vector3/4?)
                    var updateObject = updateObjects[operation.DataOffset];
                    if (updateObject.Condition != 0)     // 0 is 0.0f in float
                    {
                        ((UpdatablePropertyBase)operation.Member).SetStruct(currentPtr, updateObject.Value);
                    }
                    break;
                }

                case UpdateOperationType.ConditionalSetObjectField:
                {
                    var updateObject = updateObjects[operation.DataOffset];
                    if (updateObject.Condition != 0)     // 0 is 0.0f in float
                    {
                        ((UpdatableField)operation.Member).SetObject(currentPtr, updateObject.Value);
                    }
                    break;
                }

                case UpdateOperationType.ConditionalSetBlittableField:
                {
                    var data = (int *)((byte *)updateData + operation.DataOffset);
                    if (*data++ != 0)     // 0 is 0.0f in float
                    {
                        ((UpdatableField)operation.Member).SetBlittable(currentPtr, (IntPtr)data);
                    }
                    break;
                }

                case UpdateOperationType.ConditionalSetBlittableField4:
                {
                    var data = (int *)((byte *)updateData + operation.DataOffset);
                    if (*data++ != 0)     // 0 is 0.0f in float
                    {
                        *(int *)currentPtr = *data;
                    }
                    break;
                }

                case UpdateOperationType.ConditionalSetBlittableField8:
                {
                    var data = (int *)((byte *)updateData + operation.DataOffset);
                    if (*data++ != 0)     // 0 is 0.0f in float
                    {
                        *(Blittable8 *)currentPtr = *(Blittable8 *)data;
                    }
                    break;
                }

                case UpdateOperationType.ConditionalSetBlittableField12:
                {
                    var data = (int *)((byte *)updateData + operation.DataOffset);
                    if (*data++ != 0)     // 0 is 0.0f in float
                    {
                        *(Blittable12 *)currentPtr = *(Blittable12 *)data;
                    }
                    break;
                }

                case UpdateOperationType.ConditionalSetBlittableField16:
                {
                    var data = (int *)((byte *)updateData + operation.DataOffset);
                    if (*data++ != 0)     // 0 is 0.0f in float
                    {
                        *(Blittable16 *)currentPtr = *(Blittable16 *)data;
                    }
                    break;
                }

                case UpdateOperationType.ConditionalSetStructField:
                {
                    // Use setter to set back struct
                    var updateObject = updateObjects[operation.DataOffset];
                    if (updateObject.Condition != 0)     // 0 is 0.0f in float
                    {
                        ((UpdatableField)operation.Member).SetStruct(currentPtr, updateObject.Value);
                    }
                    break;
                }

                case UpdateOperationType.ConditionalSetObjectCustom:
                {
                    var updateObject = updateObjects[operation.DataOffset];
                    if (updateObject.Condition != 0)     // 0 is 0.0f in float
                    {
                        ((UpdatableCustomAccessor)operation.Member).SetObject(currentPtr, updateObject.Value);
                    }
                    break;
                }

                default:
                    throw new ArgumentOutOfRangeException();
                }

                operation = Interop.IncrementPinned(operation);
            }
        }