/// <summary>
        /// Create a new instance of FieldMetaData with a given MemberInfo.
        /// </summary>
        /// <param name="outRef">Indicates the reference of the out object that hold this constraint.</param>
        /// <param name="info"></param>
        /// <remarks>
        /// AttachedTag, Constraint will be set automatically by reflection.
        /// </remarks>
        protected Asn1ConstraintedFieldMetadata(Asn1Object outRef, MemberInfo info)
        {
            OutReference = outRef;
            MemberInfo = info;

            //Get AttachedTag
            AttachedTag = null;
            var attrs = MemberInfo.GetCustomAttributes(typeof(Asn1Tag), true);
            if (attrs.Length != 0)
            {
                //Only the first tag is valid.
                Asn1Tag tag = attrs[0] as Asn1Tag;
                if (tag.TagType != Asn1TagType.Context && tag.TagType != Asn1TagType.Application)
                {
                    throw new Asn1UserDefinedTypeInconsistent(ExceptionMessages.UserDefinedTypeInconsistent
                        + " Only Context-Specific tag and Application tag are allowed.");
                }
                AttachedTag = tag;
            }

            //Get Constraint
            Constraint = GetConstraints();
        }
 /// <summary>
 /// Encodes a tag to the buffer.
 /// </summary>
 /// <param name="buffer"></param>
 /// <param name="tag"></param>
 /// <returns>The length of the encoding result.</returns>
 /// <remarks>Override this method in a user-defined class only if the procedure is not applicable in some special scenarios.</remarks>
 protected virtual int TagBerEncode(IAsn1BerEncodingBuffer buffer, Asn1Tag tag)
 {
     return Asn1StandardProcedure.TagBerEncode(buffer, tag);
 }
 /// <summary>
 /// Decodes a tag from the buffer.
 /// </summary>
 /// <param name="buffer"></param>
 /// <param name="tag"></param>
 /// <returns>The number of the bytes consumed in the buffer to decode the tag.</returns>
 /// <remarks>Override this method in a user-defined class only if the procedure is not applicable in some special scenarios.</remarks>
 protected virtual int TagBerDecode(IAsn1DecodingBuffer buffer, out Asn1Tag tag)
 {
     return Asn1StandardProcedure.TagBerDecode(buffer, out tag);
 }
 /// <summary>
 /// Encodes a tag to the buffer.
 /// </summary>
 /// <param name="buffer"></param>
 /// <param name="tag"></param>
 /// <returns>The length of the encoding result.</returns>
 public static int TagBerEncode(IAsn1BerEncodingBuffer buffer, Asn1Tag tag)
 {
     //Ref. X.690: 8.1.2.2, there are four kinds of tags
     byte prefix = 0;
     switch (tag.TagType)
     {
         case Asn1TagType.Universal:
             {
                 prefix = 0;//00
             } break;
         case Asn1TagType.Application:
             {
                 prefix = 1;//01
             } break;
         case Asn1TagType.Context:
             {
                 prefix = 2;//10
             } break;
         case Asn1TagType.Private:
             {
                 prefix = 3;//11
                 //Ref. X.680: G.2.12.4
                 throw new Asn1UnreachableExcpetion(ExceptionMessages.Unreachable);
             };
     }
     prefix <<= 6;
     if (tag.EncodingWay == EncodingWay.Constructed)
     {
         prefix |= (1 << 5);//Set the sixth bit to 1 if it is encoded in constructed way. Ref. X.690: 8.1.2.3
     }
     if (tag.TagValue <= 30)//Use one byte to store the encoding result
     //Ref. X.690: 8.1.2.3
     {
         prefix |= (byte)tag.TagValue;
         buffer.WriteByte(prefix);
         return 1;
     }
     else//Use more than one bytes to store.
     {
         //Ref. X.690: 8.1.2.4.3
         throw new NotImplementedException("Case tag > 30 is not implemented. Check X.690: 8.1.2.4.3.");
     }
 }
 /// <summary>
 /// Decodes a tag from the buffer.
 /// </summary>
 /// <param name="buffer"></param>
 /// <param name="tag"></param>
 /// <returns>The number of the bytes consumed in the buffer to decode the tag.</returns>
 public static int TagBerDecode(IAsn1DecodingBuffer buffer, out Asn1Tag tag)
 {
     byte prefix = buffer.ReadByte();
     int firstTwoBits = (prefix >> 6);
     Asn1TagType tagType = Asn1TagType.Private;
     long tagValue;
     switch (firstTwoBits)
     {
         case 0:
             {
                 tagType = Asn1TagType.Universal;
             } break;
         case 1:
             {
                 tagType = Asn1TagType.Application;
             } break;
         case 2:
             {
                 tagType = Asn1TagType.Context;
             } break;
         case 3:
             {
                 throw new NotImplementedException(ExceptionMessages.Unreachable);
             };
     }
     tagValue = prefix & ((byte)(1 << 5) - 1);
     if (tagValue <= 30)
     {
         tag = new Asn1Tag(tagType, tagValue);
         if ((prefix & (1 << 5)) != 0)
         {
             tag.EncodingWay = EncodingWay.Constructed;
         }
         else
         {
             tag.EncodingWay = EncodingWay.Primitive;
         }
         return 1;
     }
     else
     {
         throw new NotImplementedException("Case tag > 30 is not implemented. Check X.690: 8.1.2.4.3.");
     }
 }
        /// <summary>
        /// Decodes the data from the buffer and stores the data in this object.
        /// </summary>
        /// <param name="buffer">A buffer that stores a BER encoding result.</param>
        /// <param name="length">The length of the encoding result of the data in the given buffer.</param>
        /// <returns>The number of the bytes consumed in the buffer to decode the data.</returns>
        /// <remarks>
        /// For the TLV (Tag, Length, Value) triple in the encoding result,
        /// this method provides the functionality of decoding Value.
        /// The decoding for Tag and Length will be done in Asn1Object::BerDecode method.
        /// </remarks>
        protected override int ValueBerDecode(IAsn1DecodingBuffer buffer, int length)
        {
            int consumedLen = 0;

            Asn1Tag tag     = null; //The current decoded tag.
            int     tagLen  = 0;    //The consumed length of the bytes in the buffer when decoding the current tag.
            bool    tagUsed = true;

            int curFieldIndex = 0;//The index of the field that currently is trying to decode.

            Asn1Object[] tempFields = GetFieldsTypeInstances();

            while (consumedLen < length)
            {
                if (curFieldIndex >= tempFields.Length)
                {
                    throw new Asn1DecodingUnexpectedData(ExceptionMessages.DecodingUnexpectedData);
                }

                if (tagUsed)
                {
                    tagLen = TagBerDecode(buffer, out tag);
                    //The decoded tag may be context , application or universal tag.
                }

                if (fieldOptionalFlags[curFieldIndex] == false)
                {
                    //Current field is mandatory.
                    if (attachedTags[curFieldIndex] != null)
                    {
                        //Current field has a context class tag.
                        //The decoded tag must be the context class tag of the field.
                        if (tag.Equals(attachedTags[curFieldIndex]))
                        {
                            consumedLen += tagLen;
                            int lengthAfterCtxTag;
                            consumedLen += LengthBerDecode(buffer, out lengthAfterCtxTag);
                            //Decodes this field by Asn1Object::BerDecode when the if-else statement for fieldOptionalFlags ends.
                        }
                        else
                        {
                            throw new Asn1DecodingUnexpectedData(ExceptionMessages.DecodingUnexpectedData + " Context Tag Decode fail.");
                        }
                    }
                    else
                    {
                        //Current field doesn't have a context class tag.
                        //The decoded tag must be the application or universal class tag of the field.
                        //Retreat the buffer since the decoding of application or universal class is part of Asn1Object::BerDecode.
                        buffer.SeekBytePosition(-tagLen);
                        //Decode this field by Asn1Object::BerDecode when the if-else statement for fieldOptionalFlags ends.
                    }
                }
                else
                {
                    //Current field is optional.
                    if (attachedTags[curFieldIndex] != null)
                    {
                        //Current field has a context class tag.
                        //Check whether it is the encoding result of this field by the context-specific class tag.
                        if (tag.Equals(attachedTags[curFieldIndex]))
                        {
                            //Yes
                            consumedLen += tagLen;
                            int lengthAfterCtxTag;
                            consumedLen += LengthBerDecode(buffer, out lengthAfterCtxTag);
                            //Decode this field by Asn1Object::BerDecode when the if-else statement for OptionalFlags ends.
                        }
                        else
                        {
                            //No
                            tagUsed = false;
                            tempFields[curFieldIndex] = null;
                            curFieldIndex++;
                            //Check the next field.
                            continue;
                        }
                    }
                    else
                    {
                        //Current field doesn't have a context class tag.
                        //Check whether it is the encoding result of this field by the top most class tag.
                        if (tag.Equals(tempFields[curFieldIndex].TopTag))
                        {
                            //Yes
                            buffer.SeekBytePosition(-tagLen);
                            //Decode this field by Asn1Object::BerDecode when the if-else statement for OptionalFlags ends.
                        }
                        else
                        {
                            //No
                            tagUsed = false;
                            tempFields[curFieldIndex] = null;
                            curFieldIndex++;
                            //Check the next field.
                            continue;
                        }
                    }
                }
                consumedLen += tempFields[curFieldIndex].BerDecode(buffer);
                tagUsed      = true;
                curFieldIndex++;
            }

            //Ensure consumedLen equals to length.
            if (consumedLen > length)
            {
                throw new Asn1DecodingUnexpectedData(ExceptionMessages.DecodingUnexpectedData);
            }

            //Stores the decoded fields in this object.
            if (tempFields.Length != fieldsMemberInfo.Length)
            {
                throw new Asn1DecodingUnexpectedData(ExceptionMessages.DecodingUnexpectedData);
            }

            for (int i = 0; i < curFieldIndex; i++)
            {
                if (fieldsMemberInfo[i].MemberType == MemberTypes.Property)
                {
                    (fieldsMemberInfo[i] as PropertyInfo).SetValue(this, tempFields[i], null);
                }
                else if (fieldsMemberInfo[i].MemberType == MemberTypes.Field)
                {
                    (fieldsMemberInfo[i] as FieldInfo).SetValue(this, tempFields[i]);
                }
                else
                {
                    //Unreachable. Ensured by the AttributeUsage of Asn1Field.
                    throw new Asn1UserDefinedTypeInconsistent(ExceptionMessages.UserDefinedTypeInconsistent
                                                              + " Asn1Field property could only be used in properties or fields.");
                }
            }
            return(consumedLen);
        }
 public override int BerEncode(IAsn1BerEncodingBuffer buffer, bool explicitTag = true)
 {
     int allLen = 0;
     Asn1Tag contextTag;
     switch (SelectedChoice)
     {
         case 0:
             allLen += field0.BerEncodeWithoutUnisersalTag(buffer);
             contextTag = new Asn1Tag(Asn1TagType.Context, 0) { EncodingWay = EncodingWay.Primitive };
             allLen += TagBerEncode(buffer, contextTag);
             break;
         case 1:
             allLen += field1.BerEncodeWithoutUnisersalTag(buffer);
             contextTag = new Asn1Tag(Asn1TagType.Context, 3) { EncodingWay = EncodingWay.Constructed };
             allLen += TagBerEncode(buffer, contextTag);
             break;
         case 2:
             allLen += field2.BerEncodeWithoutUnisersalTag(buffer);
             contextTag = new Asn1Tag(Asn1TagType.Context, 9) { EncodingWay = EncodingWay.Primitive };
             allLen += TagBerEncode(buffer, contextTag);
             break;
         case 3:
             allLen += field3.BerEncodeWithoutUnisersalTag(buffer);
             contextTag = new Asn1Tag(Asn1TagType.Context, 10) { EncodingWay = EncodingWay.Primitive };
             allLen += TagBerEncode(buffer, contextTag);
             break;
         case 4:
             allLen += field4.BerEncodeWithoutUnisersalTag(buffer);
             contextTag = new Asn1Tag(Asn1TagType.Context, 11) { EncodingWay = EncodingWay.Primitive };
             allLen += TagBerEncode(buffer, contextTag);
             break;
         default:
             throw new Asn1ConstraintsNotSatisfied(ExceptionMessages.InvalidChoiceIndex + " AuthenticationChoice");
     }
     return allLen;
 }