private static XElement BuildElementXML(object root) { if (root == null) { throw new PlistException("unsupported null"); } else if (root is bool) { if ((bool)root) { return(new XElement("true")); } else { return(new XElement("false")); } } else if (root is int) { return(new XElement("integer", root.ToString())); } else if (root is double) { return(new XElement("real", root.ToString())); } else if (root is DateTime) { DateTime date = (DateTime)root; var str = date.ToUniversalTime().ToString(DateFormat, System.Globalization.CultureInfo.InvariantCulture); return(new XElement("date", str)); } else if (root is string) { string text = root as string; return(new XElement("string", text)); } else if (root is byte[]) { byte[] data = root as byte[]; var str = System.Convert.ToBase64String(data); return(new XElement("data", str)); } else if (root is PlistList) { PlistList objectArray = root as PlistList; object[] contents = new object[objectArray.Count]; for (int i = 0; i < objectArray.Count; ++i) { contents[i] = BuildElementXML(objectArray[i]); } return(new XElement("array", contents)); } else if (root is PlistDictionary) { PlistDictionary objectDictionary = root as PlistDictionary; object[] contents = new object[objectDictionary.Count * 2]; int i = 0; foreach (var kv in objectDictionary) { contents[i++] = new XElement("key", kv.Key); contents[i++] = BuildElementXML(kv.Value); } return(new XElement("dict", contents)); } else { throw new PlistException("undefined data type"); } }
private static ulong WriteObjectBinary(Stream stream, object root, byte objectRefSize, byte[] encodeBuffer, ref ulong objectIndex, List <ulong> offsetTable) { ulong objectRef = objectIndex++; ulong objectAt = (ulong)stream.Position; offsetTable.Add(objectAt); if (root == null) { throw new PlistException("unsupported null"); } else if (root is bool) { if ((bool)root) { stream.WriteByte(TypeBitsBoolOrNil | TrueBit); } else { stream.WriteByte(TypeBitsBoolOrNil | FalseBit); } } else if (root is int) { WriteInteger((int)root, stream); } else if (root is double) { // real, 2^3 = 8 byte // 0010 0011 // force use double byte headbyte = TypeBitsReal | 0x03; stream.WriteByte(headbyte); var f64 = new Float64Bits((double)root); f64.Write(stream); } else if (root is DateTime) { DateTime date = (DateTime)root; TimeSpan elapsedSpan = date.ToUniversalTime() - BaseTime; double elapsed = elapsedSpan.TotalSeconds; byte headbyte = TypeBitsDate | 0x03; stream.WriteByte(headbyte); var f64 = new Float64Bits(elapsed); f64.Write(stream); } else if (root is string) { string text = root as string; byte typebit; int count; int writeBytes; if (IsASCIIEncodable(text)) { typebit = TypeBitsASCIIString; writeBytes = ASCII.GetBytes(text, 0, text.Length, encodeBuffer, 0); count = writeBytes; } else { typebit = TypeBitsUTF16String; writeBytes = UTF16BE.GetBytes(text, 0, text.Length, encodeBuffer, 0); count = writeBytes >> 1; } if (count < 15) { byte headbyte = (byte)(typebit | count); stream.WriteByte(headbyte); } else { byte headbyte = (byte)(typebit | 0x0F); stream.WriteByte(headbyte); WriteInteger((long)count, stream); } stream.Write(encodeBuffer, 0, writeBytes); } else if (root is byte[]) { byte[] data = root as byte[]; byte typebit = TypeBitsBinaryData; if (data.Length < 15) { byte headbyte = (byte)(typebit | data.Length); stream.WriteByte(headbyte); } else { byte headbyte = (byte)(typebit | 0x0F); stream.WriteByte(headbyte); WriteInteger(data.LongLength, stream); } stream.Write(data, 0, data.Length); } else if (root is PlistList) { PlistList objectArray = root as PlistList; byte typebit = TypeBitsArray; if (objectArray.Count < 15) { byte headbyte = (byte)(typebit | objectArray.Count); stream.WriteByte(headbyte); } else { byte headbyte = (byte)(typebit | 0x0F); stream.WriteByte(headbyte); WriteInteger(objectArray.Count, stream); } ulong arrayAt = (ulong)stream.Position; // reserve data ulong reserveBytes = (ulong)objectArray.Count * objectRefSize; for (ulong i = 0; i < reserveBytes; ++i) { stream.WriteByte(0); } for (int i = 0; i < objectArray.Count; ++i) { ulong arrayObjectRef = WriteObjectBinary(stream, objectArray[i], objectRefSize, encodeBuffer, ref objectIndex, offsetTable); stream.Seek((long)arrayAt + i * objectRefSize, SeekOrigin.Begin); BigEndianWriter.WriteNBytesUnsignedInteger(stream, arrayObjectRef, objectRefSize); stream.Seek(0, SeekOrigin.End); } } else if (root is PlistDictionary) { PlistDictionary objectDictionary = root as PlistDictionary; byte typebit = TypeBitsDictionary; if (objectDictionary.Count < 15) { byte headbyte = (byte)(typebit | objectDictionary.Count); stream.WriteByte(headbyte); } else { byte headbyte = (byte)(typebit | 0x0F); stream.WriteByte(headbyte); WriteInteger(objectDictionary.Count, stream); } ulong dictionaryAt = (ulong)stream.Position; // reserve data ulong reserveBytes = (ulong)objectDictionary.Count * 2 * objectRefSize; for (ulong j = 0; j < reserveBytes; ++j) { stream.WriteByte(0); } /* key, key, key, value, value, value... */ long keyBytes = objectRefSize * (long)objectDictionary.Count; int i = 0; foreach (var objectKeyValue in objectDictionary) { ulong arrayKeyRef = WriteObjectBinary(stream, objectKeyValue.Key, objectRefSize, encodeBuffer, ref objectIndex, offsetTable); ulong arrayValueRef = WriteObjectBinary(stream, objectKeyValue.Value, objectRefSize, encodeBuffer, ref objectIndex, offsetTable); stream.Seek((long)dictionaryAt + i * objectRefSize, SeekOrigin.Begin); BigEndianWriter.WriteNBytesUnsignedInteger(stream, arrayKeyRef, objectRefSize); stream.Seek((long)dictionaryAt + keyBytes + i * objectRefSize, SeekOrigin.Begin); BigEndianWriter.WriteNBytesUnsignedInteger(stream, arrayValueRef, objectRefSize); stream.Seek(0, SeekOrigin.End); i++; } } else { throw new PlistException(); } return(objectRef); }
public static bool EqualObject(object x, object y, double deltaReal, double deltaDateSeconds) { if (x.GetType() != y.GetType()) { return(false); } if (x is bool || x is int || x is string) { bool eq = x.Equals(y); return(eq); } if (x is byte[]) { byte[] xbyte = x as byte[]; byte[] ybyte = y as byte[]; if (xbyte.Length != ybyte.Length) { return(false); } for (int i = 0; i < xbyte.Length; ++i) { if (xbyte[i] != ybyte[i]) { return(false); } } return(true); // bool eq = (x as byte[]).SequenceEqual(y as byte[]); // return eq; } if (x is double) { bool eq = Math.Abs((double)x - (double)y) < deltaReal; return(eq); } if (x is DateTime) { var deltaTime = (DateTime)x - (DateTime)y; bool eq = Math.Abs(deltaTime.TotalSeconds) < deltaDateSeconds; return(eq); } if (x is PlistList) { PlistList xList = x as PlistList; PlistList yList = y as PlistList; if (xList.Count != yList.Count) { return(false); } for (int i = 0; i < xList.Count; ++i) { if (EqualObject(xList[i], yList[i], deltaReal, deltaDateSeconds) == false) { return(false); } } return(true); } if (x is PlistDictionary) { PlistDictionary xDict = x as PlistDictionary; PlistDictionary yDict = y as PlistDictionary; if (xDict.Count != yDict.Count) { return(false); } foreach (var kv in xDict) { if (yDict.ContainsKey(kv.Key) == false) { return(false); } var yValue = yDict[kv.Key]; if (EqualObject(kv.Value, yValue, deltaReal, deltaDateSeconds) == false) { return(false); } } return(true); } throw new PlistException("undefined data type"); }
private static object ReadObjectBinary(byte[] bytes, ref Trailer trailer, ulong objectRef) { ulong objectAt = ReadOffset(bytes, trailer, objectRef); byte headbyte = bytes[objectAt]; int type = headbyte & 0xF0; int nnnn = headbyte & 0x0F; switch (type) { case TypeBitsBoolOrNil: /* 0000 null, false, true*/ switch (nnnn) { case 0x0: throw new PlistException("unsupported null"); case FalseBit: return(false); case TrueBit: return(true); default: throw new PlistException("undefined value"); } case TypeBitsInteger: /* 0001 integer */ { ulong nextAt; long integer = ReadInteger(bytes, objectAt, out nextAt); if (integer < int.MinValue || int.MaxValue < integer) { throw new PlistException("int overflow, we use int for simplity."); } return((int)integer); } case TypeBitsReal: /* 0010 real */ { int nnnn_bytes = TwoPowNNNN(nnnn); var real = BigEndianReader.ReadNBytesReal(bytes, nnnn_bytes, objectAt + 1); return(real); } case TypeBitsDate: /* 0011 date */ { double elapsed = BigEndianReader.ReadNBytesReal(bytes, 8, objectAt + 1); var span = TimeSpan.FromSeconds(elapsed); DateTime date = BaseTime.Add(span).ToLocalTime(); return(date); } case TypeBitsBinaryData: /* 0100 data */ { ulong dataLength; ulong dataAt; IntNNNNorNextInt(bytes, objectAt, nnnn, out dataLength, out dataAt); byte[] data = new byte[dataLength]; Array.Copy(bytes, (long)dataAt, data, 0, (long)dataLength); return(data); } case TypeBitsASCIIString: /* 0101 ascii string */ { ulong asciiCount; ulong asciiAt; IntNNNNorNextInt(bytes, objectAt, nnnn, out asciiCount, out asciiAt); string ascii; if (asciiAt + asciiCount < int.MaxValue) { ascii = System.Text.Encoding.ASCII.GetString(bytes, (int)asciiAt, (int)asciiCount); } else { byte[] asciiBytes = new byte[asciiCount]; Array.Copy(bytes, (long)asciiAt, asciiBytes, 0, (long)asciiCount); ascii = System.Text.Encoding.ASCII.GetString(asciiBytes, 0, (int)asciiBytes.Length); } return(ascii); } case TypeBitsUTF16String: /* 0110 utf16 string */ { ulong utf16Count; ulong utf16At; IntNNNNorNextInt(bytes, objectAt, nnnn, out utf16Count, out utf16At); ulong utf16byteLength = utf16Count * 2; string utf16; if (utf16At + utf16byteLength < int.MaxValue) { utf16 = UTF16BE.GetString(bytes, (int)utf16At, (int)utf16byteLength); } else { byte[] utf16Bytes = new byte[utf16byteLength]; Array.Copy(bytes, (long)utf16At, utf16Bytes, 0, (long)utf16byteLength); utf16 = UTF16BE.GetString(utf16Bytes, 0, utf16Bytes.Length); } return(utf16); } case TypeBitsArray: /* 1010 array */ { ulong count; ulong arrayAt; IntNNNNorNextInt(bytes, objectAt, nnnn, out count, out arrayAt); int capacity = (int)(Math.Min(count, int.MaxValue)); PlistList objectArray = new PlistList(capacity); for (ulong i = 0; i < count; ++i) { ulong arrayObjectRef = BigEndianReader.ReadNBytesUnsignedInteger(bytes, trailer.ObjectRefSize, arrayAt + trailer.ObjectRefSize * i); objectArray.Add(ReadObjectBinary(bytes, ref trailer, arrayObjectRef)); } return(objectArray); } case TypeBitsDictionary: /* 1101 dictionary */ { ulong count; ulong dictionaryAt; IntNNNNorNextInt(bytes, objectAt, nnnn, out count, out dictionaryAt); int capacity = (int)(Math.Min(count, int.MaxValue)); PlistDictionary objectDictionary = new PlistDictionary(capacity); /* key, key, key, value, value, value... */ ulong keyBytes = trailer.ObjectRefSize * count; for (ulong i = 0; i < count; ++i) { ulong keyObjectRef = BigEndianReader.ReadNBytesUnsignedInteger(bytes, trailer.ObjectRefSize, dictionaryAt + trailer.ObjectRefSize * i); ulong valueObjectRef = BigEndianReader.ReadNBytesUnsignedInteger(bytes, trailer.ObjectRefSize, dictionaryAt + keyBytes + trailer.ObjectRefSize * i); string keyObject = ReadObjectBinary(bytes, ref trailer, keyObjectRef) as string; object valueObject = ReadObjectBinary(bytes, ref trailer, valueObjectRef); objectDictionary.Add(keyObject, valueObject); } return(objectDictionary); } default: throw new PlistException("undefined data type"); } }