Skip to content

Commit

Permalink
Split AsnReader into a class and a ref struct (AsnValueReader)
Browse files Browse the repository at this point in the history
Largely, the intent of this change was to reduce the number of temporary readers required for deeply structured payloads (`ReadSequence` returns a new reader), while still maintaining the better usability of the class; and, frankly, to understand what the impact of splitting functionality into "bare metal" and "more usable" pieces was.

```
foreach (file in AsnReader.*)
{
    Copy/paste the entirety of the `partial class AsnReader`;
    Change the top version to say ref partial struct AsnValueReader;
    Replace all AsnReader calls in the top version to say AsnValueReader.
    Replace all ReadOnlyMemory in the top portion with ReadOnlySpan;
    Remove ArraySegment overloads from the top portion (when present);
    Rewrite the bottom portion to open a ref reader at the state the memory is tracking and defer work into the ref reader;
    Run all the existing tests to ensure that nothing broke in that piece;
}
```

Once that was done, I changed the asn.xslt to generate all of the decoding in terms of AsnValueReader.  This didn't change field generation from ReadOnlyMemory to ReadOnlySpan (and the types from struct to ref struct)--a change that could be done later--but instead added a new ReadOnlyMemory input parameter so it can use Overlaps to slice the memory as appropriate (with a fallback of ToArray()).

While I was editing asn.xslt I fixed probably all of the whitespace errors it generated.  I still left the "stylecop, have no opinion about whitespace" pragma since the generator isn't run as part of most people's (or the official) builds.  This also turns on the nullability checks in all of the generated structs.
  • Loading branch information
bartonjs authored Jan 22, 2020
1 parent 0dcdbac commit 47ab0dc
Show file tree
Hide file tree
Showing 114 changed files with 4,563 additions and 2,382 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ internal partial struct AlgorithmIdentifierAsn
{
internal Oid Algorithm;
internal ReadOnlyMemory<byte>? Parameters;

internal void Encode(AsnWriter writer)
{
Encode(writer, Asn1Tag.Sequence);
}

internal void Encode(AsnWriter writer, Asn1Tag tag)
{
writer.PushSequence(tag);

writer.WriteObjectIdentifier(Algorithm);

if (Parameters.HasValue)
Expand All @@ -39,37 +39,35 @@ internal static AlgorithmIdentifierAsn Decode(ReadOnlyMemory<byte> encoded, AsnE
{
return Decode(Asn1Tag.Sequence, encoded, ruleSet);
}

internal static AlgorithmIdentifierAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
{
AsnReader reader = new AsnReader(encoded, ruleSet);
Decode(reader, expectedTag, out AlgorithmIdentifierAsn decoded);
AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet);

Decode(ref reader, expectedTag, encoded, out AlgorithmIdentifierAsn decoded);
reader.ThrowIfNotEmpty();
return decoded;
}

internal static void Decode(AsnReader reader, out AlgorithmIdentifierAsn decoded)
internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory<byte> rebind, out AlgorithmIdentifierAsn decoded)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));

Decode(reader, Asn1Tag.Sequence, out decoded);
Decode(ref reader, Asn1Tag.Sequence, rebind, out decoded);
}

internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out AlgorithmIdentifierAsn decoded)
internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory<byte> rebind, out AlgorithmIdentifierAsn decoded)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));

decoded = default;
AsnReader sequenceReader = reader.ReadSequence(expectedTag);

AsnValueReader sequenceReader = reader.ReadSequence(expectedTag);
ReadOnlySpan<byte> rebindSpan = rebind.Span;
int offset;
ReadOnlySpan<byte> tmpSpan;

decoded.Algorithm = sequenceReader.ReadObjectIdentifier();

if (sequenceReader.HasData)
{
decoded.Parameters = sequenceReader.ReadEncodedValue();
tmpSpan = sequenceReader.ReadEncodedValue();
decoded.Parameters = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ internal partial struct AttributeAsn
{
internal Oid AttrType;
internal ReadOnlyMemory<byte>[] AttrValues;

internal void Encode(AsnWriter writer)
{
Encode(writer, Asn1Tag.Sequence);
}

internal void Encode(AsnWriter writer, Asn1Tag tag)
{
writer.PushSequence(tag);

writer.WriteObjectIdentifier(AttrType);

writer.PushSetOf();
for (int i = 0; i < AttrValues.Length; i++)
{
writer.WriteEncodedValue(AttrValues[i].Span);
writer.WriteEncodedValue(AttrValues[i].Span);
}
writer.PopSetOf();

Expand All @@ -42,33 +42,30 @@ internal static AttributeAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRul
{
return Decode(Asn1Tag.Sequence, encoded, ruleSet);
}

internal static AttributeAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
{
AsnReader reader = new AsnReader(encoded, ruleSet);
Decode(reader, expectedTag, out AttributeAsn decoded);
AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet);

Decode(ref reader, expectedTag, encoded, out AttributeAsn decoded);
reader.ThrowIfNotEmpty();
return decoded;
}

internal static void Decode(AsnReader reader, out AttributeAsn decoded)
internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory<byte> rebind, out AttributeAsn decoded)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));

Decode(reader, Asn1Tag.Sequence, out decoded);
Decode(ref reader, Asn1Tag.Sequence, rebind, out decoded);
}

internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out AttributeAsn decoded)
internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory<byte> rebind, out AttributeAsn decoded)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));

decoded = default;
AsnReader sequenceReader = reader.ReadSequence(expectedTag);
AsnReader collectionReader;

AsnValueReader sequenceReader = reader.ReadSequence(expectedTag);
AsnValueReader collectionReader;
ReadOnlySpan<byte> rebindSpan = rebind.Span;
int offset;
ReadOnlySpan<byte> tmpSpan;

decoded.AttrType = sequenceReader.ReadObjectIdentifier();

// Decode SEQUENCE OF for AttrValues
Expand All @@ -79,7 +76,8 @@ internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out Attribute

while (collectionReader.HasData)
{
tmpItem = collectionReader.ReadEncodedValue();
tmpSpan = collectionReader.ReadEncodedValue();
tmpItem = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray();
tmpList.Add(tmpItem);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ internal partial struct CurveAsn
internal ReadOnlyMemory<byte> A;
internal ReadOnlyMemory<byte> B;
internal ReadOnlyMemory<byte>? Seed;

internal void Encode(AsnWriter writer)
{
Encode(writer, Asn1Tag.Sequence);
}

internal void Encode(AsnWriter writer, Asn1Tag tag)
{
writer.PushSequence(tag);

writer.WriteOctetString(A.Span);
writer.WriteOctetString(B.Span);

Expand All @@ -41,46 +41,43 @@ internal static CurveAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules r
{
return Decode(Asn1Tag.Sequence, encoded, ruleSet);
}

internal static CurveAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
{
AsnReader reader = new AsnReader(encoded, ruleSet);
Decode(reader, expectedTag, out CurveAsn decoded);
AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet);

Decode(ref reader, expectedTag, encoded, out CurveAsn decoded);
reader.ThrowIfNotEmpty();
return decoded;
}

internal static void Decode(AsnReader reader, out CurveAsn decoded)
internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory<byte> rebind, out CurveAsn decoded)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));

Decode(reader, Asn1Tag.Sequence, out decoded);
Decode(ref reader, Asn1Tag.Sequence, rebind, out decoded);
}

internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out CurveAsn decoded)
internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory<byte> rebind, out CurveAsn decoded)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));

decoded = default;
AsnReader sequenceReader = reader.ReadSequence(expectedTag);

AsnValueReader sequenceReader = reader.ReadSequence(expectedTag);
ReadOnlySpan<byte> rebindSpan = rebind.Span;
int offset;
ReadOnlySpan<byte> tmpSpan;


if (sequenceReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory<byte> tmpA))
if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan))
{
decoded.A = tmpA;
decoded.A = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray();
}
else
{
decoded.A = sequenceReader.ReadOctetString();
}


if (sequenceReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory<byte> tmpB))
if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan))
{
decoded.B = tmpB;
decoded.B = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray();
}
else
{
Expand All @@ -91,9 +88,9 @@ internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out CurveAsn
if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.PrimitiveBitString))
{

if (sequenceReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory<byte> tmpSeed))
if (sequenceReader.TryReadPrimitiveBitStringValue(out _, out tmpSpan))
{
decoded.Seed = tmpSeed;
decoded.Seed = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray();
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ internal partial struct DigestInfoAsn
{
internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn DigestAlgorithm;
internal ReadOnlyMemory<byte> Digest;

internal void Encode(AsnWriter writer)
{
Encode(writer, Asn1Tag.Sequence);
}

internal void Encode(AsnWriter writer, Asn1Tag tag)
{
writer.PushSequence(tag);

DigestAlgorithm.Encode(writer);
writer.WriteOctetString(Digest.Span);
writer.PopSequence(tag);
Expand All @@ -34,37 +34,34 @@ internal static DigestInfoAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRu
{
return Decode(Asn1Tag.Sequence, encoded, ruleSet);
}

internal static DigestInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
{
AsnReader reader = new AsnReader(encoded, ruleSet);
Decode(reader, expectedTag, out DigestInfoAsn decoded);
AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet);

Decode(ref reader, expectedTag, encoded, out DigestInfoAsn decoded);
reader.ThrowIfNotEmpty();
return decoded;
}

internal static void Decode(AsnReader reader, out DigestInfoAsn decoded)
internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory<byte> rebind, out DigestInfoAsn decoded)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));

Decode(reader, Asn1Tag.Sequence, out decoded);
Decode(ref reader, Asn1Tag.Sequence, rebind, out decoded);
}

internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out DigestInfoAsn decoded)
internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory<byte> rebind, out DigestInfoAsn decoded)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));

decoded = default;
AsnReader sequenceReader = reader.ReadSequence(expectedTag);

System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(sequenceReader, out decoded.DigestAlgorithm);
AsnValueReader sequenceReader = reader.ReadSequence(expectedTag);
ReadOnlySpan<byte> rebindSpan = rebind.Span;
int offset;
ReadOnlySpan<byte> tmpSpan;

System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.DigestAlgorithm);

if (sequenceReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory<byte> tmpDigest))
if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan))
{
decoded.Digest = tmpDigest;
decoded.Digest = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray();
}
else
{
Expand Down
Loading

0 comments on commit 47ab0dc

Please sign in to comment.