@@ -46,9 +46,12 @@ public static IAsnSerializable CreateSnmpData(byte[] buffer, int index, int coun
4646 throw new SnmpException ( "empty data buffer" ) ;
4747 }
4848
49- var slice = new byte [ count ] ;
50- Buffer . BlockCopy ( buffer , index , slice , 0 , count ) ;
51- return Parse ( slice ) ;
49+ if ( index == 0 && count == buffer . Length )
50+ {
51+ return Parse ( buffer ) ;
52+ }
53+
54+ return Parse ( new ReadOnlyMemory < byte > ( buffer , index , count ) ) ;
5255 }
5356
5457 /// <summary>
@@ -90,6 +93,11 @@ public static IAsnSerializable CreateSnmpData(int type, Stream stream)
9093 }
9194
9295 private static IAsnSerializable Parse ( byte [ ] payload )
96+ {
97+ return Parse ( new ReadOnlyMemory < byte > ( payload ) ) ;
98+ }
99+
100+ private static IAsnSerializable Parse ( ReadOnlyMemory < byte > payload )
93101 {
94102 try
95103 {
@@ -212,11 +220,20 @@ private static IAsnSerializable Parse(byte[] payload)
212220
213221 if ( tag . HasSameClassAndValue ( Asn1Tag . Sequence ) )
214222 {
215- try
223+ var sequenceKind = ClassifySequence ( trimmedPayload ) ;
224+ if ( sequenceKind == SequenceKind . Scope )
216225 {
217- return Scope . ReadFrom ( new AsnReader ( trimmedPayload , AsnEncodingRules . BER ) ) ;
226+ try
227+ {
228+ return Scope . ReadFrom ( new AsnReader ( trimmedPayload , AsnEncodingRules . BER ) ) ;
229+ }
230+ catch
231+ {
232+ return new EncodedSequence ( trimmedPayload ) ;
233+ }
218234 }
219- catch
235+
236+ if ( sequenceKind == SequenceKind . VarBindList )
220237 {
221238 try
222239 {
@@ -227,6 +244,8 @@ private static IAsnSerializable Parse(byte[] payload)
227244 return new EncodedSequence ( trimmedPayload ) ;
228245 }
229246 }
247+
248+ return new EncodedSequence ( trimmedPayload ) ;
230249 }
231250 }
232251 catch ( Exception ex ) when ( ex is not SnmpException )
@@ -239,54 +258,110 @@ private static IAsnSerializable Parse(byte[] payload)
239258
240259 private sealed class EncodedSequence : IAsnSerializable
241260 {
242- private readonly byte [ ] _encoded ;
261+ private readonly ReadOnlyMemory < byte > _encoded ;
243262
244- public EncodedSequence ( byte [ ] encoded )
263+ public EncodedSequence ( ReadOnlyMemory < byte > encoded )
245264 {
246- _encoded = encoded ?? throw new ArgumentNullException ( nameof ( encoded ) ) ;
265+ _encoded = encoded ;
247266 }
248267
249268 public SnmpType TypeCode => SnmpType . Sequence ;
250269
251270 public void WriteTo ( AsnWriter writer )
252271 {
253- writer . WriteEncodedValue ( _encoded ) ;
272+ writer . WriteEncodedValue ( _encoded . Span ) ;
273+ }
274+ }
275+
276+ private enum SequenceKind
277+ {
278+ Unknown = 0 ,
279+ Scope = 1 ,
280+ VarBindList = 2 ,
281+ }
282+
283+ private static SequenceKind ClassifySequence ( ReadOnlyMemory < byte > payload )
284+ {
285+ try
286+ {
287+ var reader = new AsnReader ( payload , AsnEncodingRules . BER ) ;
288+ var sequence = reader . ReadSequence ( ) ;
289+ if ( ! sequence . HasData )
290+ {
291+ return SequenceKind . Unknown ;
292+ }
293+
294+ var firstTag = sequence . PeekTag ( ) ;
295+ if ( firstTag . HasSameClassAndValue ( Asn1Tag . Sequence ) )
296+ {
297+ return SequenceKind . VarBindList ;
298+ }
299+
300+ if ( ! firstTag . HasSameClassAndValue ( Asn1Tag . PrimitiveOctetString ) )
301+ {
302+ return SequenceKind . Unknown ;
303+ }
304+
305+ // Scope starts with contextEngineId + contextName (octet strings),
306+ // followed by a context-specific constructed PDU.
307+ sequence . ReadOctetString ( ) ;
308+ if ( ! sequence . HasData || ! sequence . PeekTag ( ) . HasSameClassAndValue ( Asn1Tag . PrimitiveOctetString ) )
309+ {
310+ return SequenceKind . Unknown ;
311+ }
312+
313+ sequence . ReadOctetString ( ) ;
314+ if ( ! sequence . HasData )
315+ {
316+ return SequenceKind . Unknown ;
317+ }
318+
319+ var pduTag = sequence . PeekTag ( ) ;
320+ return pduTag . TagClass == TagClass . ContextSpecific && pduTag . IsConstructed
321+ ? SequenceKind . Scope
322+ : SequenceKind . Unknown ;
323+ }
324+ catch
325+ {
326+ return SequenceKind . Unknown ;
254327 }
255328 }
256329
257- private static byte [ ] TrimToSingleBerValue ( byte [ ] payload )
330+ private static ReadOnlyMemory < byte > TrimToSingleBerValue ( ReadOnlyMemory < byte > payload )
258331 {
259- if ( payload . Length < 2 )
332+ var span = payload . Span ;
333+
334+ if ( span . Length < 2 )
260335 {
261336 throw new SnmpException ( "invalid BER data" ) ;
262337 }
263338
264339 var offset = 1 ; // tag octet
265340
266341 // High-tag-number form.
267- if ( ( payload [ 0 ] & 0x1F ) == 0x1F )
342+ if ( ( span [ 0 ] & 0x1F ) == 0x1F )
268343 {
269344 while ( true )
270345 {
271- if ( offset >= payload . Length )
346+ if ( offset >= span . Length )
272347 {
273348 throw new SnmpException ( "invalid BER data" ) ;
274349 }
275350
276- var octet = payload [ offset ++ ] ;
351+ var octet = span [ offset ++ ] ;
277352 if ( ( octet & 0x80 ) == 0 )
278353 {
279354 break ;
280355 }
281356 }
282357 }
283358
284- if ( offset >= payload . Length )
359+ if ( offset >= span . Length )
285360 {
286361 throw new SnmpException ( "invalid BER data" ) ;
287362 }
288363
289- var firstLengthOctet = payload [ offset ++ ] ;
364+ var firstLengthOctet = span [ offset ++ ] ;
290365 long contentLength ;
291366 if ( ( firstLengthOctet & 0x80 ) == 0 )
292367 {
@@ -295,25 +370,25 @@ private static byte[] TrimToSingleBerValue(byte[] payload)
295370 else
296371 {
297372 var lengthOctetCount = firstLengthOctet & 0x7F ;
298- if ( lengthOctetCount == 0 || offset + lengthOctetCount > payload . Length )
373+ if ( lengthOctetCount == 0 || offset + lengthOctetCount > span . Length )
299374 {
300375 throw new SnmpException ( "invalid BER length" ) ;
301376 }
302377
303378 contentLength = 0 ;
304379 for ( var i = 0 ; i < lengthOctetCount ; i ++ )
305380 {
306- contentLength = ( contentLength << 8 ) | payload [ offset ++ ] ;
381+ contentLength = ( contentLength << 8 ) | span [ offset ++ ] ;
307382 }
308383 }
309384
310385 var totalLength = offset + contentLength ;
311- if ( totalLength <= 0 || totalLength > payload . Length )
386+ if ( totalLength <= 0 || totalLength > span . Length )
312387 {
313388 throw new SnmpException ( "invalid BER length" ) ;
314389 }
315390
316- if ( totalLength == payload . Length )
391+ if ( totalLength == span . Length )
317392 {
318393 return payload ;
319394 }
@@ -323,8 +398,6 @@ private static byte[] TrimToSingleBerValue(byte[] payload)
323398 throw new SnmpException ( "BER value too large" ) ;
324399 }
325400
326- var trimmed = new byte [ ( int ) totalLength ] ;
327- Buffer . BlockCopy ( payload , 0 , trimmed , 0 , trimmed . Length ) ;
328- return trimmed ;
401+ return payload . Slice ( 0 , ( int ) totalLength ) ;
329402 }
330403}
0 commit comments