using System; using System.IO; using System.Text; using ln.type; namespace ln.bson { public static class BsonReader { public static BsonDocument ReadDocument(byte[] buffer) => ReadDocument(buffer, 0, buffer.Length); public static BsonDocument ReadDocument(byte[] buffer, int offset) => ReadDocument(buffer, offset, buffer.Length - offset); public static BsonDocument ReadDocument(byte[] buffer, int offset, int length) { using (var ms = new MemoryStream(buffer, offset, length)) return ReadDocument(ms); } public static BsonDocument ReadDocument(Stream stream) { BsonDocument bsonDocument = new BsonDocument(); ReadDocumentItems(stream, ((s, value) => bsonDocument.Add(s,value))); return bsonDocument; } private static BsonArray ReadArray(Stream stream) { BsonArray bsonArray = new BsonArray(); ReadDocumentItems(stream, ((s, value) => bsonArray.Add(value))); return bsonArray; } private static void ReadDocumentItems(Stream stream, Action appender) { int documentLength = stream.ReadInteger(); BsonType bsonType = BsonType.Null; while (bsonType != BsonType.EndOfDocument) { bsonType = (BsonType)stream.ReadByte(); if (bsonType == BsonType.EndOfDocument) return; string name = stream.ReadCString(); BsonValue bsonValue = null; switch (bsonType) { case BsonType.Boolean: if (stream.ReadByte() == 0) bsonValue = BsonBoolean.False; else bsonValue = BsonBoolean.True; break; case BsonType.Double: bsonValue = new BsonDouble(stream.ReadDouble()); break; case BsonType.Int32: bsonValue = new BsonInt32(stream.ReadInteger()); break; case BsonType.Int64: bsonValue = new BsonInt64(stream.ReadLong()); break; case BsonType.Null: bsonValue = BsonNull.Instance; break; case BsonType.Binary: int blength = stream.ReadInteger(); BsonBinarySubType subType = (BsonBinarySubType)stream.ReadByte(); switch (subType) { case BsonBinarySubType.UuidOld: case BsonBinarySubType.Uuid: if (blength != 16) throw new ArgumentOutOfRangeException(nameof(blength)); bsonValue = new BsonGuid(stream.ReadBytes(blength)); break; default: bsonValue = new BsonBinary(subType, stream.ReadBytes(blength)); break; } break; case BsonType.String: blength = stream.ReadInteger(); bsonValue = new BsonString(Encoding.UTF8.GetString(stream.ReadBytes(blength),0, blength-1)); break; case BsonType.DateTime: bsonValue = new BsonDateTime(DateTimeExtensions.FromUnixTimeMilliseconds(stream.ReadLong())); break; case BsonType.Document: bsonValue = ReadDocument(stream); break; case BsonType.Array: bsonValue = ReadArray(stream); break; case BsonType.EndOfDocument: return; default: throw new NotSupportedException(); } appender(name, bsonValue); } } } }