private static AbstractRecord ReadSingleRecord(DeserializationState state) { AbstractRecord retVal; if (state.nullCount > 0) { --state.nullCount; retVal = null; } else { ClassRecord cRecord; ArrayRecord aRecord; var recType = (RecordTypeEnumeration)state.array.ReadByteFromBytes(ref state.idx); switch (recType) { case RecordTypeEnumeration.SerializedStreamHeader: state.array.Skip(ref state.idx, 16); // Skip the header of 4 ints retVal = null; break; case RecordTypeEnumeration.ClassWithID: cRecord = NewRecord <ClassRecord>(state); var refID = state.array.ReadInt32LEFromBytes(ref state.idx); var refRecord = (ClassRecord)state.records[refID]; // Copy metadata from referenced object cRecord.AssemblyName = refRecord.AssemblyName; cRecord.TypeName = refRecord.TypeName; cRecord.Members.Capacity = refRecord.Members.Count; for (var i = 0; i < refRecord.Members.Count; ++i) { var member = new ClassRecordMember(); var refMember = refRecord.Members[i]; member.AssemblyName = refMember.AssemblyName; member.TypeName = refMember.TypeName; member.Name = refMember.Name; cRecord.Members.Add(member); } // Read values ReadMemberValues(state, cRecord); retVal = cRecord; break; case RecordTypeEnumeration.SystemClassWithMembers: cRecord = NewRecord <ClassRecord>(state); ReadMembers(state, cRecord); ReadMemberValues(state, cRecord); retVal = cRecord; break; case RecordTypeEnumeration.ClassWithMembers: cRecord = NewRecord <ClassRecord>(state); ReadMembers(state, cRecord); cRecord.AssemblyName = state.assemblies[state.array.ReadInt32LEFromBytes(ref state.idx)]; ReadMemberValues(state, cRecord); retVal = cRecord; break; case RecordTypeEnumeration.SystemClassWithMembersAndTypes: cRecord = NewRecord <ClassRecord>(state); ReadMembers(state, cRecord); ReadMemberTypes(state, cRecord); ReadMemberValues(state, cRecord); retVal = cRecord; break; case RecordTypeEnumeration.ClassWithMembersAndTypes: cRecord = NewRecord <ClassRecord>(state); ReadMembers(state, cRecord); ReadMemberTypes(state, cRecord); cRecord.AssemblyName = state.assemblies[state.array.ReadInt32LEFromBytes(ref state.idx)]; ReadMemberValues(state, cRecord); retVal = cRecord; break; case RecordTypeEnumeration.BinaryObjectString: var strRecord = NewRecord <StringRecord>(state); strRecord.StringValue = state.array.Read7BitLengthPrefixedString(ref state.idx); retVal = strRecord; break; case RecordTypeEnumeration.BinaryArray: aRecord = NewRecord <ArrayRecord>(state); ReadArrayInformation(state, aRecord); ReadArrayValues(state, aRecord); retVal = aRecord; break; case RecordTypeEnumeration.MemberPrimitiveTyped: var pRecord = new PrimitiveWrapperRecord(); pRecord.Value = ReadPrimitiveValue(state, (PrimitiveTypeEnumeration)state.array.ReadByteFromBytes(ref state.idx)); retVal = pRecord; break; case RecordTypeEnumeration.MemberReference: retVal = new RecordPlaceholder(state.array.ReadInt32LEFromBytes(ref state.idx)); break; case RecordTypeEnumeration.ObjectNull: retVal = null; break; case RecordTypeEnumeration.MessageEnd: state.recordsEnded = true; retVal = null; break; case RecordTypeEnumeration.BinaryLibrary: // VS2012 should guarantee that first parameter will be executed first state.assemblies.Add(state.array.ReadInt32LEFromBytes(ref state.idx), state.array.Read7BitLengthPrefixedString(ref state.idx)); retVal = null; break; case RecordTypeEnumeration.ObjectNullMultiple256: state.nullCount = state.array.ReadByteFromBytes(ref state.idx) - 1; // Returning the first null retVal = null; break; case RecordTypeEnumeration.ObjectNullMultiple: state.nullCount = state.array.ReadInt32LEFromBytes(ref state.idx) - 1; // Returning the first null retVal = null; break; case RecordTypeEnumeration.ArraySinglePrimitive: aRecord = NewRecord <ArrayRecord>(state); state.typeInfos.Add(aRecord, BinaryTypeEnumeration.Primitive); ReadArrayLengths(state, aRecord); ReadAdditionalTypeInfo(state, aRecord); ReadArrayValues(state, aRecord); retVal = aRecord; break; case RecordTypeEnumeration.ArraySingleObject: aRecord = NewRecord <ArrayRecord>(state); state.typeInfos.Add(aRecord, BinaryTypeEnumeration.Object); ReadArrayLengths(state, aRecord); ReadArrayValues(state, aRecord); retVal = aRecord; break; case RecordTypeEnumeration.ArraySingleString: aRecord = NewRecord <ArrayRecord>(state); state.typeInfos.Add(aRecord, BinaryTypeEnumeration.String); ReadArrayLengths(state, aRecord); ReadArrayValues(state, aRecord); retVal = aRecord; break; case RecordTypeEnumeration.MethodCall: throw new NotImplementedException(); // TODO case RecordTypeEnumeration.MethodReturn: throw new NotImplementedException(); // TODO default: throw new InvalidOperationException("Unsupported record type: " + recType + "."); } } return(retVal); }
private static void WriteArrayRecord(SerializationState state, ArrayRecord array, Int32 id) { var rank = Math.Max(1, array.Rank); Type pType = null; // Default for empty arrays and arrays with just nulls is ArraySinglePrimitive var recType = RecordTypeEnumeration.ArraySinglePrimitive; var nonNullEncountered = false; foreach (var val in array.ValuesAsVector) { if (val != null || (val is PrimitiveWrapperRecord && ((PrimitiveWrapperRecord)val).Value != null)) { var valType = (val is PrimitiveWrapperRecord ? ((PrimitiveWrapperRecord)val).Value : val).GetType(); if (nonNullEncountered) { if ( (recType == RecordTypeEnumeration.ArraySingleString && !(val is String || val is StringRecord)) || (recType == RecordTypeEnumeration.ArraySinglePrimitive && !Object.Equals(pType, valType)) ) { recType = RecordTypeEnumeration.ArraySingleObject; } } else { recType = (val is String || val is StringRecord) ? RecordTypeEnumeration.ArraySingleString : ((!(val is AbstractRecord) || val is PrimitiveWrapperRecord) ? RecordTypeEnumeration.ArraySinglePrimitive : RecordTypeEnumeration.ArraySingleObject); if (recType == RecordTypeEnumeration.ArraySinglePrimitive) { pType = valType; } nonNullEncountered = true; } } if (recType == RecordTypeEnumeration.ArraySingleObject) { break; } } var recTypeToUse = BinaryArrayTypeEnumeration.Single == array.ArrayKind ? recType : RecordTypeEnumeration.BinaryArray; // Write information common for all arrays state.EnsureCapacity(9); state.array .WriteByteToBytes(ref state.idx, (Byte)recTypeToUse) .WriteInt32LEToBytes(ref state.idx, id); if (RecordTypeEnumeration.BinaryArray != recTypeToUse) { state.array.WriteInt32LEToBytes(ref state.idx, array.ValuesAsVector.Count); } state.WriteArrayToStream(); PrimitiveTypeEnumeration pEnum; switch (recTypeToUse) { case RecordTypeEnumeration.BinaryArray: var ak = array.ArrayKind; var cap = 7 + 4 * rank; // array type (1), rank (4), rank lengths (4 each) + type info (1) + possible primitive info var hasOffset = false; switch (array.ArrayKind) { case BinaryArrayTypeEnumeration.SingleOffset: case BinaryArrayTypeEnumeration.JaggedOffset: case BinaryArrayTypeEnumeration.RectangularOffset: hasOffset = true; cap += 4 * rank; // rank offsets (4 each); break; } state.EnsureCapacity(cap); state.array .WriteByteToBytes(ref state.idx, (Byte)RecordTypeEnumeration.BinaryArray) .WriteInt32LEToBytes(ref state.idx, rank); for (var i = 0; i < rank; ++i) { state.array.WriteInt32LEToBytes(ref state.idx, array.Lengths[i]); } if (hasOffset) { for (var i = 0; i < rank; ++i) { state.array.WriteInt32LEToBytes(ref state.idx, array.LowerBounds[i]); } } BinaryTypeEnumeration typeEnum; switch (recType) { case RecordTypeEnumeration.ArraySinglePrimitive: typeEnum = BinaryTypeEnumeration.Primitive; break; case RecordTypeEnumeration.ArraySingleObject: typeEnum = BinaryTypeEnumeration.Object; break; case RecordTypeEnumeration.ArraySingleString: typeEnum = BinaryTypeEnumeration.String; break; default: throw new InvalidOperationException("The code to detect array type has changed and this switch clause wasn't adjusted appropriately."); } state.array.WriteByteToBytes(ref state.idx, (Byte)typeEnum); pEnum = GetPrimitiveTypeFromType(pType); if (BinaryTypeEnumeration.Primitive == typeEnum) { state.array.WriteByteToBytes(ref state.idx, (Byte)pEnum); } state.WriteArrayToStream(); WriteArrayValues(state, array.ValuesAsVector, obj => { if (BinaryTypeEnumeration.Primitive == typeEnum) { WritePrimitive(state, obj, pEnum); } else { var rec = obj as AbstractRecord; if (rec == null) { if (obj is String) { rec = new StringRecord(); ((StringRecord)rec).StringValue = (String)obj; } else { rec = new PrimitiveWrapperRecord(); ((PrimitiveWrapperRecord)rec).Value = obj; } } WriteSingleRecord(state, rec, false); } }); break; // Serialize all information about array case RecordTypeEnumeration.ArraySinglePrimitive: state.EnsureCapacity(1); pEnum = GetPrimitiveTypeFromType(pType); state.array.WriteByteToBytes(ref state.idx, (Byte)pEnum); state.WriteArrayToStream(); WriteArrayValues(state, array.ValuesAsVector, obj => WritePrimitive(state, obj is PrimitiveWrapperRecord ? ((PrimitiveWrapperRecord)obj).Value : obj, pEnum)); break; case RecordTypeEnumeration.ArraySingleObject: WriteArrayValues(state, array.ValuesAsVector, obj => { var objRec = obj as AbstractRecord; if (objRec == null) { objRec = new PrimitiveWrapperRecord(); ((PrimitiveWrapperRecord)objRec).Value = obj; } WriteSingleRecord(state, objRec, false); }); break; case RecordTypeEnumeration.ArraySingleString: WriteArrayValues(state, array.ValuesAsVector, obj => { var str = obj as StringRecord; if (str == null) { str = new StringRecord(); str.StringValue = (String)obj; } WriteSingleRecord(state, str, false); }); break; } }