Skip to content

Commit b4117db

Browse files
committed
fix algorithmic complexity issue; add regression tests
1 parent d8b2287 commit b4117db

File tree

7 files changed

+221
-17
lines changed

7 files changed

+221
-17
lines changed

CBOR/PeterO/Cbor/CBORObject.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7942,12 +7942,8 @@ private static int MapCompare(
79427942
if (listACount != listBCount) {
79437943
return listACount < listBCount ? -1 : 1;
79447944
}
7945-
var sortedASet = new List<CBORObject>(mapA.Keys);
7946-
var sortedBSet = new List<CBORObject>(mapB.Keys);
7947-
// DebugUtility.Log("---sorting mapA's keys");
7948-
sortedASet.Sort();
7949-
// DebugUtility.Log("---sorting mapB's keys");
7950-
sortedBSet.Sort();
7945+
var sortedASet = new List<CBORObject>(PropertyMap.GetSortedKeys(mapA));
7946+
var sortedBSet = new List<CBORObject>(PropertyMap.GetSortedKeys(mapB));
79517947
// DebugUtility.Log("---done sorting");
79527948
listACount = sortedASet.Count;
79537949
listBCount = sortedBSet.Count;

CBOR/PeterO/Cbor/PropertyMap.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ private sealed class OrderedDictionary<TKey, TValue> :
3535
private readonly IDictionary<TKey, TValue> dict;
3636
private readonly LinkedList<TKey> list;
3737
public OrderedDictionary() {
38-
this.dict = new Dictionary<TKey, TValue>();
38+
this.dict = new SortedDictionary<TKey, TValue>();
3939
this.list = new LinkedList<TKey>();
4040
}
4141
public void Add(KeyValuePair<TKey, TValue> kvp) {
@@ -179,6 +179,12 @@ public ICollection<TKey> Keys {
179179
}
180180
}
181181

182+
public ICollection<TKey> SortedKeys {
183+
get {
184+
return this.dict.Keys;
185+
}
186+
}
187+
182188
public ICollection<TValue> Values {
183189
get {
184190
return new ValueWrapper<TKey, TValue>(this.dict, this.list);
@@ -895,6 +901,21 @@ public static object EnumToObjectAsInteger(Enum value) {
895901
Convert.ToInt32(value, CultureInfo.InvariantCulture));
896902
}
897903

904+
public static ICollection<TKey>
905+
GetSortedKeys<TKey, TValue>(
906+
IDictionary<TKey, TValue> dict) {
907+
var odict = dict as OrderedDictionary<TKey, TValue>;
908+
if (odict != null) {
909+
return odict.SortedKeys;
910+
}
911+
var sdict = dict as SortedDictionary<TKey, TValue>;
912+
if (sdict != null) {
913+
return sdict.Keys;
914+
}
915+
throw new InvalidOperationException("Internal error: Map doesn't" +
916+
"\u0020support sorted keys");
917+
}
918+
898919
public static ICollection<KeyValuePair<TKey, TValue>>
899920
GetEntries<TKey, TValue>(
900921
IDictionary<TKey, TValue> dict) {

CBORTest/CBORGenerator.cs

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,13 @@ private static void GenerateArgument(
8484
private static int[]
8585
valueMajorTypes = {
8686
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4,
87-
4, 5, 6, 6, 7, 7, 7, 7, 7, 7,
87+
4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7,
88+
};
89+
90+
private static int[]
91+
valueMajorTypesHighDepth = {
92+
0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
93+
5, 5, 5, 5, 5, 5, 6, 7,
8894
};
8995

9096
private static int[] valueMajorTypesHighLength = {
@@ -130,20 +136,50 @@ private static void GenerateUtf8(IRandomGenExtended ra, ByteWriter bs, int
130136
}
131137
}
132138

139+
private void GenerateSmall(IRandomGenExtended r, int depth, ByteWriter bs) {
140+
int v = r.GetInt32(100);
141+
if (v < 25) {
142+
GenerateArgument(r, 0, r.GetInt32(100), bs);
143+
} else if (v < 35) {
144+
bs.Write(0x41);
145+
bs.Write(0x20);
146+
} else if (v < 45) {
147+
bs.Write(0x41);
148+
bs.Write(0x20);
149+
} else if (v < 50) {
150+
bs.Write(0x81);
151+
this.GenerateSmall(r, depth + 1, bs);
152+
} else if (v < 53) {
153+
bs.Write(0xa2);
154+
bs.Write(0xf7);
155+
bs.Write(0xf6);
156+
this.GenerateSmall(r, depth + 1, bs);
157+
bs.Write(0xf5);
158+
} else if (v < 80) {
159+
bs.Write(r.GetInt32(0x40));
160+
} else if (v < 100) {
161+
bs.Write(r.GetInt32(0x60));
162+
}
163+
}
133164
private void Generate(IRandomGenExtended r, int depth, ByteWriter bs) {
134165
int majorType = valueMajorTypes[r.GetInt32(valueMajorTypes.Length)];
166+
if (depth > 6) {
167+
majorType = valueMajorTypesHighDepth[r.GetInt32(
168+
valueMajorTypesHighDepth.Length)];
169+
}
135170
if (bs.ByteLength > 2000000) {
136171
majorType = valueMajorTypesHighLength[r.GetInt32(
137172
valueMajorTypesHighLength.Length)];
138173
}
139-
if (majorType == 3 || majorType == 2) {
174+
if (majorType == 3 || majorType == 2) { // Byte and text strings
140175
int len = r.GetInt32(1000);
141176
if (r.GetInt32(50) == 0 && depth < 2) {
142177
var v = (long)r.GetInt32(100000) * r.GetInt32(100000);
143178
len = (int)(v / 100000);
144-
}
145-
if (depth > 6) {
179+
} else if (depth > 6) {
146180
len = r.GetInt32(100) == 0 ? 1 : 0;
181+
} else if (depth > 2) {
182+
len = r.GetInt32(16) + 1;
147183
}
148184
// TODO: Ensure key uniqueness
149185
if (r.GetInt32(2) == 0) {
@@ -174,11 +210,18 @@ private void Generate(IRandomGenExtended r, int depth, ByteWriter bs) {
174210
}
175211
}
176212
return;
177-
} else if (majorType == 4 || majorType == 5) {
213+
} else if (majorType == 4 || majorType == 5) { // Arrays and maps
178214
int len = r.GetInt32(8);
179215
if (r.GetInt32(50) == 0 && depth < 2) {
180216
var v = (long)r.GetInt32(1000) * r.GetInt32(1000);
181217
len = (int)(v / 1000);
218+
} else if (depth > 6) {
219+
len = r.GetInt32(100) == 0 ? 1 : 0;
220+
} else if (depth > 2) {
221+
len = r.GetInt32(3) + 1;
222+
}
223+
if (depth > 6) {
224+
len = r.GetInt32(100) < 50 ? 1 : (r.GetInt32(100) < 10 ? 2 : 0);
182225
}
183226
bool indefiniteLength = r.GetInt32(2) == 0;
184227
if (indefiniteLength) {
@@ -187,7 +230,11 @@ private void Generate(IRandomGenExtended r, int depth, ByteWriter bs) {
187230
GenerateArgument(r, majorType, len, bs);
188231
}
189232
for (int i = 0; i < len; ++i) {
190-
this.Generate(r, depth + 1, bs);
233+
if (depth > 6) {
234+
this.GenerateSmall(r, depth + 1, bs);
235+
} else {
236+
this.Generate(r, depth + 1, bs);
237+
}
191238
if (majorType == 5) {
192239
this.Generate(r, depth + 1, bs);
193240
}
@@ -229,7 +276,7 @@ private void Generate(IRandomGenExtended r, int depth, ByteWriter bs) {
229276
}
230277
break;
231278
}
232-
if (majorType == 6) {
279+
if (majorType == 6) { // Tags
233280
this.Generate(r, depth + 1, bs);
234281
}
235282
}

CBORTest/CBORObjectTest.cs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9721,6 +9721,145 @@ public void TestFromJsonStringLongKindIntOrFloat2() {
97219721
Assert.IsTrue(cbor.AsDoubleValue() == Double.NegativeInfinity);
97229722
}
97239723

9724+
[Test]
9725+
public void TestRoundTripRegressions() {
9726+
{
9727+
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
9728+
var bytes = new byte[] {
9729+
(byte)0xba, 0x00, 0x00, 0x00, 0x03,
9730+
(byte)0xf9,
9731+
(byte)0x83, 0x1d,
9732+
(byte)0xda,
9733+
(byte)0xb6,
9734+
(byte)0xda, 0x50, 0x56, 0x1a, 0x50,
9735+
(byte)0xe3, 0x2c, 0x7a, 0x16,
9736+
(byte)0xfa, 0x50, 0x32, 0x73, 0x07,
9737+
(byte)0xfa, (byte)0xb9, 0x2d, 0x73, (byte)0xce, 0x38, (byte)0xd0,
9738+
};
9739+
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
9740+
}
9741+
{
9742+
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
9743+
var bytes = new byte[] {
9744+
(byte)0xbf,
9745+
(byte)0x9f,
9746+
(byte)0xbf, 0x39, 0x20,
9747+
(byte)0x8f, 0x4a, 0x1f, 0x46, 0x26, 0x0b, 0x3e, 0x72, 0x2c, 0x7f, 0x11,
9748+
0x2e, 0x39,
9749+
(byte)0x9d,
9750+
(byte)0xba, 0x1a, 0x11,
9751+
(byte)0x8d,
9752+
(byte)0xc0,
9753+
(byte)0xb4, 0x38,
9754+
(byte)0xb6,
9755+
(byte)0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
9756+
(byte)0xd8, 0x3b,
9757+
(byte)0x99, 0x00, 0x02, 0x3b, 0x05,
9758+
(byte)0xbb,
9759+
(byte)0xea,
9760+
(byte)0x8e, 0x4b,
9761+
(byte)0xd3, 0x5e, 0x22,
9762+
(byte)0x9f, 0x59, 0x00, 0x00,
9763+
(byte)0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x20,
9764+
(byte)0xbf, 0x1a, 0x00, 0x00, 0x00, 0x61,
9765+
(byte)0xb9, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x00, 0x0e,
9766+
(byte)0xba, 0x00, 0x00, 0x00, 0x00,
9767+
(byte)0xff,
9768+
(byte)0xff,
9769+
(byte)0xff,
9770+
(byte)0xd8, 0x22,
9771+
(byte)0xf8,
9772+
(byte)0x93,
9773+
(byte)0xd9,
9774+
(byte)0xaf, 0x33, 0x19,
9775+
(byte)0xf0,
9776+
(byte)0xf0,
9777+
(byte)0xf9,
9778+
(byte)0x85,
9779+
(byte)0x93,
9780+
(byte)0x99, 0x00, 0x01, 0x3a,
9781+
(byte)0xb5,
9782+
(byte)0xfb, 0x4d, 0x43,
9783+
(byte)0x98, 0x00,
9784+
(byte)0xff, (byte)0xfa, (byte)0xb0, (byte)0xb4, (byte)0xdc, 0x6d,
9785+
(byte)0xff,
9786+
};
9787+
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
9788+
}
9789+
{
9790+
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
9791+
var bytes = new byte[] {
9792+
(byte)0xdb, 0x0d,
9793+
(byte)0xcb, 0x5d, 0x78,
9794+
(byte)0x92,
9795+
(byte)0xc2,
9796+
(byte)0xc7, 0x2b,
9797+
(byte)0xb9, 0x00, 0x02, 0x39,
9798+
(byte)0xee,
9799+
(byte)0xa0, (byte)0xa0, 0x1a, 0x0e, (byte)0xd9, (byte)0xec, (byte)0xca,
9800+
(byte)0xf2,
9801+
};
9802+
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
9803+
}
9804+
{
9805+
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
9806+
var bytes = new byte[] {
9807+
(byte)0xbf,
9808+
(byte)0xfb,
9809+
(byte)0xb1, 0x21,
9810+
(byte)0x93,
9811+
(byte)0x8c,
9812+
(byte)0xc6,
9813+
(byte)0xf3,
9814+
(byte)0xcf,
9815+
(byte)0xb7, (byte)0xf8, 0x76, 0x18, (byte)0xda, 0x39, 0x60, (byte)0xf4,
9816+
(byte)0xff,
9817+
};
9818+
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
9819+
}
9820+
{
9821+
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
9822+
var bytes = new byte[] {
9823+
(byte)0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9824+
0x00, 0x02, (byte)0xf0, 0x0d, 0x2a, 0x21,
9825+
};
9826+
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
9827+
}
9828+
{
9829+
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
9830+
var bytes = new byte[] {
9831+
(byte)0xba, 0x00, 0x00, 0x00, 0x02,
9832+
(byte)0xf9, 0x48, 0x37,
9833+
(byte)0xda,
9834+
(byte)0xb5, 0x72,
9835+
(byte)0xcf,
9836+
(byte)0xf8, 0x31, 0x3b, 0x06, 0x78,
9837+
(byte)0xdb, 0x44, 0x7d, (byte)0xba, (byte)0xbd, 0x7d, 0x39, (byte)0x98,
9838+
(byte)0xb9,
9839+
};
9840+
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
9841+
}
9842+
}
9843+
[Test]
9844+
public void TestMapCompareRegressions() {
9845+
CBORObject m1, m2;
9846+
m1 = CBORObject.NewMap().Add(3, 4).Add(1, 2);
9847+
m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
9848+
Assert.AreEqual(0, m1.CompareTo(m2));
9849+
TestCommon.CompareTestEqualAndConsistent(m1, m2);
9850+
m1 = CBORObject.NewMap().Add(3, 2).Add(1, 2);
9851+
m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
9852+
TestCommon.CompareTestLess(m1, m2);
9853+
m1 = CBORObject.NewMap().Add(3, 7).Add(1, 2);
9854+
m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
9855+
TestCommon.CompareTestGreater(m1, m2);
9856+
m1 = CBORObject.NewMap().Add(3, 4).Add(1, 0);
9857+
m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
9858+
TestCommon.CompareTestLess(m1, m2);
9859+
m1 = CBORObject.NewMap().Add(3, 4).Add(1, 7);
9860+
m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
9861+
TestCommon.CompareTestGreater(m1, m2);
9862+
}
97249863
[Test]
97259864
public void TestToObject_TypeMapper() {
97269865
var mapper = new CBORTypeMapper()

CBORTest/CBORTest.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk='Microsoft.NET.Sdk'>
22
<PropertyGroup>
3-
<TargetFramework>netcoreapp2.1</TargetFramework>
3+
<TargetFramework>netcoreapp5.0</TargetFramework>
44
</PropertyGroup>
55
<PropertyGroup Condition=' &apos;$(Configuration)&apos;==&apos;Debug&apos; '>
66
<DebugType>full</DebugType>

CBORTest/CBORTestCommon.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ public static CBORObject RandomNumberOrRational(IRandomGenExtended rand) {
119119
public static CBORObject RandomCBORMap(IRandomGenExtended rand, int depth) {
120120
int x = rand.GetInt32(100);
121121
int count = (x < 80) ? 2 : ((x < 93) ? 1 : ((x < 98) ? 0 : 10));
122-
CBORObject cborRet = CBORObject.NewMap();
122+
CBORObject cborRet = rand.GetInt32(100) < 30 ?
123+
CBORObject.NewOrderedMap() : CBORObject.NewMap();
123124
for (var i = 0; i < count; ++i) {
124125
CBORObject key = RandomCBORObject(rand, depth + 1);
125126
CBORObject value = RandomCBORObject(rand, depth + 1);

CBORTest/TestCommon.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,7 @@ private static bool ByteArraysEqual(
761761
return true;
762762
}
763763

764-
private static bool ByteArraysEqual(byte[] arr1, byte[] arr2) {
764+
public static bool ByteArraysEqual(byte[] arr1, byte[] arr2) {
765765
if (arr1 == null) {
766766
return arr2 == null;
767767
}

0 commit comments

Comments
 (0)