You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

325 lines
11 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. using System;
  26. using System.Globalization;
  27. using System.IO;
  28. using System.Text;
  29. using Newtonsoft.Json.Utilities;
  30. namespace Newtonsoft.Json.Bson
  31. {
  32. internal class BsonBinaryWriter
  33. {
  34. private static readonly Encoding Encoding = new UTF8Encoding(false);
  35. private readonly BinaryWriter _writer;
  36. private byte[] _largeByteBuffer;
  37. public DateTimeKind DateTimeKindHandling { get; set; }
  38. public BsonBinaryWriter(BinaryWriter writer)
  39. {
  40. DateTimeKindHandling = DateTimeKind.Utc;
  41. _writer = writer;
  42. }
  43. public void Flush()
  44. {
  45. _writer.Flush();
  46. }
  47. public void Close()
  48. {
  49. _writer.Close();
  50. //_writer.Dispose();
  51. }
  52. public void WriteToken(BsonToken t)
  53. {
  54. CalculateSize(t);
  55. WriteTokenInternal(t);
  56. }
  57. private void WriteTokenInternal(BsonToken t)
  58. {
  59. switch (t.Type)
  60. {
  61. case BsonType.Object:
  62. {
  63. BsonObject value = (BsonObject)t;
  64. _writer.Write(value.CalculatedSize);
  65. foreach (BsonProperty property in value)
  66. {
  67. _writer.Write((sbyte)property.Value.Type);
  68. WriteString((string)property.Name.Value, property.Name.ByteCount, null);
  69. WriteTokenInternal(property.Value);
  70. }
  71. _writer.Write((byte)0);
  72. }
  73. break;
  74. case BsonType.Array:
  75. {
  76. BsonArray value = (BsonArray)t;
  77. _writer.Write(value.CalculatedSize);
  78. ulong index = 0;
  79. foreach (BsonToken c in value)
  80. {
  81. _writer.Write((sbyte)c.Type);
  82. WriteString(index.ToString(CultureInfo.InvariantCulture), MathUtils.IntLength(index), null);
  83. WriteTokenInternal(c);
  84. index++;
  85. }
  86. _writer.Write((byte)0);
  87. }
  88. break;
  89. case BsonType.Integer:
  90. {
  91. BsonValue value = (BsonValue)t;
  92. _writer.Write(Convert.ToInt32(value.Value, CultureInfo.InvariantCulture));
  93. }
  94. break;
  95. case BsonType.Long:
  96. {
  97. BsonValue value = (BsonValue)t;
  98. _writer.Write(Convert.ToInt64(value.Value, CultureInfo.InvariantCulture));
  99. }
  100. break;
  101. case BsonType.Number:
  102. {
  103. BsonValue value = (BsonValue)t;
  104. _writer.Write(Convert.ToDouble(value.Value, CultureInfo.InvariantCulture));
  105. }
  106. break;
  107. case BsonType.String:
  108. {
  109. BsonString value = (BsonString)t;
  110. WriteString((string)value.Value, value.ByteCount, value.CalculatedSize - 4);
  111. }
  112. break;
  113. case BsonType.Boolean:
  114. _writer.Write(t == BsonBoolean.True);
  115. break;
  116. case BsonType.Null:
  117. case BsonType.Undefined:
  118. break;
  119. case BsonType.Date:
  120. {
  121. BsonValue value = (BsonValue)t;
  122. long ticks = 0;
  123. if (value.Value is DateTime)
  124. {
  125. DateTime dateTime = (DateTime)value.Value;
  126. if (DateTimeKindHandling == DateTimeKind.Utc)
  127. {
  128. dateTime = dateTime.ToUniversalTime();
  129. }
  130. else if (DateTimeKindHandling == DateTimeKind.Local)
  131. {
  132. dateTime = dateTime.ToLocalTime();
  133. }
  134. ticks = DateTimeUtils.ConvertDateTimeToJavaScriptTicks(dateTime, false);
  135. }
  136. #if !NET20
  137. else
  138. {
  139. DateTimeOffset dateTimeOffset = (DateTimeOffset)value.Value;
  140. ticks = DateTimeUtils.ConvertDateTimeToJavaScriptTicks(dateTimeOffset.UtcDateTime, dateTimeOffset.Offset);
  141. }
  142. #endif
  143. _writer.Write(ticks);
  144. }
  145. break;
  146. case BsonType.Binary:
  147. {
  148. BsonBinary value = (BsonBinary)t;
  149. byte[] data = (byte[])value.Value;
  150. _writer.Write(data.Length);
  151. _writer.Write((byte)value.BinaryType);
  152. _writer.Write(data);
  153. }
  154. break;
  155. case BsonType.Oid:
  156. {
  157. BsonValue value = (BsonValue)t;
  158. byte[] data = (byte[])value.Value;
  159. _writer.Write(data);
  160. }
  161. break;
  162. case BsonType.Regex:
  163. {
  164. BsonRegex value = (BsonRegex)t;
  165. WriteString((string)value.Pattern.Value, value.Pattern.ByteCount, null);
  166. WriteString((string)value.Options.Value, value.Options.ByteCount, null);
  167. }
  168. break;
  169. default:
  170. throw new ArgumentOutOfRangeException(nameof(t), "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type));
  171. }
  172. }
  173. private void WriteString(string s, int byteCount, int? calculatedlengthPrefix)
  174. {
  175. if (calculatedlengthPrefix != null)
  176. {
  177. _writer.Write(calculatedlengthPrefix.GetValueOrDefault());
  178. }
  179. WriteUtf8Bytes(s, byteCount);
  180. _writer.Write((byte)0);
  181. }
  182. public void WriteUtf8Bytes(string s, int byteCount)
  183. {
  184. if (s != null)
  185. {
  186. if (byteCount <= 256)
  187. {
  188. if (_largeByteBuffer == null)
  189. {
  190. _largeByteBuffer = new byte[256];
  191. }
  192. Encoding.GetBytes(s, 0, s.Length, _largeByteBuffer, 0);
  193. _writer.Write(_largeByteBuffer, 0, byteCount);
  194. }
  195. else
  196. {
  197. byte[] bytes = Encoding.GetBytes(s);
  198. _writer.Write(bytes);
  199. }
  200. }
  201. }
  202. private int CalculateSize(int stringByteCount)
  203. {
  204. return stringByteCount + 1;
  205. }
  206. private int CalculateSizeWithLength(int stringByteCount, bool includeSize)
  207. {
  208. int baseSize = (includeSize)
  209. ? 5 // size bytes + terminator
  210. : 1; // terminator
  211. return baseSize + stringByteCount;
  212. }
  213. private int CalculateSize(BsonToken t)
  214. {
  215. switch (t.Type)
  216. {
  217. case BsonType.Object:
  218. {
  219. BsonObject value = (BsonObject)t;
  220. int bases = 4;
  221. foreach (BsonProperty p in value)
  222. {
  223. int size = 1;
  224. size += CalculateSize(p.Name);
  225. size += CalculateSize(p.Value);
  226. bases += size;
  227. }
  228. bases += 1;
  229. value.CalculatedSize = bases;
  230. return bases;
  231. }
  232. case BsonType.Array:
  233. {
  234. BsonArray value = (BsonArray)t;
  235. int size = 4;
  236. ulong index = 0;
  237. foreach (BsonToken c in value)
  238. {
  239. size += 1;
  240. size += CalculateSize(MathUtils.IntLength(index));
  241. size += CalculateSize(c);
  242. index++;
  243. }
  244. size += 1;
  245. value.CalculatedSize = size;
  246. return value.CalculatedSize;
  247. }
  248. case BsonType.Integer:
  249. return 4;
  250. case BsonType.Long:
  251. return 8;
  252. case BsonType.Number:
  253. return 8;
  254. case BsonType.String:
  255. {
  256. BsonString value = (BsonString)t;
  257. string s = (string)value.Value;
  258. value.ByteCount = (s != null) ? Encoding.GetByteCount(s) : 0;
  259. value.CalculatedSize = CalculateSizeWithLength(value.ByteCount, value.IncludeLength);
  260. return value.CalculatedSize;
  261. }
  262. case BsonType.Boolean:
  263. return 1;
  264. case BsonType.Null:
  265. case BsonType.Undefined:
  266. return 0;
  267. case BsonType.Date:
  268. return 8;
  269. case BsonType.Binary:
  270. {
  271. BsonBinary value = (BsonBinary)t;
  272. byte[] data = (byte[])value.Value;
  273. value.CalculatedSize = 4 + 1 + data.Length;
  274. return value.CalculatedSize;
  275. }
  276. case BsonType.Oid:
  277. return 12;
  278. case BsonType.Regex:
  279. {
  280. BsonRegex value = (BsonRegex)t;
  281. int size = 0;
  282. size += CalculateSize(value.Pattern);
  283. size += CalculateSize(value.Options);
  284. value.CalculatedSize = size;
  285. return value.CalculatedSize;
  286. }
  287. default:
  288. throw new ArgumentOutOfRangeException(nameof(t), "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type));
  289. }
  290. }
  291. }
  292. }