Skip to content

Commit

Permalink
fix algorithmic complexity issue; add regression tests
Browse files Browse the repository at this point in the history
  • Loading branch information
peteroupc committed Jan 18, 2022
1 parent d8b2287 commit b4117db
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 17 deletions.
8 changes: 2 additions & 6 deletions CBOR/PeterO/Cbor/CBORObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7942,12 +7942,8 @@ private static int MapCompare(
if (listACount != listBCount) {
return listACount < listBCount ? -1 : 1;
}
var sortedASet = new List<CBORObject>(mapA.Keys);
var sortedBSet = new List<CBORObject>(mapB.Keys);
// DebugUtility.Log("---sorting mapA's keys");
sortedASet.Sort();
// DebugUtility.Log("---sorting mapB's keys");
sortedBSet.Sort();
var sortedASet = new List<CBORObject>(PropertyMap.GetSortedKeys(mapA));
var sortedBSet = new List<CBORObject>(PropertyMap.GetSortedKeys(mapB));
// DebugUtility.Log("---done sorting");
listACount = sortedASet.Count;
listBCount = sortedBSet.Count;
Expand Down
23 changes: 22 additions & 1 deletion CBOR/PeterO/Cbor/PropertyMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ private sealed class OrderedDictionary<TKey, TValue> :
private readonly IDictionary<TKey, TValue> dict;
private readonly LinkedList<TKey> list;
public OrderedDictionary() {
this.dict = new Dictionary<TKey, TValue>();
this.dict = new SortedDictionary<TKey, TValue>();
this.list = new LinkedList<TKey>();
}
public void Add(KeyValuePair<TKey, TValue> kvp) {
Expand Down Expand Up @@ -179,6 +179,12 @@ public ICollection<TKey> Keys {
}
}

public ICollection<TKey> SortedKeys {
get {
return this.dict.Keys;
}
}

public ICollection<TValue> Values {
get {
return new ValueWrapper<TKey, TValue>(this.dict, this.list);
Expand Down Expand Up @@ -895,6 +901,21 @@ public static object EnumToObjectAsInteger(Enum value) {
Convert.ToInt32(value, CultureInfo.InvariantCulture));
}

public static ICollection<TKey>
GetSortedKeys<TKey, TValue>(
IDictionary<TKey, TValue> dict) {
var odict = dict as OrderedDictionary<TKey, TValue>;
if (odict != null) {
return odict.SortedKeys;
}
var sdict = dict as SortedDictionary<TKey, TValue>;
if (sdict != null) {
return sdict.Keys;
}
throw new InvalidOperationException("Internal error: Map doesn't" +
"\u0020support sorted keys");
}

public static ICollection<KeyValuePair<TKey, TValue>>
GetEntries<TKey, TValue>(
IDictionary<TKey, TValue> dict) {
Expand Down
61 changes: 54 additions & 7 deletions CBORTest/CBORGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,13 @@ private static void GenerateArgument(
private static int[]
valueMajorTypes = {
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4,
4, 5, 6, 6, 7, 7, 7, 7, 7, 7,
4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7,
};

private static int[]
valueMajorTypesHighDepth = {
0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 6, 7,
};

private static int[] valueMajorTypesHighLength = {
Expand Down Expand Up @@ -130,20 +136,50 @@ private static void GenerateUtf8(IRandomGenExtended ra, ByteWriter bs, int
}
}

private void GenerateSmall(IRandomGenExtended r, int depth, ByteWriter bs) {
int v = r.GetInt32(100);
if (v < 25) {
GenerateArgument(r, 0, r.GetInt32(100), bs);
} else if (v < 35) {
bs.Write(0x41);
bs.Write(0x20);
} else if (v < 45) {
bs.Write(0x41);
bs.Write(0x20);
} else if (v < 50) {
bs.Write(0x81);
this.GenerateSmall(r, depth + 1, bs);
} else if (v < 53) {
bs.Write(0xa2);
bs.Write(0xf7);
bs.Write(0xf6);
this.GenerateSmall(r, depth + 1, bs);
bs.Write(0xf5);
} else if (v < 80) {
bs.Write(r.GetInt32(0x40));
} else if (v < 100) {
bs.Write(r.GetInt32(0x60));
}
}
private void Generate(IRandomGenExtended r, int depth, ByteWriter bs) {
int majorType = valueMajorTypes[r.GetInt32(valueMajorTypes.Length)];
if (depth > 6) {
majorType = valueMajorTypesHighDepth[r.GetInt32(
valueMajorTypesHighDepth.Length)];
}
if (bs.ByteLength > 2000000) {
majorType = valueMajorTypesHighLength[r.GetInt32(
valueMajorTypesHighLength.Length)];
}
if (majorType == 3 || majorType == 2) {
if (majorType == 3 || majorType == 2) { // Byte and text strings
int len = r.GetInt32(1000);
if (r.GetInt32(50) == 0 && depth < 2) {
var v = (long)r.GetInt32(100000) * r.GetInt32(100000);
len = (int)(v / 100000);
}
if (depth > 6) {
} else if (depth > 6) {
len = r.GetInt32(100) == 0 ? 1 : 0;
} else if (depth > 2) {
len = r.GetInt32(16) + 1;
}
// TODO: Ensure key uniqueness
if (r.GetInt32(2) == 0) {
Expand Down Expand Up @@ -174,11 +210,18 @@ private void Generate(IRandomGenExtended r, int depth, ByteWriter bs) {
}
}
return;
} else if (majorType == 4 || majorType == 5) {
} else if (majorType == 4 || majorType == 5) { // Arrays and maps
int len = r.GetInt32(8);
if (r.GetInt32(50) == 0 && depth < 2) {
var v = (long)r.GetInt32(1000) * r.GetInt32(1000);
len = (int)(v / 1000);
} else if (depth > 6) {
len = r.GetInt32(100) == 0 ? 1 : 0;
} else if (depth > 2) {
len = r.GetInt32(3) + 1;
}
if (depth > 6) {
len = r.GetInt32(100) < 50 ? 1 : (r.GetInt32(100) < 10 ? 2 : 0);
}
bool indefiniteLength = r.GetInt32(2) == 0;
if (indefiniteLength) {
Expand All @@ -187,7 +230,11 @@ private void Generate(IRandomGenExtended r, int depth, ByteWriter bs) {
GenerateArgument(r, majorType, len, bs);
}
for (int i = 0; i < len; ++i) {
this.Generate(r, depth + 1, bs);
if (depth > 6) {
this.GenerateSmall(r, depth + 1, bs);
} else {
this.Generate(r, depth + 1, bs);
}
if (majorType == 5) {
this.Generate(r, depth + 1, bs);
}
Expand Down Expand Up @@ -229,7 +276,7 @@ private void Generate(IRandomGenExtended r, int depth, ByteWriter bs) {
}
break;
}
if (majorType == 6) {
if (majorType == 6) { // Tags
this.Generate(r, depth + 1, bs);
}
}
Expand Down
139 changes: 139 additions & 0 deletions CBORTest/CBORObjectTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9721,6 +9721,145 @@ public void TestFromJsonStringLongKindIntOrFloat2() {
Assert.IsTrue(cbor.AsDoubleValue() == Double.NegativeInfinity);
}

[Test]
public void TestRoundTripRegressions() {
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
(byte)0xba, 0x00, 0x00, 0x00, 0x03,
(byte)0xf9,
(byte)0x83, 0x1d,
(byte)0xda,
(byte)0xb6,
(byte)0xda, 0x50, 0x56, 0x1a, 0x50,
(byte)0xe3, 0x2c, 0x7a, 0x16,
(byte)0xfa, 0x50, 0x32, 0x73, 0x07,
(byte)0xfa, (byte)0xb9, 0x2d, 0x73, (byte)0xce, 0x38, (byte)0xd0,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
(byte)0xbf,
(byte)0x9f,
(byte)0xbf, 0x39, 0x20,
(byte)0x8f, 0x4a, 0x1f, 0x46, 0x26, 0x0b, 0x3e, 0x72, 0x2c, 0x7f, 0x11,
0x2e, 0x39,
(byte)0x9d,
(byte)0xba, 0x1a, 0x11,
(byte)0x8d,
(byte)0xc0,
(byte)0xb4, 0x38,
(byte)0xb6,
(byte)0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
(byte)0xd8, 0x3b,
(byte)0x99, 0x00, 0x02, 0x3b, 0x05,
(byte)0xbb,
(byte)0xea,
(byte)0x8e, 0x4b,
(byte)0xd3, 0x5e, 0x22,
(byte)0x9f, 0x59, 0x00, 0x00,
(byte)0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x20,
(byte)0xbf, 0x1a, 0x00, 0x00, 0x00, 0x61,
(byte)0xb9, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x00, 0x0e,
(byte)0xba, 0x00, 0x00, 0x00, 0x00,
(byte)0xff,
(byte)0xff,
(byte)0xff,
(byte)0xd8, 0x22,
(byte)0xf8,
(byte)0x93,
(byte)0xd9,
(byte)0xaf, 0x33, 0x19,
(byte)0xf0,
(byte)0xf0,
(byte)0xf9,
(byte)0x85,
(byte)0x93,
(byte)0x99, 0x00, 0x01, 0x3a,
(byte)0xb5,
(byte)0xfb, 0x4d, 0x43,
(byte)0x98, 0x00,
(byte)0xff, (byte)0xfa, (byte)0xb0, (byte)0xb4, (byte)0xdc, 0x6d,
(byte)0xff,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
(byte)0xdb, 0x0d,
(byte)0xcb, 0x5d, 0x78,
(byte)0x92,
(byte)0xc2,
(byte)0xc7, 0x2b,
(byte)0xb9, 0x00, 0x02, 0x39,
(byte)0xee,
(byte)0xa0, (byte)0xa0, 0x1a, 0x0e, (byte)0xd9, (byte)0xec, (byte)0xca,
(byte)0xf2,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
(byte)0xbf,
(byte)0xfb,
(byte)0xb1, 0x21,
(byte)0x93,
(byte)0x8c,
(byte)0xc6,
(byte)0xf3,
(byte)0xcf,
(byte)0xb7, (byte)0xf8, 0x76, 0x18, (byte)0xda, 0x39, 0x60, (byte)0xf4,
(byte)0xff,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
(byte)0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, (byte)0xf0, 0x0d, 0x2a, 0x21,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
(byte)0xba, 0x00, 0x00, 0x00, 0x02,
(byte)0xf9, 0x48, 0x37,
(byte)0xda,
(byte)0xb5, 0x72,
(byte)0xcf,
(byte)0xf8, 0x31, 0x3b, 0x06, 0x78,
(byte)0xdb, 0x44, 0x7d, (byte)0xba, (byte)0xbd, 0x7d, 0x39, (byte)0x98,
(byte)0xb9,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
}
[Test]
public void TestMapCompareRegressions() {
CBORObject m1, m2;
m1 = CBORObject.NewMap().Add(3, 4).Add(1, 2);
m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
Assert.AreEqual(0, m1.CompareTo(m2));
TestCommon.CompareTestEqualAndConsistent(m1, m2);
m1 = CBORObject.NewMap().Add(3, 2).Add(1, 2);
m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
TestCommon.CompareTestLess(m1, m2);
m1 = CBORObject.NewMap().Add(3, 7).Add(1, 2);
m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
TestCommon.CompareTestGreater(m1, m2);
m1 = CBORObject.NewMap().Add(3, 4).Add(1, 0);
m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
TestCommon.CompareTestLess(m1, m2);
m1 = CBORObject.NewMap().Add(3, 4).Add(1, 7);
m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
TestCommon.CompareTestGreater(m1, m2);
}
[Test]
public void TestToObject_TypeMapper() {
var mapper = new CBORTypeMapper()
Expand Down
2 changes: 1 addition & 1 deletion CBORTest/CBORTest.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk='Microsoft.NET.Sdk'>
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp5.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition=' &apos;$(Configuration)&apos;==&apos;Debug&apos; '>
<DebugType>full</DebugType>
Expand Down
3 changes: 2 additions & 1 deletion CBORTest/CBORTestCommon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ public static CBORObject RandomNumberOrRational(IRandomGenExtended rand) {
public static CBORObject RandomCBORMap(IRandomGenExtended rand, int depth) {
int x = rand.GetInt32(100);
int count = (x < 80) ? 2 : ((x < 93) ? 1 : ((x < 98) ? 0 : 10));
CBORObject cborRet = CBORObject.NewMap();
CBORObject cborRet = rand.GetInt32(100) < 30 ?
CBORObject.NewOrderedMap() : CBORObject.NewMap();
for (var i = 0; i < count; ++i) {
CBORObject key = RandomCBORObject(rand, depth + 1);
CBORObject value = RandomCBORObject(rand, depth + 1);
Expand Down
2 changes: 1 addition & 1 deletion CBORTest/TestCommon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ private static bool ByteArraysEqual(
return true;
}

private static bool ByteArraysEqual(byte[] arr1, byte[] arr2) {
public static bool ByteArraysEqual(byte[] arr1, byte[] arr2) {
if (arr1 == null) {
return arr2 == null;
}
Expand Down

0 comments on commit b4117db

Please sign in to comment.