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; } }
private Boolean DoesEqual(StringRecord rec) { return(rec != null && String.Equals(rec.StringValue, this.StringValue)); }
private static void WriteClassRecord(SerializationState state, ClassRecord claas, Int32 id) { var metaDataKey = Tuple.Create(claas.AssemblyName, claas.TypeName); Int32 otherID; if (state.serializedObjects.TryGetValue(metaDataKey, out otherID)) { // Another record of the same type was serialized earlier, can use previous info state.EnsureCapacity(9); state.array .WriteByteToBytes(ref state.idx, (Byte)RecordTypeEnumeration.ClassWithID) .WriteInt32LEToBytes(ref state.idx, id) .WriteInt32LEToBytes(ref state.idx, otherID); state.WriteArrayToStream(); } else { var isSystem = claas.AssemblyName == null; var nameByteCount = SafeByteCount(claas.TypeName); state.EnsureCapacity(14 + nameByteCount); // class type (1), id (4), space for class name length (max 5), member count (4) state.array .WriteByteToBytes(ref state.idx, (Byte)(isSystem ? RecordTypeEnumeration.SystemClassWithMembersAndTypes : RecordTypeEnumeration.ClassWithMembersAndTypes)) .WriteInt32LEToBytes(ref state.idx, id) .WriteInt32Encoded7Bit(ref state.idx, nameByteCount) .WriteStringToBytes(ref state.idx, UTF8, claas.TypeName) .WriteInt32LEToBytes(ref state.idx, claas.Members.Count); state.WriteArrayToStream(); // Write member names foreach (var member in claas.Members) { nameByteCount = SafeByteCount(member.Name); state.EnsureCapacity(5 + nameByteCount); state.array .WriteInt32Encoded7Bit(ref state.idx, nameByteCount) .WriteStringToBytes(ref state.idx, UTF8, member.Name); state.WriteArrayToStream(); } // Write member type infos state.EnsureCapacity(claas.Members.Count); var mTypeCodes = new List <Tuple <BinaryTypeEnumeration, PrimitiveTypeEnumeration> >(claas.Members.Count); foreach (var member in claas.Members) { PrimitiveTypeEnumeration pType; var bt = GetTypeInfo(member.Value, out pType); mTypeCodes.Add(Tuple.Create(bt, pType)); state.array.WriteByteToBytes(ref state.idx, (Byte)bt); } state.WriteArrayToStream(); // Write additional type info where applicable for (var i = 0; i < mTypeCodes.Count; ++i) { var member = claas.Members[i]; var tuple = mTypeCodes[i]; switch (tuple.Item1) { case BinaryTypeEnumeration.Primitive: state.EnsureCapacity(1); state.array.WriteByteToBytes(ref state.idx, (Byte)tuple.Item2); break; case BinaryTypeEnumeration.SystemClass: nameByteCount = SafeByteCount(member.TypeName); state.EnsureCapacity(5 + nameByteCount); state.array .WriteInt32Encoded7Bit(ref state.idx, nameByteCount) .WriteStringToBytes(ref state.idx, UTF8, member.TypeName); break; case BinaryTypeEnumeration.Class: nameByteCount = SafeByteCount(member.TypeName); state.EnsureCapacity(9 + nameByteCount); state.array .WriteInt32Encoded7Bit(ref state.idx, nameByteCount) .WriteStringToBytes(ref state.idx, UTF8, member.TypeName) .WriteInt32LEToBytes(ref state.idx, state.assemblies[member.AssemblyName]); break; case BinaryTypeEnumeration.PrimitiveArray: state.EnsureCapacity(1); state.array .WriteByteToBytes(ref state.idx, (Byte)tuple.Item2); break; } state.WriteArrayToStream(); } // Write this class assembly name if needed if (!isSystem) { state.EnsureCapacity(4); state.array.WriteInt32LEToBytes(ref state.idx, state.assemblies[claas.AssemblyName]); state.WriteArrayToStream(); } // Write member values for (var i = 0; i < mTypeCodes.Count; ++i) { var member = claas.Members[i]; var val = member.Value; // Change raw string values to StringRecords to enable caching strings by id if (val is String) { var sRec = new StringRecord(); sRec.StringValue = (String)val; val = sRec; } var rec = val as AbstractRecord; if (rec != null) { // All arrays and non-structs are serialized afterwards. if (rec is ArrayRecord || (rec is ClassRecord && !((ClassRecord)rec).IsSerializedInPlace)) { // Add to mapping before calling recursively in order to create MemberReference if (state.TryAddRecord(rec, out id)) { // The record hasn't been serialized, add to queue state.recordQueue.Enqueue(rec); } } // Write the record WriteSingleRecord(state, rec, false); } else { // Write value as primitive WritePrimitive(state, val, mTypeCodes[i].Item2); } } } if (!state.serializedObjects.ContainsKey(metaDataKey)) { state.serializedObjects.Add(metaDataKey, id); } }