/// <summary> /// Emits a single element to the binary stream /// </summary> /// <param name="writer"></param> /// <param name="tokens"></param> /// <param name="ename"></param> /// <returns>number of bytes written</returns> private int WriteElement(BinaryWriter writer, IStream <Token <ModelTokenType> > tokens, string ename) { Token <ModelTokenType> token = tokens.Peek(); if (tokens.IsCompleted || token == null) { throw new TokenException <ModelTokenType>(token, BsonWriter.ErrorUnterminated); } BsonElementType elemType; switch (token.TokenType) { case ModelTokenType.ArrayBegin: { elemType = BsonElementType.Array; break; } case ModelTokenType.ObjectBegin: { elemType = BsonElementType.Document; break; } case ModelTokenType.Primitive: { elemType = BsonFormatter.GetElementType(token.Value); break; } default: { // the rest are invalid states throw new TokenException <ModelTokenType>(token, String.Format(BsonWriter.ErrorUnexpectedToken, token.TokenType)); } } // write element type writer.Write((byte)elemType); int total = BsonWriter.SizeOfByte; // for element type // write EName total += BsonFormatter.WriteString(writer, ename, true); IBsonFormattable formattable = token.Value as IBsonFormattable; if (formattable != null) { total += formattable.Format(this, writer); } else { switch (elemType) { case BsonElementType.Double: { // consume token value tokens.Pop(); // write double data writer.Write((double)token.Value); total += BsonWriter.SizeOfDouble; break; } case BsonElementType.String: case BsonElementType.JavaScriptCode: case BsonElementType.Symbol: { // consume token value tokens.Pop(); // write as string data total += BsonFormatter.WriteString(writer, token.ValueAsString(), false); break; } case BsonElementType.Document: case BsonElementType.Array: { // delegate property to sub-document total += this.WriteDocument(writer, tokens); break; } case BsonElementType.Binary: { // consume token value tokens.Pop(); total += BsonFormatter.WriteBinary(writer, token); break; } case BsonElementType.ObjectID: { // consume token value tokens.Pop(); // write ObjectID data writer.Write((byte[])token.Value); total += BsonWriter.SizeOfObjectID; break; } case BsonElementType.Boolean: { // consume token value tokens.Pop(); // write bool data bool value = true.Equals(token.Value); writer.Write(value ? BsonWriter.TrueByte : BsonWriter.FalseByte); total += BsonWriter.SizeOfByte; break; } case BsonElementType.DateTimeUtc: { // consume token value tokens.Pop(); DateTime value = (DateTime)token.Value; if (value.Kind == DateTimeKind.Local) { // convert server-local to UTC value = value.ToUniversalTime(); } // find the duration since Jan 1, 1970 TimeSpan duration = value.Subtract(BsonWriter.UnixEpoch); // get the total milliseconds long ticks = (long)duration.TotalMilliseconds; // write long data writer.Write((long)ticks); total += BsonWriter.SizeOfInt64; break; } case BsonElementType.RegExp: { // consume token value tokens.Pop(); Regex regex = token.Value as Regex; if (regex == null) { goto default; } // default implementation is to simply return the pattern string string pattern = regex.ToString(); // write cstring data total += BsonFormatter.WriteString(writer, pattern, true); bool isGlobal = false; // nothing to switch on string options = isGlobal ? "g" : ""; switch (regex.Options & (RegexOptions.IgnoreCase | RegexOptions.Multiline)) { case RegexOptions.IgnoreCase: { options += "i"; break; } case RegexOptions.Multiline: { options += "m"; break; } case RegexOptions.IgnoreCase | RegexOptions.Multiline: { options += "im"; break; } } // write cstring data total += BsonFormatter.WriteString(writer, options, true); break; } case BsonElementType.DBPointer: { // consume token value tokens.Pop(); BsonDBPointer pointer = token.Value as BsonDBPointer; if (pointer == null) { goto default; } // write string data total += BsonFormatter.WriteString(writer, pointer.Namespace, false); // write bytes writer.Write((byte[])pointer.ObjectID); total += BsonWriter.SizeOfObjectID; break; } case BsonElementType.CodeWithScope: { // consume token value tokens.Pop(); BsonCodeWithScope codews = token.Value as BsonCodeWithScope; if (codews == null) { goto default; } total += this.WriteCodeWithScope(writer, codews); break; } case BsonElementType.Int32: { // consume token value tokens.Pop(); // write int data writer.Write((int)token.Value); total += BsonWriter.SizeOfInt32; break; } case BsonElementType.TimeStamp: case BsonElementType.Int64: { // consume token value tokens.Pop(); // TODO: determine how to convert TimeStamp // write long data writer.Write((long)token.Value); total += BsonWriter.SizeOfInt64; break; } case BsonElementType.Undefined: case BsonElementType.Null: case BsonElementType.MinKey: case BsonElementType.MaxKey: { // consume token value tokens.Pop(); // no data emitted for these break; } default: { // the rest are invalid states throw new TokenException <ModelTokenType>(token, String.Format(BsonWriter.ErrorUnexpectedToken, token.TokenType)); } } } return(total); }
private static void ReadElement(List <Token <ModelTokenType> > tokens, BinaryReader reader, bool isArrayItem) { BsonElementType elemType = (BsonElementType)reader.ReadByte(); string ename = BsonTokenizer.ReadCString(reader); if (!isArrayItem) { tokens.Add(ModelGrammar.TokenProperty(ename)); } switch (elemType) { case BsonElementType.Double: { double value = reader.ReadDouble(); tokens.Add(ModelGrammar.TokenPrimitive(value)); break; } case BsonElementType.String: { string value = BsonTokenizer.ReadString(reader); tokens.Add(ModelGrammar.TokenPrimitive(value)); break; } case BsonElementType.JavaScriptCode: { BsonJavaScriptCode value = (BsonJavaScriptCode)BsonTokenizer.ReadString(reader); tokens.Add(ModelGrammar.TokenPrimitive(value)); break; } case BsonElementType.Symbol: { BsonSymbol value = (BsonSymbol)BsonTokenizer.ReadString(reader); tokens.Add(ModelGrammar.TokenPrimitive(value)); break; } case BsonElementType.Document: { BsonTokenizer.ReadDocument(tokens, reader, false); break; } case BsonElementType.Array: { BsonTokenizer.ReadDocument(tokens, reader, true); break; } case BsonElementType.Binary: { BsonTokenizer.ReadBinary(tokens, reader); break; } case BsonElementType.ObjectID: { byte[] value = reader.ReadBytes(BsonWriter.SizeOfObjectID); tokens.Add(ModelGrammar.TokenPrimitive(new BsonObjectID(value))); break; } case BsonElementType.Boolean: { bool value = reader.ReadByte() != BsonWriter.FalseByte; tokens.Add(value ? ModelGrammar.TokenTrue : ModelGrammar.TokenFalse); break; } case BsonElementType.DateTimeUtc: { DateTime value = BsonWriter.UnixEpoch.AddMilliseconds(reader.ReadInt64()); tokens.Add(ModelGrammar.TokenPrimitive(value)); break; } case BsonElementType.RegExp: { string pattern = BsonTokenizer.ReadCString(reader); string optionsStr = BsonTokenizer.ReadCString(reader); RegexOptions options = RegexOptions.ECMAScript; for (int i = optionsStr.Length - 1; i >= 0; i--) { char ch = optionsStr[i]; switch (ch) { case 'g': { // TODO: ensure correct encoding of ^$ //options |= RegexOptions.Multiline; break; } case 'i': { options |= RegexOptions.IgnoreCase; break; } case 'm': { options |= RegexOptions.Multiline; break; } } } Regex regex = new Regex(pattern, options); tokens.Add(ModelGrammar.TokenPrimitive(regex)); break; } case BsonElementType.DBPointer: { string value1 = BsonTokenizer.ReadString(reader); byte[] value2 = reader.ReadBytes(BsonWriter.SizeOfObjectID); BsonDBPointer pointer = new BsonDBPointer { Namespace = value1, ObjectID = new BsonObjectID(value2) }; tokens.Add(ModelGrammar.TokenPrimitive(pointer)); break; } case BsonElementType.CodeWithScope: { int size = reader.ReadInt32(); string value = BsonTokenizer.ReadString(reader); tokens.Add(ModelGrammar.TokenPrimitive(value)); BsonTokenizer.ReadDocument(tokens, reader, false); break; } case BsonElementType.Int32: { int value = reader.ReadInt32(); tokens.Add(ModelGrammar.TokenPrimitive(value)); break; } case BsonElementType.TimeStamp: { long value = reader.ReadInt64(); // TODO: convert to TimeSpan? tokens.Add(ModelGrammar.TokenPrimitive(value)); break; } case BsonElementType.Int64: { long value = reader.ReadInt64(); tokens.Add(ModelGrammar.TokenPrimitive(value)); break; } case BsonElementType.Undefined: case BsonElementType.Null: case BsonElementType.MinKey: case BsonElementType.MaxKey: { // no data value break; } default: { throw new DeserializationException( String.Format(BsonTokenizer.ErrorUnexpectedElementType, elemType), reader.BaseStream.Position); } } }