using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Utilities;
namespace Newtonsoft.Json.Bson{
internal class BsonBinaryWriter
{
private static readonly Encoding Encoding = Encoding.UTF8;
private readonly BinaryWriter _writer;
private byte[] _largeByteBuffer;
private int _maxChars;
public BsonBinaryWriter(Stream stream)
{
_writer = new BinaryWriter(stream);
}
public void Flush()
{
_writer.Flush();
}
public void WriteToken(BsonToken t)
{
CalculateSize(t);
WriteTokenInternal(t);
}
private void WriteTokenInternal(BsonToken t)
{
switch (t.Type)
{
case BsonType.Object:
{
BsonObject value = (BsonObject) t;
_writer.Write(value.CalculatedSize);
foreach (BsonProperty property in value)
{
_writer.Write((sbyte)property.Value.Type);
WriteString((string)property.Name.Value, property.Name.ByteCount, null);
WriteTokenInternal(property.Value);
}
_writer.Write((byte)0);
}
break;
case BsonType.Array:
{
BsonArray value = (BsonArray) t;
_writer.Write(value.CalculatedSize);
int index = 0;
foreach (BsonToken c in value)
{
_writer.Write((sbyte)c.Type);
WriteString(index.ToString(CultureInfo.InvariantCulture), MathUtils.IntLength(index), null);
WriteTokenInternal(c);
index++;
}
_writer.Write((byte)0);
}
break;
case BsonType.Integer:
{
BsonValue value = (BsonValue) t;
_writer.Write(Convert.ToInt32(value.Value, CultureInfo.InvariantCulture));
}
break;
case BsonType.Long:
{
BsonValue value = (BsonValue)t;
_writer.Write(Convert.ToInt64(value.Value, CultureInfo.InvariantCulture));
}
break;
case BsonType.Number:
{
BsonValue value = (BsonValue)t;
_writer.Write(Convert.ToDouble(value.Value, CultureInfo.InvariantCulture));
}
break;
case BsonType.String:
{
BsonString value = (BsonString)t;
WriteString((string)value.Value, value.ByteCount, value.CalculatedSize - 4);
}
break;
case BsonType.Boolean:
{
BsonValue value = (BsonValue)t;
_writer.Write((bool)value.Value);
}
break;
case BsonType.Null:
case BsonType.Undefined:
break;
case BsonType.Date:
{
BsonValue value = (BsonValue)t;
long ticks = 0;
if (value.Value is DateTime)
{
DateTime dateTime = (DateTime) value.Value;
dateTime = dateTime.ToUniversalTime();
ticks = JsonConvert.ConvertDateTimeToJavaScriptTicks(dateTime);
}
#if !PocketPC && !NET20
else
{
DateTimeOffset dateTimeOffset = (DateTimeOffset) value.Value;
ticks = JsonConvert.ConvertDateTimeToJavaScriptTicks(dateTimeOffset.UtcDateTime, dateTimeOffset.Offset);
}
#endif
_writer.Write(ticks);
}
break;
case BsonType.Binary:
{
BsonValue value = (BsonValue)t;
byte[] data = (byte[])value.Value;
_writer.Write(data.Length);
_writer.Write((byte)BsonBinaryType.Data);
_writer.Write(data);
}
break;
case BsonType.Oid:
{
BsonValue value = (BsonValue)t;
byte[] data = (byte[])value.Value;
_writer.Write(data);
}
break;
case BsonType.Regex:
{
BsonRegex value = (BsonRegex) t;
WriteString((string)value.Pattern.Value, value.Pattern.ByteCount, null);
WriteString((string)value.Options.Value, value.Options.ByteCount, null);
}
break;
default:
throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type));
}
}
private void WriteString(string s, int byteCount, int? calculatedlengthPrefix)
{
if (calculatedlengthPrefix != null)
_writer.Write(calculatedlengthPrefix.Value);
if (s != null)
{
if (_largeByteBuffer == null)
{
_largeByteBuffer = new byte[256];
_maxChars = 256/Encoding.GetMaxByteCount(1);
}
if (byteCount <= 256)
{
Encoding.GetBytes(s, 0, s.Length, _largeByteBuffer, 0);
_writer.Write(_largeByteBuffer, 0, byteCount);
}
else
{
int charCount;
int totalCharsWritten = 0;
for (int i = s.Length; i > 0; i -= charCount)
{
charCount = (i > _maxChars) ? _maxChars : i;
int count = Encoding.GetBytes(s, totalCharsWritten, charCount, _largeByteBuffer, 0);
_writer.Write(_largeByteBuffer, 0, count);
totalCharsWritten += charCount;
}
}
}
_writer.Write((byte)0);
}
private int CalculateSize(int stringByteCount)
{
return stringByteCount + 1;
}
private int CalculateSizeWithLength(int stringByteCount, bool includeSize)
{
int baseSize = (includeSize)
? 5 // size bytes + terminator
: 1; // terminator
return baseSize + stringByteCount;
}
private int CalculateSize(BsonToken t)
{
switch (t.Type)
{
case BsonType.Object:
{
BsonObject value = (BsonObject) t;
int bases = 4;
foreach (BsonProperty p in value)
{
int size = 1;
size += CalculateSize(p.Name);
size += CalculateSize(p.Value);
bases += size;
}
bases += 1;
value.CalculatedSize = bases;
return bases;
}
case BsonType.Array:
{
BsonArray value = (BsonArray)t;
int size = 4;
int index = 0;
foreach (BsonToken c in value)
{
size += 1;
size += CalculateSize(MathUtils.IntLength(index));
size += CalculateSize(c);
index++;
}
size += 1;
value.CalculatedSize = size;
return value.CalculatedSize;
}
case BsonType.Integer:
return 4;
case BsonType.Long:
return 8;
case BsonType.Number:
return 8;
case BsonType.String:
{
BsonString value = (BsonString)t;
string s = (string) value.Value;
value.ByteCount = (s != null) ? Encoding.GetByteCount(s) : 0;
value.CalculatedSize = CalculateSizeWithLength(value.ByteCount, value.IncludeLength);
return value.CalculatedSize;
}
case BsonType.Boolean:
return 1;
case BsonType.Null:
case BsonType.Undefined:
return 0;
case BsonType.Date:
return 8;
case BsonType.Binary:
{
BsonValue value = (BsonValue) t;
byte[] data = (byte[])value.Value;
value.CalculatedSize = 4 + 1 + data.Length;
return value.CalculatedSize;
}
case BsonType.Oid:
return 12;
case BsonType.Regex:
{
BsonRegex value = (BsonRegex) t;
int size = 0;
size += CalculateSize(value.Pattern);
size += CalculateSize(value.Options);
value.CalculatedSize = size;
return value.CalculatedSize;
}
default:
throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type));
}
}
}
}
|