Skip to content

Commit 6f34703

Browse files
committed
Reduce tmp buffers used by Asn1InputStream
1 parent 02c2dd8 commit 6f34703

9 files changed

Lines changed: 237 additions & 283 deletions

crypto/src/asn1/ASN1StreamParser.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ namespace Org.BouncyCastle.Asn1
55
{
66
public class Asn1StreamParser
77
{
8-
internal static Asn1StreamParser CreateSubParser(Stream sub, int parentDepth, int limit, byte[][] tmpBuffers) =>
9-
new Asn1StreamParser(sub, Asn1InputStream.DecrementDepth(parentDepth), limit, tmpBuffers);
8+
internal static Asn1StreamParser CreateSubParser(Stream sub, int parentDepth, int limit, byte[] tmp) =>
9+
new Asn1StreamParser(sub, Asn1InputStream.DecrementDepth(parentDepth), limit, tmp);
1010

1111
private readonly Stream m_in;
1212
private readonly int m_depth;
1313
private readonly int m_limit;
14-
15-
private readonly byte[][] m_tmpBuffers;
14+
private readonly byte[] m_tmp;
1615

1716
public Asn1StreamParser(Stream input)
1817
: this(input, Asn1InputStream.FindLimit(input))
@@ -25,19 +24,19 @@ public Asn1StreamParser(byte[] encoding)
2524
}
2625

2726
public Asn1StreamParser(Stream input, int limit)
28-
: this(input, Asn1InputStream.FindDepth(), limit, new byte[16][])
27+
: this(input, Asn1InputStream.FindDepth(), limit, tmp: new byte[16])
2928
{
3029
}
3130

32-
private Asn1StreamParser(Stream input, int depth, int limit, byte[][] tmpBuffers)
31+
private Asn1StreamParser(Stream input, int depth, int limit, byte[] tmp)
3332
{
3433
if (!input.CanRead)
3534
throw new ArgumentException("Expected stream to be readable", nameof(input));
3635

3736
m_in = input;
3837
m_depth = depth;
3938
m_limit = limit;
40-
m_tmpBuffers = tmpBuffers;
39+
m_tmp = tmp;
4140
}
4241

4342
public int Limit => m_limit;
@@ -65,7 +64,7 @@ internal IAsn1Convertible ImplParseObject(int tagHdr)
6564
throw new IOException("indefinite-length primitive encoding encountered");
6665

6766
IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(m_in, m_limit);
68-
Asn1StreamParser sp = CreateSubParser(indIn, m_depth, m_limit, m_tmpBuffers);
67+
Asn1StreamParser sp = CreateSubParser(indIn, m_depth, m_limit, m_tmp);
6968

7069
int tagClass = tagHdr & Asn1Tags.Private;
7170
if (0 != tagClass)
@@ -82,7 +81,7 @@ internal IAsn1Convertible ImplParseObject(int tagHdr)
8281
if (0 == (tagHdr & Asn1Tags.Flags))
8382
return ParseImplicitPrimitive(tagNo, defIn);
8483

85-
Asn1StreamParser sp = CreateSubParser(defIn, m_depth, subLimit, m_tmpBuffers);
84+
Asn1StreamParser sp = CreateSubParser(defIn, m_depth, subLimit, m_tmp);
8685

8786
int tagClass = tagHdr & Asn1Tags.Private;
8887
if (0 != tagClass)
@@ -188,7 +187,7 @@ internal IAsn1Convertible ParseImplicitPrimitive(int univTagNo, DefiniteLengthIn
188187

189188
try
190189
{
191-
return Asn1InputStream.CreatePrimitiveDerObject(univTagNo, defIn, m_tmpBuffers);
190+
return Asn1InputStream.CreatePrimitiveDerObject(univTagNo, defIn, m_tmp);
192191
}
193192
catch (ArgumentException e)
194193
{

crypto/src/asn1/Asn1InputStream.cs

Lines changed: 14 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
using System;
2-
using System.Diagnostics;
3-
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
4-
using System.Buffers.Binary;
5-
#endif
62
using System.IO;
73

84
using Org.BouncyCastle.Utilities;
@@ -65,8 +61,7 @@ internal static int FindLimit(Stream input)
6561
private readonly int m_depth;
6662
private readonly int m_limit;
6763
private readonly bool m_leaveOpen;
68-
69-
internal byte[][] m_tmpBuffers;
64+
private readonly byte[] m_tmp;
7065

7166
/**
7267
* Create an ASN1InputStream based on the input byte array. The length of DER objects in
@@ -106,11 +101,11 @@ public Asn1InputStream(Stream input, int limit)
106101
}
107102

108103
public Asn1InputStream(Stream input, int limit, bool leaveOpen)
109-
: this(input, FindDepth(), limit, leaveOpen, new byte[16][])
104+
: this(input, FindDepth(), limit, leaveOpen, tmp: new byte[16])
110105
{
111106
}
112107

113-
internal Asn1InputStream(Stream input, int depth, int limit, bool leaveOpen, byte[][] tmpBuffers)
108+
internal Asn1InputStream(Stream input, int depth, int limit, bool leaveOpen, byte[] tmp)
114109
: base(input)
115110
{
116111
if (!input.CanRead)
@@ -119,18 +114,16 @@ internal Asn1InputStream(Stream input, int depth, int limit, bool leaveOpen, byt
119114
m_depth = depth;
120115
m_limit = limit;
121116
m_leaveOpen = leaveOpen;
122-
m_tmpBuffers = tmpBuffers;
117+
m_tmp = tmp;
123118
}
124119

125120
private Asn1InputStream CreateSubStream(Stream sub, int limit) =>
126-
new Asn1InputStream(sub, DecrementDepth(m_depth), limit, leaveOpen: true, m_tmpBuffers);
121+
new Asn1InputStream(sub, DecrementDepth(m_depth), limit, leaveOpen: true, m_tmp);
127122

128123
public int Limit => m_limit;
129124

130125
protected override void Dispose(bool disposing)
131126
{
132-
m_tmpBuffers = null;
133-
134127
if (m_leaveOpen)
135128
{
136129
base.Detach(disposing);
@@ -152,7 +145,7 @@ private Asn1Object BuildObject(int tagHdr, int tagNo, int length)
152145
DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(s, length, limit: length);
153146

154147
if (0 == (tagHdr & Asn1Tags.Flags))
155-
return CreatePrimitiveDerObject(tagNo, defIn, m_tmpBuffers);
148+
return CreatePrimitiveDerObject(tagNo, defIn, m_tmp);
156149

157150
int tagClass = tagHdr & Asn1Tags.Private;
158151
if (0 != tagClass)
@@ -258,7 +251,7 @@ public Asn1Object ReadObject()
258251
throw new IOException("indefinite-length primitive encoding encountered");
259252

260253
IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(s, m_limit);
261-
Asn1StreamParser sp = Asn1StreamParser.CreateSubParser(indIn, m_depth, m_limit, m_tmpBuffers);
254+
Asn1StreamParser sp = Asn1StreamParser.CreateSubParser(indIn, m_depth, m_limit, m_tmp);
262255

263256
int tagClass = tagHdr & Asn1Tags.Private;
264257
if (0 != tagClass)
@@ -401,64 +394,22 @@ internal static int ReadLength(Stream s)
401394
return length;
402395
}
403396

404-
private static bool GetBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers, out byte[] contents)
405-
{
406-
int len = defIn.Remaining;
407-
if (len >= tmpBuffers.Length)
408-
{
409-
contents = defIn.ToArray();
410-
return false;
411-
}
412-
413-
byte[] buf = tmpBuffers[len];
414-
if (buf == null)
415-
{
416-
buf = tmpBuffers[len] = new byte[len];
417-
}
418-
419-
defIn.ReadAllIntoByteArray(buf);
420-
421-
contents = buf;
422-
return true;
423-
}
424-
425-
internal static Asn1Object CreatePrimitiveDerObject(int tagNo, DefiniteLengthInputStream defIn,
426-
byte[][] tmpBuffers)
397+
internal static Asn1Object CreatePrimitiveDerObject(int tagNo, DefiniteLengthInputStream defIn, byte[] tmp)
427398
{
428399
switch (tagNo)
429400
{
430401
case Asn1Tags.BmpString:
431-
{
432-
return CreateDerBmpString(defIn);
433-
}
402+
return DerBmpString.CreatePrimitive(defIn);
434403
case Asn1Tags.Boolean:
435-
{
436-
GetBuffer(defIn, tmpBuffers, out var contents);
437-
return DerBoolean.CreatePrimitive(contents);
438-
}
404+
return DerBoolean.CreatePrimitive(defIn);
439405
case Asn1Tags.Enumerated:
440-
{
441-
bool usedBuffer = GetBuffer(defIn, tmpBuffers, out var contents);
442-
return DerEnumerated.CreatePrimitive(contents, clone: usedBuffer);
443-
}
406+
return DerEnumerated.CreatePrimitive(defIn);
444407
case Asn1Tags.Null:
445-
{
446-
Asn1Null.CheckContentsLength(defIn.Remaining);
447-
Debug.Assert(defIn.Remaining == 0);
448-
return Asn1Null.CreatePrimitive();
449-
}
408+
return Asn1Null.CreatePrimitive(defIn);
450409
case Asn1Tags.ObjectIdentifier:
451-
{
452-
DerObjectIdentifier.CheckContentsLength(defIn.Remaining);
453-
bool usedBuffer = GetBuffer(defIn, tmpBuffers, out var contents);
454-
return DerObjectIdentifier.CreatePrimitive(contents, clone: usedBuffer);
455-
}
410+
return DerObjectIdentifier.CreatePrimitive(defIn, tmp);
456411
case Asn1Tags.RelativeOid:
457-
{
458-
Asn1RelativeOid.CheckContentsLength(defIn.Remaining);
459-
bool usedBuffer = GetBuffer(defIn, tmpBuffers, out var contents);
460-
return Asn1RelativeOid.CreatePrimitive(contents, clone: usedBuffer);
461-
}
412+
return Asn1RelativeOid.CreatePrimitive(defIn, tmp);
462413
}
463414

464415
byte[] bytes = defIn.ToArray();
@@ -513,88 +464,5 @@ internal static Asn1Object CreatePrimitiveDerObject(int tagNo, DefiniteLengthInp
513464
throw new IOException("unknown tag " + tagNo + " encountered");
514465
}
515466
}
516-
517-
private static DerBmpString CreateDerBmpString(DefiniteLengthInputStream defIn)
518-
{
519-
int remainingBytes = defIn.Remaining;
520-
if (0 != (remainingBytes & 1))
521-
throw new IOException("malformed BMPString encoding encountered");
522-
523-
int length = remainingBytes / 2;
524-
525-
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
526-
return DerBmpString.CreatePrimitive(length, defIn, (str, defIn) =>
527-
{
528-
int stringPos = 0;
529-
530-
Span<byte> buf = stackalloc byte[8];
531-
while (remainingBytes >= 8)
532-
{
533-
if (Streams.ReadFully(defIn, buf) != 8)
534-
throw new EndOfStreamException("EOF encountered in middle of BMPString");
535-
536-
str[stringPos] = (char)BinaryPrimitives.ReadUInt16BigEndian(buf[0..]);
537-
str[stringPos + 1] = (char)BinaryPrimitives.ReadUInt16BigEndian(buf[2..]);
538-
str[stringPos + 2] = (char)BinaryPrimitives.ReadUInt16BigEndian(buf[4..]);
539-
str[stringPos + 3] = (char)BinaryPrimitives.ReadUInt16BigEndian(buf[6..]);
540-
stringPos += 4;
541-
remainingBytes -= 8;
542-
}
543-
if (remainingBytes > 0)
544-
{
545-
if (Streams.ReadFully(defIn, buf) != remainingBytes)
546-
throw new EndOfStreamException("EOF encountered in middle of BMPString");
547-
548-
int bufPos = 0;
549-
do
550-
{
551-
int b1 = buf[bufPos++] << 8;
552-
int b2 = buf[bufPos++] & 0xFF;
553-
str[stringPos++] = (char)(b1 | b2);
554-
}
555-
while (bufPos < remainingBytes);
556-
}
557-
558-
if (0 != defIn.Remaining || str.Length != stringPos)
559-
throw new InvalidOperationException();
560-
});
561-
#else
562-
char[] str = new char[length];
563-
int stringPos = 0;
564-
565-
byte[] buf = new byte[8];
566-
while (remainingBytes >= 8)
567-
{
568-
if (Streams.ReadFully(defIn, buf, 0, 8) != 8)
569-
throw new EndOfStreamException("EOF encountered in middle of BMPString");
570-
571-
str[stringPos ] = (char)((buf[0] << 8) | (buf[1] & 0xFF));
572-
str[stringPos + 1] = (char)((buf[2] << 8) | (buf[3] & 0xFF));
573-
str[stringPos + 2] = (char)((buf[4] << 8) | (buf[5] & 0xFF));
574-
str[stringPos + 3] = (char)((buf[6] << 8) | (buf[7] & 0xFF));
575-
stringPos += 4;
576-
remainingBytes -= 8;
577-
}
578-
if (remainingBytes > 0)
579-
{
580-
if (Streams.ReadFully(defIn, buf, 0, remainingBytes) != remainingBytes)
581-
throw new EndOfStreamException("EOF encountered in middle of BMPString");
582-
583-
int bufPos = 0;
584-
do
585-
{
586-
int b1 = buf[bufPos++] << 8;
587-
int b2 = buf[bufPos++] & 0xFF;
588-
str[stringPos++] = (char)(b1 | b2);
589-
}
590-
while (bufPos < remainingBytes);
591-
}
592-
593-
if (0 != defIn.Remaining || str.Length != stringPos)
594-
throw new InvalidOperationException();
595-
596-
return DerBmpString.CreatePrimitive(str);
597-
#endif
598-
}
599467
}
600468
}

crypto/src/asn1/Asn1Null.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55

66
namespace Org.BouncyCastle.Asn1
77
{
8-
/**
9-
* A Null object.
10-
*/
8+
/// <summary>A NULL object.</summary>
119
public abstract class Asn1Null
1210
: Asn1Object
1311
{
@@ -19,8 +17,7 @@ private Meta() : base(typeof(Asn1Null), Asn1Tags.Null) {}
1917

2018
internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
2119
{
22-
CheckContentsLength(octetString.GetOctetsLength());
23-
return CreatePrimitive();
20+
return CreatePrimitive(octetString.GetOctetsLength());
2421
}
2522
}
2623

@@ -77,17 +74,20 @@ internal Asn1Null()
7774
{
7875
}
7976

80-
public override string ToString()
81-
{
82-
return "NULL";
83-
}
77+
public override string ToString() => "NULL";
8478

8579
internal static void CheckContentsLength(int contentsLength)
8680
{
8781
if (0 != contentsLength)
8882
throw new InvalidOperationException("malformed NULL encoding encountered");
8983
}
9084

91-
internal static Asn1Null CreatePrimitive() => DerNull.Instance;
85+
internal static Asn1Null CreatePrimitive(DefiniteLengthInputStream defIn) => CreatePrimitive(defIn.Remaining);
86+
87+
private static Asn1Null CreatePrimitive(int contentsLength)
88+
{
89+
CheckContentsLength(contentsLength);
90+
return DerNull.Instance;
91+
}
9292
}
9393
}

0 commit comments

Comments
 (0)