private void Assert(BsonTrie<int> trie, string name)
        {
            var subject = new TrieNameDecoder<int>(trie);

            using (var memoryStream = new MemoryStream())
            using (var bsonStream = new BsonStreamAdapter(memoryStream))
            {
                bsonStream.WriteCString(name);
                bsonStream.WriteInt32(20);
                bsonStream.Position = 0;

                var result = subject.Decode(bsonStream, Utf8Encodings.Strict);

                result.Should().Be(name);
            }
        }
        private void Assert(BsonTrie <int> trie, string name)
        {
            var subject = new TrieNameDecoder <int>(trie);

            using (var memoryStream = new MemoryStream())
                using (var bsonStream = new BsonStreamAdapter(memoryStream))
                {
                    bsonStream.WriteCString(name);
                    bsonStream.WriteInt32(20);
                    bsonStream.Position = 0;

                    var result = subject.Decode(bsonStream, Utf8Encodings.Strict);

                    result.Should().Be(name);
                }
        }
        // private methods
        private void Assert(string name, TrieNameDecoder <int> subject, bool found, int value)
        {
            using (var memoryStream = new MemoryStream())
                using (var bsonStream = new BsonStreamAdapter(memoryStream))
                {
                    bsonStream.WriteCString(name);
                    bsonStream.WriteInt32(20);
                    bsonStream.Position = 0;

                    var result = subject.Decode(bsonStream, Utf8Encodings.Strict);

                    result.Should().Be(name);
                    subject.Found.Should().Be(found);
                    subject.Value.Should().Be(value);
                }
        }
        private void Assert(BsonTrie <int> trie, string name, bool found, int value)
        {
            // test multiple calls to the same TrieDecoder instance
            var subject = new TrieNameDecoder <int>(trie);

            // 1st pass: assert calling test's expectation
            Assert(name, subject, found, value);

            // 2rd pass: assert calling test's expectation once again
            Assert(name, subject, found, value);

            // 3rd pass: different value in stream --> assert "found: false" and "value: default"
            Assert("doesn't exist in trie", subject, false, default);

            // 4th pass: assert calling test's expectation once again
            Assert(name, subject, found, value);
        }
        // public methods
        /// <summary>
        /// Deserializes the members.
        /// </summary>
        /// <param name="context">The deserialization context.</param>
        /// <param name="memberHandler">The member handler.</param>
        /// <returns>The found member flags.</returns>
        public long DeserializeMembers(BsonDeserializationContext context, Action <string, long> memberHandler)
        {
            var reader           = context.Reader;
            var foundMemberFlags = 0L;

            reader.ReadStartDocument();
            var trieDecoder = new TrieNameDecoder <long>(_trie);

            while (reader.ReadBsonType() != 0)
            {
                var elementName = reader.ReadName(trieDecoder);

                long memberFlag;
                if (trieDecoder.Found)
                {
                    memberFlag = trieDecoder.Value;
                }
                else
                {
                    if (_extraMemberFlag == 0)
                    {
                        throw new BsonSerializationException(string.Format(
                                                                 "Invalid element: '{0}'.", elementName));
                    }
                    else
                    {
                        memberFlag = _extraMemberFlag;
                    }
                }

                memberHandler(elementName, memberFlag);
                foundMemberFlags |= memberFlag;
            }
            reader.ReadEndDocument();

            var missingRequiredMemberFlags = _requiredMemberFlags & ~foundMemberFlags;

            if (missingRequiredMemberFlags != 0)
            {
                var missingRequiredMember = FindFirstMissingRequiredMember(missingRequiredMemberFlags);
                throw new BsonSerializationException(string.Format(
                                                         "Missing element: '{0}'.", missingRequiredMember.ElementName));
            }

            return(foundMemberFlags);
        }
        /// <summary>
        /// Deserializes a value.
        /// </summary>
        /// <param name="context">The deserialization context.</param>
        /// <returns>A deserialized value.</returns>
        public TClass DeserializeClass(BsonDeserializationContext context)
        {
            var bsonReader = context.Reader;

            var bsonType = bsonReader.GetCurrentBsonType();

            if (bsonType != BsonType.Document)
            {
                var message = string.Format(
                    "Expected a nested document representing the serialized form of a {0} value, but found a value of type {1} instead.",
                    typeof(TClass).FullName, bsonType);
                throw new FormatException(message);
            }

            Dictionary <string, object> values = null;
            var document = default(TClass);

#if NET452 || NETSTANDARD2_0
            ISupportInitialize supportsInitialization = null;
#endif
            if (_classMap.HasCreatorMaps)
            {
                // for creator-based deserialization we first gather the values in a dictionary and then call a matching creator
                values = new Dictionary <string, object>();
            }
            else
            {
                // for mutable classes we deserialize the values directly into the result object
                document = (TClass)_classMap.CreateInstance();

#if NET452 || NETSTANDARD2_0
                supportsInitialization = document as ISupportInitialize;
                if (supportsInitialization != null)
                {
                    supportsInitialization.BeginInit();
                }
#endif
#if NETSTANDARD1_5
                if (_beginInitMethodInfo != null)
                {
                    _beginInitMethodInfo.Invoke(document, new object[0]);
                }
#endif
            }

            var discriminatorConvention     = _classMap.GetDiscriminatorConvention();
            var allMemberMaps               = _classMap.AllMemberMaps;
            var extraElementsMemberMapIndex = _classMap.ExtraElementsMemberMapIndex;
            var memberMapBitArray           = FastMemberMapHelper.GetBitArray(allMemberMaps.Count);

            bsonReader.ReadStartDocument();
            var elementTrie = _classMap.ElementTrie;
            while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
            {
                var trieDecoder = new TrieNameDecoder <int>(elementTrie);
                var elementName = bsonReader.ReadName(trieDecoder);

                if (trieDecoder.Found)
                {
                    var memberMapIndex = trieDecoder.Value;
                    var memberMap      = allMemberMaps[memberMapIndex];
                    if (memberMapIndex != extraElementsMemberMapIndex)
                    {
                        if (document != null)
                        {
                            if (memberMap.IsReadOnly)
                            {
                                bsonReader.SkipValue();
                            }
                            else
                            {
                                var value = DeserializeMemberValue(context, memberMap);
                                memberMap.Setter(document, value);
                            }
                        }
                        else
                        {
                            var value = DeserializeMemberValue(context, memberMap);
                            values[elementName] = value;
                        }
                    }
                    else
                    {
                        if (document != null)
                        {
                            DeserializeExtraElementMember(context, document, elementName, memberMap);
                        }
                        else
                        {
                            DeserializeExtraElementValue(context, values, elementName, memberMap);
                        }
                    }
                    memberMapBitArray[memberMapIndex >> 5] |= 1U << (memberMapIndex & 31);
                }
                else
                {
                    if (elementName == discriminatorConvention.ElementName)
                    {
                        bsonReader.SkipValue(); // skip over discriminator
                        continue;
                    }

                    if (extraElementsMemberMapIndex >= 0)
                    {
                        var extraElementsMemberMap = _classMap.ExtraElementsMemberMap;
                        if (document != null)
                        {
                            DeserializeExtraElementMember(context, document, elementName, extraElementsMemberMap);
                        }
                        else
                        {
                            DeserializeExtraElementValue(context, values, elementName, extraElementsMemberMap);
                        }
                        memberMapBitArray[extraElementsMemberMapIndex >> 5] |= 1U << (extraElementsMemberMapIndex & 31);
                    }
                    else if (_classMap.IgnoreExtraElements)
                    {
                        bsonReader.SkipValue();
                    }
                    else
                    {
                        var message = string.Format(
                            "Element '{0}' does not match any field or property of class {1}.",
                            elementName, _classMap.ClassType.FullName);
                        throw new FormatException(message);
                    }
                }
            }
            bsonReader.ReadEndDocument();

            // check any members left over that we didn't have elements for (in blocks of 32 elements at a time)
            for (var bitArrayIndex = 0; bitArrayIndex < memberMapBitArray.Length; ++bitArrayIndex)
            {
                var memberMapIndex = bitArrayIndex << 5;
                var memberMapBlock = ~memberMapBitArray[bitArrayIndex]; // notice that bits are flipped so 1's are now the missing elements

                // work through this memberMapBlock of 32 elements
                while (true)
                {
                    // examine missing elements (memberMapBlock is shifted right as we work through the block)
                    for (; (memberMapBlock & 1) != 0; ++memberMapIndex, memberMapBlock >>= 1)
                    {
                        var memberMap = allMemberMaps[memberMapIndex];
                        if (memberMap.IsReadOnly)
                        {
                            continue;
                        }

                        if (memberMap.IsRequired)
                        {
                            var fieldOrProperty = (memberMap.MemberInfo is FieldInfo) ? "field" : "property";
                            var message         = string.Format(
                                "Required element '{0}' for {1} '{2}' of class {3} is missing.",
                                memberMap.ElementName, fieldOrProperty, memberMap.MemberName, _classMap.ClassType.FullName);
                            throw new FormatException(message);
                        }

                        if (document != null)
                        {
                            memberMap.ApplyDefaultValue(document);
                        }
                        else if (memberMap.IsDefaultValueSpecified && !memberMap.IsReadOnly)
                        {
                            values[memberMap.ElementName] = memberMap.DefaultValue;
                        }
                    }

                    if (memberMapBlock == 0)
                    {
                        break;
                    }

                    // skip ahead to the next missing element
                    var leastSignificantBit = FastMemberMapHelper.GetLeastSignificantBit(memberMapBlock);
                    memberMapIndex  += leastSignificantBit;
                    memberMapBlock >>= leastSignificantBit;
                }
            }

            if (document != null)
            {
#if NET452 || NETSTANDARD2_0
                if (supportsInitialization != null)
                {
                    supportsInitialization.EndInit();
                }
#endif
#if NETSTANDARD1_5
                if (_endInitMethodInfo != null)
                {
                    _endInitMethodInfo.Invoke(document, new object[0]);
                }
#endif
                return(document);
            }
            else
            {
                return(CreateInstanceUsingCreator(values));
            }
        }
        // public methods
        /// <summary>
        /// Deserializes the members.
        /// </summary>
        /// <param name="context">The deserialization context.</param>
        /// <param name="memberHandler">The member handler.</param>
        /// <returns>The found member flags.</returns>
        public long DeserializeMembers(BsonDeserializationContext context, Action<string, long> memberHandler)
        {
            var reader = context.Reader;
            var foundMemberFlags = 0L;

            reader.ReadStartDocument();
            while (reader.ReadBsonType() != 0)
            {
                var trieDecoder = new TrieNameDecoder<long>(_trie);
                var elementName = reader.ReadName(trieDecoder);

                long memberFlag;
                if (trieDecoder.Found)
                {
                    memberFlag = trieDecoder.Value;
                }
                else
                {
                    if (_extraMemberFlag == 0)
                    {
                        throw new BsonSerializationException(string.Format(
                            "Invalid element: '{0}'.", elementName));
                    }
                    else
                    {
                        memberFlag = _extraMemberFlag;
                    }
                }

                memberHandler(elementName, memberFlag);
                foundMemberFlags |= memberFlag;
            }
            reader.ReadEndDocument();

            var missingRequiredMemberFlags = _requiredMemberFlags & ~foundMemberFlags;
            if (missingRequiredMemberFlags != 0)
            {
                var missingRequiredMember = FindFirstMissingRequiredMember(missingRequiredMemberFlags);
                throw new BsonSerializationException(string.Format(
                   "Missing element: '{0}'.", missingRequiredMember.ElementName));
            }

            return foundMemberFlags;
        }