MessagePackProtocol.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. #if !BESTHTTP_DISABLE_SIGNALR_CORE && BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK
  2. using System;
  3. using System.Collections.Generic;
  4. using BestHTTP.PlatformSupport.Memory;
  5. using BestHTTP.SignalRCore.Messages;
  6. using GameDevWare.Serialization;
  7. using GameDevWare.Serialization.MessagePack;
  8. using GameDevWare.Serialization.Serializers;
  9. using UnityEngine;
  10. namespace BestHTTP.SignalRCore.Encoders
  11. {
  12. public sealed class MessagePackProtocolSerializationOptions
  13. {
  14. /// <summary>
  15. /// A function that must return a TypeSerializer for the given Type. To serialize an enum it can return an EnumNumberSerializer (default) to serialize enums as numbers or EnumSerializer to serialize them as strings.
  16. /// </summary>
  17. public Func<Type, TypeSerializer> EnumSerializerFactory;
  18. }
  19. /// <summary>
  20. /// IPRotocol implementation using the "Json & MessagePack Serialization" asset store package (https://assetstore.unity.com/packages/tools/network/json-messagepack-serialization-59918).
  21. /// </summary>
  22. public sealed class MessagePackProtocol : BestHTTP.SignalRCore.IProtocol
  23. {
  24. public string Name { get { return "messagepack"; } }
  25. public TransferModes Type { get { return TransferModes.Binary; } }
  26. public IEncoder Encoder { get; private set; }
  27. public HubConnection Connection { get; set; }
  28. public MessagePackProtocolSerializationOptions Options { get; set; }
  29. public MessagePackProtocol()
  30. : this(new MessagePackProtocolSerializationOptions { EnumSerializerFactory = (enumType) => new EnumNumberSerializer(enumType) })
  31. {
  32. }
  33. public MessagePackProtocol(MessagePackProtocolSerializationOptions options)
  34. {
  35. this.Options = options;
  36. GameDevWare.Serialization.Json.DefaultSerializers.Clear();
  37. GameDevWare.Serialization.Json.DefaultSerializers.AddRange(new TypeSerializer[]
  38. {
  39. new BinarySerializer(),
  40. new DateTimeOffsetSerializer(),
  41. new DateTimeSerializer(),
  42. new GuidSerializer(),
  43. new StreamSerializer(),
  44. new UriSerializer(),
  45. new VersionSerializer(),
  46. new TimeSpanSerializer(),
  47. new DictionaryEntrySerializer(),
  48. new BestHTTP.SignalRCore.Encoders.Vector2Serializer(),
  49. new BestHTTP.SignalRCore.Encoders.Vector3Serializer(),
  50. new BestHTTP.SignalRCore.Encoders.Vector4Serializer(),
  51. new PrimitiveSerializer(typeof (bool)),
  52. new PrimitiveSerializer(typeof (byte)),
  53. new PrimitiveSerializer(typeof (decimal)),
  54. new PrimitiveSerializer(typeof (double)),
  55. new PrimitiveSerializer(typeof (short)),
  56. new PrimitiveSerializer(typeof (int)),
  57. new PrimitiveSerializer(typeof (long)),
  58. new PrimitiveSerializer(typeof (sbyte)),
  59. new PrimitiveSerializer(typeof (float)),
  60. new PrimitiveSerializer(typeof (ushort)),
  61. new PrimitiveSerializer(typeof (uint)),
  62. new PrimitiveSerializer(typeof (ulong)),
  63. new PrimitiveSerializer(typeof (string)),
  64. });
  65. }
  66. /// <summary>
  67. /// This function must convert all element in the arguments array to the corresponding type from the argTypes array.
  68. /// </summary>
  69. public object[] GetRealArguments(Type[] argTypes, object[] arguments)
  70. {
  71. if (arguments == null || arguments.Length == 0)
  72. return null;
  73. if (argTypes.Length > arguments.Length)
  74. throw new Exception(string.Format("argType.Length({0}) < arguments.length({1})", argTypes.Length, arguments.Length));
  75. return arguments;
  76. }
  77. /// <summary>
  78. /// Convert a value to the given type.
  79. /// </summary>
  80. public object ConvertTo(Type toType, object obj)
  81. {
  82. if (obj == null)
  83. return null;
  84. #if NETFX_CORE
  85. TypeInfo typeInfo = toType.GetTypeInfo();
  86. #endif
  87. #if NETFX_CORE
  88. if (typeInfo.IsEnum)
  89. #else
  90. if (toType.IsEnum)
  91. #endif
  92. return Enum.Parse(toType, obj.ToString(), true);
  93. #if NETFX_CORE
  94. if (typeInfo.IsPrimitive)
  95. #else
  96. if (toType.IsPrimitive)
  97. #endif
  98. return Convert.ChangeType(obj, toType);
  99. if (toType == typeof(string))
  100. return obj.ToString();
  101. #if NETFX_CORE
  102. if (typeInfo.IsGenericType && toType.Name == "Nullable`1")
  103. return Convert.ChangeType(obj, toType.GenericTypeArguments[0]);
  104. #else
  105. if (toType.IsGenericType && toType.Name == "Nullable`1")
  106. return Convert.ChangeType(obj, toType.GetGenericArguments()[0]);
  107. #endif
  108. return obj;
  109. }
  110. /// <summary>
  111. /// This function must return the encoded representation of the given message.
  112. /// </summary>
  113. public BufferSegment EncodeMessage(Message message)
  114. {
  115. var memBuffer = BufferPool.Get(256, true);
  116. var stream = new BestHTTP.Extensions.BufferPoolMemoryStream(memBuffer, 0, memBuffer.Length, true, true, false, true);
  117. // Write 5 bytes for placeholder for length prefix
  118. stream.WriteByte(0);
  119. stream.WriteByte(0);
  120. stream.WriteByte(0);
  121. stream.WriteByte(0);
  122. stream.WriteByte(0);
  123. var buffer = BufferPool.Get(MsgPackWriter.DEFAULT_BUFFER_SIZE, true);
  124. var context = new SerializationContext {
  125. Options = SerializationOptions.SuppressTypeInformation,
  126. EnumSerializerFactory = this.Options.EnumSerializerFactory,
  127. ExtensionTypeHandler = CustomMessagePackExtensionTypeHandler.Instance
  128. };
  129. var writer = new MsgPackWriter(stream, context, buffer);
  130. switch (message.type)
  131. {
  132. case MessageTypes.StreamItem:
  133. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streamitem-message-encoding-1
  134. // [2, Headers, InvocationId, Item]
  135. writer.WriteArrayBegin(4);
  136. writer.WriteNumber(2);
  137. WriteHeaders(writer);
  138. writer.WriteString(message.invocationId);
  139. WriteValue(writer, message.item);
  140. writer.WriteArrayEnd();
  141. break;
  142. case MessageTypes.Completion:
  143. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#completion-message-encoding-1
  144. // [3, Headers, InvocationId, ResultKind, Result?]
  145. byte resultKind = (byte)(!string.IsNullOrEmpty(message.error) ? /*error*/ 1 : message.result != null ? /*non-void*/ 3 : /*void*/ 2);
  146. writer.WriteArrayBegin(resultKind == 2 ? 4 : 5);
  147. writer.WriteNumber(3);
  148. WriteHeaders(writer);
  149. writer.WriteString(message.invocationId);
  150. writer.WriteNumber(resultKind);
  151. if (resultKind == 1) // error
  152. writer.WriteString(message.error);
  153. else if (resultKind == 3) // non-void
  154. WriteValue(writer, message.result);
  155. writer.WriteArrayEnd();
  156. break;
  157. case MessageTypes.Invocation:
  158. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#invocation-message-encoding-1
  159. // [1, Headers, InvocationId, NonBlocking, Target, [Arguments], [StreamIds]]
  160. case MessageTypes.StreamInvocation:
  161. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streaminvocation-message-encoding-1
  162. // [4, Headers, InvocationId, Target, [Arguments], [StreamIds]]
  163. writer.WriteArrayBegin(message.streamIds != null ? 6 : 5);
  164. writer.WriteNumber((int)message.type);
  165. WriteHeaders(writer);
  166. writer.WriteString(message.invocationId);
  167. writer.WriteString(message.target);
  168. writer.WriteArrayBegin(message.arguments != null ? message.arguments.Length : 0);
  169. if (message.arguments != null)
  170. for (int i = 0; i < message.arguments.Length; ++i)
  171. WriteValue(writer, message.arguments[i]);
  172. writer.WriteArrayEnd();
  173. if (message.streamIds != null)
  174. {
  175. writer.WriteArrayBegin(message.streamIds.Length);
  176. for (int i = 0; i < message.streamIds.Length; ++i)
  177. WriteValue(writer, message.streamIds[i]);
  178. writer.WriteArrayEnd();
  179. }
  180. writer.WriteArrayEnd();
  181. break;
  182. case MessageTypes.CancelInvocation:
  183. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#cancelinvocation-message-encoding-1
  184. // [5, Headers, InvocationId]
  185. writer.WriteArrayBegin(3);
  186. writer.WriteNumber(5);
  187. WriteHeaders(writer);
  188. writer.WriteString(message.invocationId);
  189. writer.WriteArrayEnd();
  190. break;
  191. case MessageTypes.Ping:
  192. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#ping-message-encoding-1
  193. // [6]
  194. writer.WriteArrayBegin(1);
  195. writer.WriteNumber(6);
  196. writer.WriteArrayEnd();
  197. break;
  198. case MessageTypes.Close:
  199. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#close-message-encoding-1
  200. // [7, Error, AllowReconnect?]
  201. writer.WriteArrayBegin(string.IsNullOrEmpty(message.error) ? 1 : 2);
  202. writer.WriteNumber(7);
  203. if (!string.IsNullOrEmpty(message.error))
  204. writer.WriteString(message.error);
  205. writer.WriteArrayEnd();
  206. break;
  207. }
  208. writer.Flush();
  209. // release back the buffer we used for the MsgPackWriter
  210. BufferPool.Release(buffer);
  211. // get how much bytes got written to the buffer. This includes the 5 placeholder bytes too.
  212. int length = (int)stream.Position;
  213. // this is the length without the 5 placeholder bytes
  214. int contentLength = length - 5;
  215. // get the stream's internal buffer. We set the releaseBuffer flag to false, so we can use it safely.
  216. buffer = stream.GetBuffer();
  217. // add varint length prefix
  218. byte prefixBytes = GetRequiredBytesForLengthPrefix(contentLength);
  219. WriteLengthAsVarInt(buffer, 5 - prefixBytes, contentLength);
  220. // return with the final segment
  221. return new BufferSegment(buffer, 5 - prefixBytes, contentLength + prefixBytes);
  222. }
  223. private void WriteValue(MsgPackWriter writer, object value)
  224. {
  225. if (value == null)
  226. writer.WriteNull();
  227. else
  228. writer.WriteValue(value, value.GetType());
  229. }
  230. private void WriteHeaders(MsgPackWriter writer)
  231. {
  232. writer.WriteObjectBegin(0);
  233. writer.WriteObjectEnd();
  234. }
  235. /// <summary>
  236. /// This function must parse binary representation of the messages into the list of Messages.
  237. /// </summary>
  238. public void ParseMessages(BufferSegment segment, ref List<Message> messages)
  239. {
  240. messages.Clear();
  241. int offset = segment.Offset;
  242. while (offset < segment.Count)
  243. {
  244. int length = (int)ReadVarInt(segment.Data, ref offset);
  245. using (var stream = new System.IO.MemoryStream(segment.Data, offset, length))
  246. {
  247. var buff = BufferPool.Get(MsgPackReader.DEFAULT_BUFFER_SIZE, true);
  248. try
  249. {
  250. var context = new SerializationContext {
  251. Options = SerializationOptions.SuppressTypeInformation,
  252. ExtensionTypeHandler = CustomMessagePackExtensionTypeHandler.Instance
  253. };
  254. var reader = new MsgPackReader(stream, context, Endianness.BigEndian, buff);
  255. reader.NextToken();
  256. reader.NextToken();
  257. int messageType = reader.ReadByte();
  258. switch ((MessageTypes)messageType)
  259. {
  260. case MessageTypes.Invocation: messages.Add(ReadInvocation(reader)); break;
  261. case MessageTypes.StreamItem: messages.Add(ReadStreamItem(reader)); break;
  262. case MessageTypes.Completion: messages.Add(ReadCompletion(reader)); break;
  263. case MessageTypes.StreamInvocation: messages.Add(ReadStreamInvocation(reader)); break;
  264. case MessageTypes.CancelInvocation: messages.Add(ReadCancelInvocation(reader)); break;
  265. case MessageTypes.Ping:
  266. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#ping-message-encoding-1
  267. messages.Add(new Message { type = MessageTypes.Ping });
  268. break;
  269. case MessageTypes.Close: messages.Add(ReadClose(reader)); break;
  270. }
  271. reader.NextToken();
  272. }
  273. finally
  274. {
  275. BufferPool.Release(buff);
  276. }
  277. }
  278. offset += length;
  279. }
  280. }
  281. private Message ReadClose(MsgPackReader reader)
  282. {
  283. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#close-message-encoding-1
  284. string error = reader.ReadString();
  285. bool allowReconnect = false;
  286. try
  287. {
  288. allowReconnect = reader.ReadBoolean();
  289. }
  290. catch { }
  291. return new Message
  292. {
  293. type = MessageTypes.Close,
  294. error = error,
  295. allowReconnect = allowReconnect
  296. };
  297. }
  298. private Message ReadCancelInvocation(MsgPackReader reader)
  299. {
  300. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#cancelinvocation-message-encoding-1
  301. ReadHeaders(reader);
  302. string invocationId = reader.ReadString();
  303. return new Message
  304. {
  305. type = MessageTypes.CancelInvocation,
  306. invocationId = invocationId
  307. };
  308. }
  309. private Message ReadStreamInvocation(MsgPackReader reader)
  310. {
  311. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streaminvocation-message-encoding-1
  312. ReadHeaders(reader);
  313. string invocationId = reader.ReadString();
  314. string target = reader.ReadString();
  315. object[] arguments = ReadArguments(reader, target);
  316. string[] streamIds = ReadStreamIds(reader);
  317. return new Message
  318. {
  319. type = MessageTypes.StreamInvocation,
  320. invocationId = invocationId,
  321. target = target,
  322. arguments = arguments,
  323. streamIds = streamIds
  324. };
  325. }
  326. private Message ReadCompletion(MsgPackReader reader)
  327. {
  328. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#completion-message-encoding-1
  329. ReadHeaders(reader);
  330. string invocationId = reader.ReadString();
  331. byte resultKind = reader.ReadByte();
  332. switch(resultKind)
  333. {
  334. // 1 - Error result - Result contains a String with the error message
  335. case 1:
  336. string error = reader.ReadString();
  337. return new Message
  338. {
  339. type = MessageTypes.Completion,
  340. invocationId = invocationId,
  341. error = error
  342. };
  343. // 2 - Void result - Result is absent
  344. case 2:
  345. return new Message
  346. {
  347. type = MessageTypes.Completion,
  348. invocationId = invocationId
  349. };
  350. // 3 - Non-Void result - Result contains the value returned by the server
  351. case 3:
  352. object item = ReadItem(reader, invocationId);
  353. return new Message
  354. {
  355. type = MessageTypes.Completion,
  356. invocationId = invocationId,
  357. item = item,
  358. result = item
  359. };
  360. default:
  361. throw new NotImplementedException("Unknown resultKind: " + resultKind);
  362. }
  363. }
  364. private Message ReadStreamItem(MsgPackReader reader)
  365. {
  366. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streamitem-message-encoding-1
  367. ReadHeaders(reader);
  368. string invocationId = reader.ReadString();
  369. object item = ReadItem(reader, invocationId);
  370. return new Message
  371. {
  372. type = MessageTypes.StreamItem,
  373. invocationId = invocationId,
  374. item = item
  375. };
  376. }
  377. private Message ReadInvocation(MsgPackReader reader)
  378. {
  379. // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#invocation-message-encoding-1
  380. ReadHeaders(reader);
  381. string invocationId = reader.ReadString();
  382. string target = reader.ReadString();
  383. object[] arguments = ReadArguments(reader, target);
  384. string[] streamIds = ReadStreamIds(reader);
  385. return new Message
  386. {
  387. type = MessageTypes.Invocation,
  388. invocationId = invocationId,
  389. target = target,
  390. arguments = arguments,
  391. streamIds = streamIds
  392. };
  393. }
  394. private object ReadItem(MsgPackReader reader, string invocationId)
  395. {
  396. long longId = 0;
  397. if (long.TryParse(invocationId, out longId))
  398. {
  399. Type itemType = this.Connection.GetItemType(longId);
  400. return reader.ReadValue(itemType);
  401. }
  402. else
  403. return reader.ReadValue(typeof(object));
  404. }
  405. private string[] ReadStreamIds(MsgPackReader reader)
  406. {
  407. return reader.ReadValue(typeof(string[])) as string[];
  408. }
  409. private object[] ReadArguments(MsgPackReader reader, string target)
  410. {
  411. var subscription = this.Connection.GetSubscription(target);
  412. object[] args;
  413. if (subscription == null || subscription.callbacks == null || subscription.callbacks.Count == 0)
  414. {
  415. args = reader.ReadValue(typeof(object[])) as object[];
  416. }
  417. else
  418. {
  419. reader.NextToken();
  420. if (subscription.callbacks[0].ParamTypes != null)
  421. {
  422. args = new object[subscription.callbacks[0].ParamTypes.Length];
  423. for (int i = 0; i < subscription.callbacks[0].ParamTypes.Length; ++i)
  424. args[i] = reader.ReadValue(subscription.callbacks[0].ParamTypes[i]);
  425. }
  426. else
  427. args = null;
  428. reader.NextToken();
  429. }
  430. return args;
  431. }
  432. private Dictionary<string, string> ReadHeaders(MsgPackReader reader)
  433. {
  434. return reader.ReadValue(typeof(Dictionary<string, string>)) as Dictionary<string, string>;
  435. }
  436. public static byte GetRequiredBytesForLengthPrefix(int length)
  437. {
  438. byte bytes = 0;
  439. do
  440. {
  441. length >>= 7;
  442. bytes++;
  443. }
  444. while (length > 0);
  445. return bytes;
  446. }
  447. public static int WriteLengthAsVarInt(byte[] data, int offset, int length)
  448. {
  449. do
  450. {
  451. var current = data[offset];
  452. current = (byte)(length & 0x7f);
  453. length >>= 7;
  454. if (length > 0)
  455. {
  456. current |= 0x80;
  457. }
  458. data[offset++] = current;
  459. }
  460. while (length > 0);
  461. return offset;
  462. }
  463. public static uint ReadVarInt(byte[] data, ref int offset)
  464. {
  465. var length = 0U;
  466. var numBytes = 0;
  467. byte byteRead;
  468. do
  469. {
  470. byteRead = data[offset + numBytes];
  471. length = length | (((uint)(byteRead & 0x7f)) << (numBytes * 7));
  472. numBytes++;
  473. }
  474. while (offset + numBytes < data.Length && ((byteRead & 0x80) != 0));
  475. offset += numBytes;
  476. return length;
  477. }
  478. }
  479. public sealed class CustomMessagePackExtensionTypeHandler : MessagePackExtensionTypeHandler
  480. {
  481. public const int EXTENSION_TYPE_DATE_TIME = -1;
  482. public const int DATE_TIME_SIZE = 8;
  483. public const long BclSecondsAtUnixEpoch = 62135596800;
  484. public const int NanosecondsPerTick = 100;
  485. public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
  486. private static readonly Type[] DefaultExtensionTypes = new[] { typeof(DateTime) };
  487. public static CustomMessagePackExtensionTypeHandler Instance = new CustomMessagePackExtensionTypeHandler();
  488. public override IEnumerable<Type> ExtensionTypes
  489. {
  490. get { return DefaultExtensionTypes; }
  491. }
  492. public override bool TryRead(sbyte type, ArraySegment<byte> data, out object value)
  493. {
  494. if (data.Array == null) throw new ArgumentNullException("data");
  495. value = default(object);
  496. switch (type)
  497. {
  498. case EXTENSION_TYPE_DATE_TIME:
  499. switch (data.Count)
  500. {
  501. case 4:
  502. {
  503. var intValue = unchecked((int)(FromBytes(data.Array, data.Offset, 4)));
  504. value = CustomMessagePackExtensionTypeHandler.UnixEpoch.AddSeconds(unchecked((uint)intValue));
  505. return true;
  506. }
  507. case 8:
  508. {
  509. long longValue = FromBytes(data.Array, data.Offset, 8);
  510. ulong ulongValue = unchecked((ulong)longValue);
  511. long nanoseconds = (long)(ulongValue >> 34);
  512. ulong seconds = ulongValue & 0x00000003ffffffffL;
  513. value = CustomMessagePackExtensionTypeHandler.UnixEpoch.AddSeconds(seconds).AddTicks(nanoseconds / CustomMessagePackExtensionTypeHandler.NanosecondsPerTick);
  514. return true;
  515. }
  516. case 12:
  517. {
  518. var intValue = unchecked((int)(FromBytes(data.Array, data.Offset, 4)));
  519. long longValue = FromBytes(data.Array, data.Offset, 8);
  520. var nanoseconds = unchecked((uint)intValue);
  521. value = CustomMessagePackExtensionTypeHandler.UnixEpoch.AddSeconds(longValue).AddTicks(nanoseconds / CustomMessagePackExtensionTypeHandler.NanosecondsPerTick);
  522. return true;
  523. }
  524. default:
  525. throw new Exception($"Length of extension was {data.Count}. Either 4, 8 or 12 were expected.");
  526. }
  527. default:
  528. return false;
  529. }
  530. }
  531. public override bool TryWrite(object value, out sbyte type, ref ArraySegment<byte> data)
  532. {
  533. if (value == null)
  534. {
  535. type = 0;
  536. return false;
  537. }
  538. else if (value is DateTime)
  539. {
  540. type = EXTENSION_TYPE_DATE_TIME;
  541. var dateTime = (DateTime)(object)value;
  542. // The spec requires UTC. Convert to UTC if we're sure the value was expressed as Local time.
  543. // If it's Unspecified, we want to leave it alone since .NET will change the value when we convert
  544. // and we simply don't know, so we should leave it as-is.
  545. if (dateTime.Kind == DateTimeKind.Local)
  546. {
  547. dateTime = dateTime.ToUniversalTime();
  548. }
  549. var secondsSinceBclEpoch = dateTime.Ticks / TimeSpan.TicksPerSecond;
  550. var seconds = secondsSinceBclEpoch - CustomMessagePackExtensionTypeHandler.BclSecondsAtUnixEpoch;
  551. var nanoseconds = (dateTime.Ticks % TimeSpan.TicksPerSecond) * CustomMessagePackExtensionTypeHandler.NanosecondsPerTick;
  552. if ((seconds >> 34) == 0)
  553. {
  554. var data64 = unchecked((ulong)((nanoseconds << 34) | seconds));
  555. if ((data64 & 0xffffffff00000000L) == 0)
  556. {
  557. // timestamp 32(seconds in 32-bit unsigned int)
  558. var data32 = (UInt32)data64;
  559. const int TIMESTAMP_SIZE = 4;
  560. if (data.Array == null || data.Count < TIMESTAMP_SIZE)
  561. data = new ArraySegment<byte>(new byte[TIMESTAMP_SIZE]);
  562. CopyBytesImpl(data32, 4, data.Array, data.Offset);
  563. if (data.Count != DATE_TIME_SIZE)
  564. data = new ArraySegment<byte>(data.Array, data.Offset, DATE_TIME_SIZE);
  565. }
  566. else
  567. {
  568. // timestamp 64(nanoseconds in 30-bit unsigned int | seconds in 34-bit unsigned int)
  569. const int TIMESTAMP_SIZE = 8;
  570. if (data.Array == null || data.Count < TIMESTAMP_SIZE)
  571. data = new ArraySegment<byte>(new byte[TIMESTAMP_SIZE]);
  572. CopyBytesImpl(unchecked((long)data64), 8, data.Array, data.Offset);
  573. if (data.Count != DATE_TIME_SIZE)
  574. data = new ArraySegment<byte>(data.Array, data.Offset, DATE_TIME_SIZE);
  575. }
  576. }
  577. else
  578. {
  579. // timestamp 96( nanoseconds in 32-bit unsigned int | seconds in 64-bit signed int )
  580. const int TIMESTAMP_SIZE = 12;
  581. if (data.Array == null || data.Count < TIMESTAMP_SIZE)
  582. data = new ArraySegment<byte>(new byte[TIMESTAMP_SIZE]);
  583. CopyBytesImpl((uint)nanoseconds, 4, data.Array, data.Offset);
  584. CopyBytesImpl(seconds, 8, data.Array, data.Offset + 4);
  585. if (data.Count != DATE_TIME_SIZE)
  586. data = new ArraySegment<byte>(data.Array, data.Offset, DATE_TIME_SIZE);
  587. }
  588. return true;
  589. }
  590. type = default(sbyte);
  591. return false;
  592. }
  593. private void CopyBytesImpl(long value, int bytes, byte[] buffer, int index)
  594. {
  595. var endOffset = index + bytes - 1;
  596. for (var i = 0; i < bytes; i++)
  597. {
  598. buffer[endOffset - i] = unchecked((byte)(value & 0xff));
  599. value = value >> 8;
  600. }
  601. }
  602. private long FromBytes(byte[] buffer, int startIndex, int bytesToConvert)
  603. {
  604. long ret = 0;
  605. for (var i = 0; i < bytesToConvert; i++)
  606. {
  607. ret = unchecked((ret << 8) | buffer[startIndex + i]);
  608. }
  609. return ret;
  610. }
  611. }
  612. // https://github.com/neuecc/MessagePack-CSharp/blob/13c299a5172c60154bae53395612af194c02d286/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Unity/Formatters.cs#L15
  613. public sealed class Vector2Serializer : TypeSerializer
  614. {
  615. public override Type SerializedType { get { return typeof(Vector2); } }
  616. public override object Deserialize(IJsonReader reader)
  617. {
  618. if (reader == null) throw new ArgumentNullException("reader");
  619. if (reader.Token == JsonToken.Null)
  620. return null;
  621. var value = new Vector2();
  622. reader.ReadArrayBegin();
  623. int idx = 0;
  624. while (reader.Token != JsonToken.EndOfArray)
  625. value[idx++] = reader.ReadSingle();
  626. reader.ReadArrayEnd(nextToken: false);
  627. return value;
  628. }
  629. public override void Serialize(IJsonWriter writer, object value)
  630. {
  631. if (writer == null) throw new ArgumentNullException("writer");
  632. if (value == null) throw new ArgumentNullException("value");
  633. var vector2 = (Vector2)value;
  634. writer.WriteArrayBegin(2);
  635. writer.Write(vector2.x);
  636. writer.Write(vector2.y);
  637. writer.WriteArrayEnd();
  638. }
  639. }
  640. // https://github.com/neuecc/MessagePack-CSharp/blob/13c299a5172c60154bae53395612af194c02d286/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Unity/Formatters.cs#L56
  641. public sealed class Vector3Serializer : TypeSerializer
  642. {
  643. public override Type SerializedType { get { return typeof(Vector3); } }
  644. public override object Deserialize(IJsonReader reader)
  645. {
  646. if (reader == null) throw new ArgumentNullException("reader");
  647. if (reader.Token == JsonToken.Null)
  648. return null;
  649. var value = new Vector3();
  650. reader.ReadArrayBegin();
  651. int idx = 0;
  652. while (reader.Token != JsonToken.EndOfArray)
  653. value[idx++] = reader.ReadSingle();
  654. reader.ReadArrayEnd(nextToken: false);
  655. return value;
  656. }
  657. public override void Serialize(IJsonWriter writer, object value)
  658. {
  659. if (writer == null) throw new ArgumentNullException("writer");
  660. if (value == null) throw new ArgumentNullException("value");
  661. var vector3 = (Vector3)value;
  662. writer.WriteArrayBegin(3);
  663. writer.Write(vector3.x);
  664. writer.Write(vector3.y);
  665. writer.Write(vector3.z);
  666. writer.WriteArrayEnd();
  667. }
  668. }
  669. // https://github.com/neuecc/MessagePack-CSharp/blob/13c299a5172c60154bae53395612af194c02d286/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Unity/Formatters.cs#L102
  670. public sealed class Vector4Serializer : TypeSerializer
  671. {
  672. public override Type SerializedType { get { return typeof(Vector4); } }
  673. public override object Deserialize(IJsonReader reader)
  674. {
  675. if (reader == null) throw new ArgumentNullException("reader");
  676. if (reader.Token == JsonToken.Null)
  677. return null;
  678. var value = new Vector4();
  679. reader.ReadArrayBegin();
  680. int idx = 0;
  681. while (reader.Token != JsonToken.EndOfArray)
  682. value[idx++] = reader.ReadSingle();
  683. reader.ReadArrayEnd(nextToken: false);
  684. return value;
  685. }
  686. public override void Serialize(IJsonWriter writer, object value)
  687. {
  688. if (writer == null) throw new ArgumentNullException("writer");
  689. if (value == null) throw new ArgumentNullException("value");
  690. var vector4 = (Vector4)value;
  691. writer.WriteArrayBegin(4);
  692. writer.Write(vector4.x);
  693. writer.Write(vector4.y);
  694. writer.Write(vector4.z);
  695. writer.Write(vector4.z);
  696. writer.WriteArrayEnd();
  697. }
  698. }
  699. }
  700. #endif