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)); } }
public void Decode(byte[] bytes, Iterator it = null, ReferenceTracker refs = null) { var decode = Decoder.GetInstance(); if (it == null) { it = new Iterator(); } if (refs == null) { refs = new ReferenceTracker(); } this.refs = refs; var totalBytes = bytes.Length; int refId = 0; IRef _ref = this; var changes = new List <DataChange>(); var allChanges = new OrderedDictionary(); // Dictionary<int, List<DataChange>> refs.Add(refId, this); while (it.Offset < totalBytes) { var _byte = bytes[it.Offset++]; if (_byte == (byte)SPEC.SWITCH_TO_STRUCTURE) { refId = Convert.ToInt32(decode.DecodeNumber(bytes, it)); _ref = refs.Get(refId); // // Trying to access a reference that haven't been decoded yet. // if (_ref == null) { throw new Exception("refId not found: " + refId); } // create empty list of changes for this refId. changes = new List <DataChange>(); allChanges[(object)refId] = changes; continue; } bool isSchema = _ref is Schema; var operation = (byte)((isSchema) ? (_byte >> 6) << 6 // "compressed" index + operation : _byte); // "uncompressed" index + operation (array/map items) if (operation == (byte)OPERATION.CLEAR) { ((ISchemaCollection)_ref).Clear(refs); continue; } int fieldIndex; string fieldName = null; string fieldType = null; System.Type childType = null; if (isSchema) { fieldIndex = _byte % ((operation == 0) ? 255 : operation); // FIXME: JS allows (0 || 255) ((Schema)_ref).fieldsByIndex.TryGetValue(fieldIndex, out fieldName); // fieldType = ((Schema)_ref).fieldTypes[fieldName]; ((Schema)_ref).fieldTypes.TryGetValue(fieldName ?? "", out fieldType); ((Schema)_ref).fieldChildTypes.TryGetValue(fieldName ?? "", out childType); } else { fieldName = ""; // FIXME fieldIndex = Convert.ToInt32(decode.DecodeNumber(bytes, it)); if (((ISchemaCollection)_ref).HasSchemaChild) { fieldType = "ref"; childType = ((ISchemaCollection)_ref).GetChildType(); } else { fieldType = ((ISchemaCollection)_ref).ChildPrimitiveType; } } object value = null; object previousValue = null; object dynamicIndex = null; if (!isSchema) { previousValue = _ref.GetByIndex(fieldIndex); if ((operation & (byte)OPERATION.ADD) == (byte)OPERATION.ADD) { // MapSchema dynamic index. dynamicIndex = (((ISchemaCollection)_ref).GetItems() is OrderedDictionary) ? (object)decode.DecodeString(bytes, it) : fieldIndex; ((ISchemaCollection)_ref).SetIndex(fieldIndex, dynamicIndex); } else { dynamicIndex = ((ISchemaCollection)_ref).GetIndex(fieldIndex); } } else if (fieldName != null) // FIXME: duplicate check { previousValue = ((Schema)_ref)[fieldName]; } // // Delete operations // if ((operation & (byte)OPERATION.DELETE) == (byte)OPERATION.DELETE) { if (operation != (byte)OPERATION.DELETE_AND_ADD) { _ref.DeleteByIndex(fieldIndex); } // Flag `refId` for garbage collection. if (previousValue != null && previousValue is IRef) { refs.Remove(((IRef)previousValue).__refId); } value = null; } if (fieldName == null) { // // keep skipping next bytes until reaches a known structure // by local decoder. // Iterator nextIterator = new Iterator() { Offset = it.Offset }; while (it.Offset < totalBytes) { if (decode.SwitchStructureCheck(bytes, it)) { nextIterator.Offset = it.Offset + 1; if (refs.Has(Convert.ToInt32(decode.DecodeNumber(bytes, nextIterator)))) { break; } } it.Offset++; } continue; } else if (operation == (byte)OPERATION.DELETE) { // // FIXME: refactor me. // Don't do anything. // } else if (fieldType == "ref") { refId = Convert.ToInt32(decode.DecodeNumber(bytes, it)); value = refs.Get(refId); if (operation != (byte)OPERATION.REPLACE) { var concreteChildType = GetSchemaType(bytes, it, childType); if (value == null) { value = CreateTypeInstance(concreteChildType); if (previousValue != null) { ((Schema)value).OnChange = ((Schema)previousValue).OnChange; ((Schema)value).OnRemove = ((Schema)previousValue).OnRemove; if ( ((IRef)previousValue).__refId > 0 && refId != ((IRef)previousValue).__refId ) { refs.Remove(((IRef)previousValue).__refId); } } } refs.Add(refId, (IRef)value, (value != previousValue)); } } else if (childType == null) { // primitive values value = decode.DecodePrimitiveType(fieldType, bytes, it); } else { refId = Convert.ToInt32(decode.DecodeNumber(bytes, it)); value = refs.Get(refId); ISchemaCollection valueRef = (refs.Has(refId)) ? (ISchemaCollection)previousValue : (ISchemaCollection)Activator.CreateInstance(childType); value = valueRef.Clone(); // keep reference to nested childPrimitiveType. string childPrimitiveType; ((Schema)_ref).fieldChildPrimitiveTypes.TryGetValue(fieldName, out childPrimitiveType); ((ISchemaCollection)value).ChildPrimitiveType = childPrimitiveType; if (previousValue != null) { ((ISchemaCollection)value).MoveEventHandlers(((ISchemaCollection)previousValue)); if ( ((IRef)previousValue).__refId > 0 && refId != ((IRef)previousValue).__refId ) { refs.Remove(((IRef)previousValue).__refId); var deletes = new List <DataChange>(); var items = ((ISchemaCollection)previousValue).GetItems(); foreach (var key in items.Keys) { deletes.Add(new DataChange() { DynamicIndex = key, Op = (byte)OPERATION.DELETE, Value = null, PreviousValue = items[key] }); } allChanges[(object)((IRef)previousValue).__refId] = deletes; } } refs.Add(refId, (IRef)value, (valueRef != previousValue)); } bool hasChange = (previousValue != value); if (value != null) { if (value is IRef) { ((IRef)value).__refId = refId; } if (_ref is Schema) { ((Schema)_ref)[fieldName] = value; } else if (_ref is ISchemaCollection) { ((ISchemaCollection)_ref).SetByIndex(fieldIndex, dynamicIndex, value); } } if (hasChange) { changes.Add(new DataChange { Op = operation, Field = fieldName, DynamicIndex = dynamicIndex, Value = value, PreviousValue = previousValue }); } } TriggerChanges(ref allChanges); refs.GarbageCollection(); }