reader.ReadStartMap();
Helpers.VerifyValue(reader, dupeKey);
reader.ReadInt32();
+
+ int bytesRead = reader.BytesRead;
+ int bytesRemaining = reader.BytesRemaining;
+ CborReaderState state = reader.PeekState();
+
Assert.Throws<FormatException>(() => Helpers.VerifyValue(reader, dupeKey));
+
+ // ensure reader state is preserved
+ Assert.Equal(bytesRead, reader.BytesRead);
+ Assert.Equal(bytesRemaining, reader.BytesRemaining);
+ Assert.Equal(state, reader.PeekState());
}
[Theory]
reader.ReadInt32(); // value is always an integer
}
+ int bytesRead = reader.BytesRead;
+ int bytesRemaining = reader.BytesRemaining;
+ CborReaderState state = reader.PeekState();
+
// the final element violates sorting invariant
Assert.Throws<FormatException>(() => Helpers.VerifyValue(reader, keySequence.Last()));
+
+ // ensure reader state is preserved
+ Assert.Equal(bytesRead, reader.BytesRead);
+ Assert.Equal(bytesRemaining, reader.BytesRemaining);
+ Assert.Equal(state, reader.PeekState());
}
[Theory]
}
[Theory]
+ [InlineData(CborConformanceLevel.Strict, 42)]
+ [InlineData(CborConformanceLevel.Rfc7049Canonical, 42)]
+ [InlineData(CborConformanceLevel.Ctap2Canonical, 42)]
+ [InlineData(CborConformanceLevel.Strict, "foobar")]
+ [InlineData(CborConformanceLevel.Rfc7049Canonical, "foobar")]
+ [InlineData(CborConformanceLevel.Ctap2Canonical, "foobar")]
+ [InlineData(CborConformanceLevel.Strict, new object[] { new object[] { "x", "y" } })]
+ [InlineData(CborConformanceLevel.Rfc7049Canonical, new object[] { new object[] { "x", "y" } })]
+ [InlineData(CborConformanceLevel.Ctap2Canonical, new object[] { new object[] { "x", "y" } })]
+ internal static void WriteMap_DuplicateKeys_StrictConformance_ShouldBeRecoverableError(CborConformanceLevel level, object dupeKey)
+ {
+ byte[] expected = PerformWrite(attemptDuplicateWrite: false);
+ byte[] actual = PerformWrite(attemptDuplicateWrite: true);
+ Assert.Equal(expected.ByteArrayToHex(), actual.ByteArrayToHex());
+
+ byte[] PerformWrite(bool attemptDuplicateWrite)
+ {
+ using var writer = new CborWriter(level);
+ writer.WriteStartMap(2);
+ Helpers.WriteValue(writer, dupeKey);
+ writer.WriteInt32(0);
+
+ if (attemptDuplicateWrite)
+ {
+ Assert.Throws<InvalidOperationException>(() => Helpers.WriteValue(writer, dupeKey));
+ }
+
+ // wrap dupe key in an array to satisfy key sorting & uniqueness constraints
+ Helpers.WriteValue(writer, new object[] { dupeKey });
+ writer.WriteInt32(0);
+ writer.WriteEndMap();
+
+ return writer.ToArray();
+ }
+ }
+
+ [Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(3)]
int cmp = CborConformanceLevelHelpers.CompareEncodings(previousKeyEncoding, currentKeyEncoding, ConformanceLevel);
if (cmp > 0)
{
+ ResetBuffer(currentKeyRange.Offset);
throw new FormatException("CBOR map keys are not in sorted encoding order.");
}
else if (cmp == 0 && CborConformanceLevelHelpers.RequiresUniqueKeys(ConformanceLevel))
{
+ ResetBuffer(currentKeyRange.Offset);
throw new FormatException("CBOR map contains duplicate keys.");
}
}
if (!previousKeys.Add(currentKeyRange))
{
+ ResetBuffer(currentKeyRange.Offset);
throw new FormatException("CBOR map contains duplicate keys.");
}
}
_cachedState = CborReaderState.Unknown;
}
+ private void ResetBuffer(int position)
+ {
+ _buffer = _originalBuffer.Slice(position);
+ _bytesRead = position;
+ // invalidate the state cache
+ _cachedState = CborReaderState.Unknown;
+ }
+
private void EnsureBuffer(int length)
{
if (_buffer.Length < length)
{
Debug.Assert(_currentKeyOffset != null && _currentValueOffset == null);
- _currentValueOffset = _offset;
+ int currentValueOffset = _offset;
if (CborConformanceLevelHelpers.RequiresUniqueKeys(ConformanceLevel))
{
(int Offset, int KeyLength, int TotalLength) currentKeyRange =
(_currentKeyOffset.Value,
- _currentValueOffset.Value - _currentKeyOffset.Value,
- 0);
+ currentValueOffset - _currentKeyOffset.Value,
+ 0);
if (ranges.Contains(currentKeyRange))
{
- // TODO: check if rollback is necessary here
+ // reset writer state to before the offending key write
+ _buffer.AsSpan(currentKeyRange.Offset, _offset).Fill(0);
+ _offset = currentKeyRange.Offset;
+
throw new InvalidOperationException("Duplicate key encoding in CBOR map.");
}
}
+
+ _currentValueOffset = currentValueOffset;
}
private void HandleValueWritten()
private void AdvanceDataItemCounters()
{
- _remainingDataItems--;
- _isTagContext = false;
-
if (_currentKeyOffset != null) // this is a map context
{
if (_currentValueOffset == null)
HandleValueWritten();
}
}
+
+ _remainingDataItems--;
+ _isTagContext = false;
}
private void WriteInitialByte(CborInitialByte initialByte)