public void Decode(byte[] bytes, Iterator it = null) { var decode = Decoder.GetInstance(); if (it == null) { it = new Iterator(); } var changes = new List <DataChange>(); var totalBytes = bytes.Length; // skip TYPE_ID of existing instances if (bytes[it.Offset] == (byte)SPEC.TYPE_ID) { it.Offset += 2; } while (it.Offset < totalBytes) { var index = bytes[it.Offset++]; if (index == (byte)SPEC.END_OF_STRUCTURE) { break; } var field = fieldsByIndex[index]; var fieldType = fieldTypes[field]; System.Type childType; fieldChildTypes.TryGetValue(field, out childType); string childPrimitiveType; fieldChildPrimitiveTypes.TryGetValue(field, out childPrimitiveType); object value = null; object change = null; bool hasChange = false; if (fieldType == "ref") { // child schema type if (decode.NilCheck(bytes, it)) { it.Offset++; value = null; } else { value = this[field] ?? CreateTypeInstance(bytes, it, childType); (value as Schema).Decode(bytes, it); } hasChange = true; } // Array type else if (fieldType == "array") { change = new List <object>(); ISchemaCollection valueRef = (ISchemaCollection)(this[field] ?? Activator.CreateInstance(childType)); ISchemaCollection currentValue = valueRef.Clone(); int newLength = Convert.ToInt32(decode.DecodeNumber(bytes, it)); int numChanges = Math.Min(Convert.ToInt32(decode.DecodeNumber(bytes, it)), newLength); hasChange = (numChanges > 0); bool hasIndexChange = false; // ensure current array has the same length as encoded one if (currentValue.Count > newLength) { for (var i = newLength; i < currentValue.Count; i++) { var item = currentValue[i]; if (item is Schema && (item as Schema).OnRemove != null) { (item as Schema).OnRemove.Invoke(this, new EventArgs()); } currentValue.InvokeOnRemove(item, i); } // reduce items length List <object> items = currentValue.GetItems() as List <object>; currentValue.SetItems(items.GetRange(0, newLength)); } for (var i = 0; i < numChanges; i++) { var newIndex = Convert.ToInt32(decode.DecodeNumber(bytes, it)); int indexChangedFrom = -1; if (decode.IndexChangeCheck(bytes, it)) { decode.DecodeUint8(bytes, it); indexChangedFrom = Convert.ToInt32(decode.DecodeNumber(bytes, it)); hasIndexChange = true; } var isNew = (!hasIndexChange && currentValue[newIndex] == null) || (hasIndexChange && indexChangedFrom != -1); if (currentValue.HasSchemaChild) { Schema item = null; if (isNew) { item = (Schema)CreateTypeInstance(bytes, it, currentValue.GetChildType()); } else if (indexChangedFrom != -1) { item = (Schema)valueRef[indexChangedFrom]; } else { item = (Schema)valueRef[newIndex]; } if (item == null) { item = (Schema)CreateTypeInstance(bytes, it, currentValue.GetChildType()); isNew = true; } if (decode.NilCheck(bytes, it)) { it.Offset++; valueRef.InvokeOnRemove(item, newIndex); continue; } item.Decode(bytes, it); currentValue[newIndex] = item; } else { currentValue[newIndex] = decode.DecodePrimitiveType(childPrimitiveType, bytes, it); } if (isNew) { currentValue.InvokeOnAdd(currentValue[newIndex], newIndex); } else { currentValue.InvokeOnChange(currentValue[newIndex], newIndex); } (change as List <object>).Add(currentValue[newIndex]); } value = currentValue; } // Map type else if (fieldType == "map") { ISchemaCollection valueRef = (ISchemaCollection)(this[field] ?? Activator.CreateInstance(childType)); ISchemaCollection currentValue = valueRef.Clone(); int length = Convert.ToInt32(decode.DecodeNumber(bytes, it)); hasChange = (length > 0); bool hasIndexChange = false; OrderedDictionary items = currentValue.GetItems() as OrderedDictionary; string[] mapKeys = new string[items.Keys.Count]; items.Keys.CopyTo(mapKeys, 0); for (var i = 0; i < length; i++) { // `encodeAll` may indicate a higher number of indexes it actually encodes // TODO: do not encode a higher number than actual encoded entries if (it.Offset > bytes.Length || bytes[it.Offset] == (byte)SPEC.END_OF_STRUCTURE) { break; } string previousKey = null; if (decode.IndexChangeCheck(bytes, it)) { it.Offset++; previousKey = mapKeys[Convert.ToInt32(decode.DecodeNumber(bytes, it))]; hasIndexChange = true; } bool hasMapIndex = decode.NumberCheck(bytes, it); bool isSchemaType = childType != null; string newKey = (hasMapIndex) ? mapKeys[Convert.ToInt32(decode.DecodeNumber(bytes, it))] : decode.DecodeString(bytes, it); object item; bool isNew = (!hasIndexChange && valueRef[newKey] == null) || (hasIndexChange && previousKey == null && hasMapIndex); if (isNew && isSchemaType) { item = (Schema)CreateTypeInstance(bytes, it, currentValue.GetChildType()); } else if (previousKey != null) { item = valueRef[previousKey]; } else { item = valueRef[newKey]; } if (decode.NilCheck(bytes, it)) { it.Offset++; if (item != null && (item as Schema).OnRemove != null) { (item as Schema).OnRemove.Invoke(this, new EventArgs()); } valueRef.InvokeOnRemove(item, newKey); items.Remove(newKey); continue; } else if (!isSchemaType) { currentValue[newKey] = decode.DecodePrimitiveType(childPrimitiveType, bytes, it); } else { (item as Schema).Decode(bytes, it); currentValue[newKey] = item; } if (isNew) { currentValue.InvokeOnAdd(item, newKey); } else { currentValue.InvokeOnChange(item, newKey); } } value = currentValue; } // Primitive type else { value = decode.DecodePrimitiveType(fieldType, bytes, it); hasChange = true; } if (hasChange) { changes.Add(new DataChange { Field = field, Value = (change != null) ? change : value, PreviousValue = this[field] }); } this[field] = value; } if (changes.Count > 0 && OnChange != null) { OnChange.Invoke(this, new OnChangeEventArgs(changes)); } }
protected void TriggerChanges(ref OrderedDictionary allChanges) { foreach (object refId in allChanges.Keys) { List <DataChange> changes = (List <DataChange>)allChanges[refId]; IRef _ref = refs.Get((int)refId); bool isSchema = _ref is Schema; foreach (DataChange change in changes) { //const listener = ref['$listeners'] && ref['$listeners'][change.field]; if (!isSchema) { ISchemaCollection container = ((ISchemaCollection)_ref); if (change.Op == (byte)OPERATION.ADD && change.PreviousValue == container.GetTypeDefaultValue()) { container.InvokeOnAdd(change.Value, change.DynamicIndex); } else if (change.Op == (byte)OPERATION.DELETE) { // // FIXME: `previousValue` should always be avaiiable. // ADD + DELETE operations are still encoding DELETE operation. // if (change.PreviousValue != container.GetTypeDefaultValue()) { container.InvokeOnRemove(change.PreviousValue, change.DynamicIndex ?? change.Field); } } else if (change.Op == (byte)OPERATION.DELETE_AND_ADD) { if (change.PreviousValue != container.GetTypeDefaultValue()) { container.InvokeOnRemove(change.PreviousValue, change.DynamicIndex); } container.InvokeOnAdd(change.Value, change.DynamicIndex); } else if ( change.Op == (byte)OPERATION.REPLACE || change.Value != change.PreviousValue ) { container.InvokeOnChange(change.Value, change.DynamicIndex); } } // // trigger onRemove on child structure. // if ( (change.Op & (byte)OPERATION.DELETE) == (byte)OPERATION.DELETE && change.PreviousValue is Schema ) { ((Schema)change.PreviousValue).OnRemove?.Invoke(); } } if (isSchema) { ((Schema)_ref).OnChange?.Invoke(changes); } } }