@@ -128,7 +128,10 @@ void m_initialize(Byte[] raw, Int32 pOffset) {
128128 if ( Tag == 0 ) {
129129 throw new Asn1InvalidTagException ( Offset ) ;
130130 }
131- if ( _multiNestedTypes . Contains ( Tag ) || ( Tag & ( Byte ) Asn1Class . CONSTRUCTED ) > 0 ) {
131+ // the idea is that SET/SEQUENCE and any explicitly constructed types are constructed by default.
132+ // Though, we need to limit them for Application and higher classes which are not guaranteed to be
133+ // constructed.
134+ if ( _multiNestedTypes . Contains ( Tag ) || ( ( Tag & ( Byte ) Asn1Class . CONSTRUCTED ) > 0 && Tag < ( Byte ) Asn1Class . APPLICATION ) ) {
132135 IsConstructed = true ;
133136 }
134137 if ( PayloadLength == 0 ) {
@@ -159,27 +162,43 @@ void parseNestedType() {
159162 // processing rules (assuming zero-based bits):
160163 // if bit 5 is set to "1", or the type is SEQUENCE/SET -- the type is constructed. Unroll nested types.
161164 // if bit 5 is set to "0", attempt to resolve nested types only for UNIVERSAL tags.
165+ // some universal types cannot include nested types: skip them in advance.
162166 if ( _excludedTags . Contains ( Tag ) || PayloadLength < 2 ) { return ; }
163167 Int64 pstart = PayloadStartOffset ;
164168 Int32 plength = PayloadLength ;
165- if ( Tag == 3 ) {
169+ // BIT_STRING includes "unused bits" octet, do not count it in calculations
170+ if ( Tag == ( Byte ) Asn1Type . BIT_STRING ) {
166171 pstart = PayloadStartOffset + 1 ;
167172 plength = PayloadLength - 1 ;
168173 }
174+ // if current type is constructed or nestable by default
169175 if ( IsConstructed ) {
176+ // check if map for current type exists
170177 if ( ! _offsetMap . ContainsKey ( pstart ) ) {
178+ // if current map doesn't contain nested types boundaries, add them to the map.
179+ // this condition occurs when we face current type for the first time.
171180 predict ( pstart , plength , true , out childCount ) ;
172181 }
173-
174182 return ;
175183 }
184+ // universal types can contain only universal or constructed nested types.
185+ if ( ! testNestedForUniversal ( pstart ) ) {
186+ return ;
187+ }
188+ // attempt to unroll nested type
189+ IsConstructed = predict ( pstart , plength , false , out childCount ) ;
190+ // reiterate again and build map for children
191+ if ( IsConstructed && ! _offsetMap . ContainsKey ( pstart ) ) {
192+ predict ( pstart , plength , true , out childCount ) ;
193+ }
194+ }
195+ Boolean testNestedForUniversal ( Int64 pstart ) {
196+ // if current type is primitive, then nested type can be either, primitive or constructed only.
176197 if ( Tag > 0 && Tag < ( Byte ) Asn1Type . TAG_MASK ) {
177- IsConstructed = predict ( pstart , plength , false , out childCount ) ;
178- // reiterate again and build map for children
179- if ( IsConstructed && ! _offsetMap . ContainsKey ( pstart ) ) {
180- predict ( pstart , plength , true , out childCount ) ;
181- }
198+ return RawData [ pstart ] < ( Byte ) Asn1Class . APPLICATION ;
182199 }
200+ // otherwise return True, so we can proceed with nested type parsing which can be of any type.
201+ return true ;
183202 }
184203 Boolean predict ( Int64 start , Int32 projectedLength , Boolean assignMap , out Int32 estimatedChildCount ) {
185204 Int64 levelStart = start ;
@@ -236,7 +255,7 @@ Int64 calculatePredictLength(Int64 offset) {
236255 }
237256 void moveAndExpectTypes ( Func < Boolean > action , params Byte [ ] expectedTypes ) {
238257 if ( expectedTypes == null ) { throw new ArgumentNullException ( nameof ( expectedTypes ) ) ; }
239- HashSet < Byte > htable = new HashSet < Byte > ( ) ;
258+ var htable = new HashSet < Byte > ( ) ;
240259 foreach ( Byte tag in expectedTypes ) {
241260 htable . Add ( tag ) ;
242261 }
@@ -292,7 +311,6 @@ public Int32 GetNestedNodeCount() {
292311 /// </returns>
293312 public Boolean MoveNext ( ) {
294313 if ( NextOffset == 0 ) { return false ; }
295- //projectedIterationSize = _offsetMap[NextOffset];
296314 currentPosition = _offsetMap [ NextOffset ] ;
297315 m_initialize ( null , NextOffset ) ;
298316 return true ;
@@ -364,7 +382,8 @@ public void MoveNextCurrentLevelAndExpectTags(params Byte[] expectedTags) {
364382 /// method must be called prior to first call of this method. Subsequent <strong>BuildOffsetMap</strong>
365383 /// method calls are not necessary.
366384 /// </remarks>
367- public Boolean MoveToPosition ( Int32 newPosition ) {
385+ [ Obsolete ( "This method contains a typo. Use MoveToPosition instead." ) ]
386+ public Boolean MoveToPoisition ( Int32 newPosition ) {
368387 if ( _offsetMap == null ) {
369388 throw new InvalidOperationException ( ) ;
370389 }
@@ -376,6 +395,22 @@ public Boolean MoveToPosition(Int32 newPosition) {
376395 return true ;
377396 }
378397 /// <summary>
398+ /// Moves to a specified start offset.
399+ /// </summary>
400+ /// <param name="newPosition">ASN structure start position (offset).</param>
401+ /// <returns>
402+ /// <strong>True</strong> if specified offset is valid and pointer was successfully set to specified position,
403+ /// otherwise <strong>False</strong>.
404+ /// </returns>
405+ /// <remarks>
406+ /// Specified position validity is determined based on internal map and <see cref="BuildOffsetMap"/>
407+ /// method must be called prior to first call of this method. Subsequent <strong>BuildOffsetMap</strong>
408+ /// method calls are not necessary.
409+ /// </remarks>
410+ public Boolean MoveToPosition ( Int32 newPosition ) {
411+ return MoveToPoisition ( newPosition ) ;
412+ }
413+ /// <summary>
379414 /// Moves to the beginning of the file.
380415 /// </summary>
381416 public void Reset ( ) {
0 commit comments