Skip to content

Commit 4ccf6ef

Browse files
committed
Make sure empty and null strings are not allowed
1 parent 5584c1f commit 4ccf6ef

2 files changed

Lines changed: 56 additions & 2 deletions

File tree

Src/FastData.Tests/FastDataGeneratorTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,46 @@ public void Generate_ThrowOnInvalidType()
123123
Assert.Throws<InvalidOperationException>(() => FastDataGenerator.Generate([DateTime.Now, DateTime.UtcNow], new FastDataConfig(StructureType.Array), new DummyGenerator()));
124124
}
125125

126+
[Fact]
127+
public void Generate_StringKeys_EmptyString_ThrowsFriendlyException()
128+
{
129+
FastDataConfig config = new FastDataConfig();
130+
Assert.Throws<InvalidOperationException>(() => FastDataGenerator.Generate([""], config, new DummyGenerator()));
131+
}
132+
133+
[Fact]
134+
public void Generate_StringKeys_Null_ThrowsFriendlyException()
135+
{
136+
string[] keys = [null!];
137+
FastDataConfig config = new FastDataConfig();
138+
Assert.Throws<InvalidOperationException>(() => FastDataGenerator.Generate(keys, config, new DummyGenerator()));
139+
}
140+
141+
[Fact]
142+
public void Generate_IgnoreCase_DeduplicatesCaseInsensitive()
143+
{
144+
FastDataConfig config = new FastDataConfig(StructureType.Array)
145+
{
146+
IgnoreCase = true,
147+
DeduplicationMode = DeduplicationMode.SortThrowOnDup
148+
};
149+
150+
Assert.Throws<InvalidOperationException>(() => FastDataGenerator.Generate(["abc", "ABC"], config, new DummyGenerator()));
151+
}
152+
153+
[Fact(Skip = "Known issue: all-negative sparse inputs can select Elias-Fano and throw during construction.")]
154+
public void Generate_Auto_AllNegativeSparse_DoesNotThrow()
155+
{
156+
int[] keys = Enumerable.Range(1, 1000).Select(static x => -x * 20).ToArray();
157+
FastDataConfig config = new FastDataConfig();
158+
159+
ContextCaptureGenerator generator = new ContextCaptureGenerator();
160+
Exception? error = Record.Exception(() => FastDataGenerator.Generate(keys, config, generator));
161+
162+
Assert.Null(error);
163+
Assert.False(generator.Context is EliasFanoContext<int>);
164+
}
165+
126166
[Fact]
127167
public void Generate_Overloads_String_Work()
128168
{

Src/FastData/FastDataGenerator.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,35 @@ private static string GenerateStringInternal<TValue>(ReadOnlyMemory<string> keys
7676
if (!values.IsEmpty && keys.Length != values.Length)
7777
throw new InvalidOperationException("The number of values does not match the number of keys.");
7878

79+
ReadOnlySpan<string> keySpan = keys.Span;
80+
81+
for (int i = 0; i < keySpan.Length; i++)
82+
{
83+
string? key = keySpan[i];
84+
85+
if (key is null)
86+
throw new InvalidOperationException("Keys cannot contain null values.");
87+
88+
if (key.Length == 0)
89+
throw new InvalidOperationException("Keys cannot contain empty strings.");
90+
}
91+
7992
factory ??= NullLoggerFactory.Instance;
8093

8194
ILogger logger = factory.CreateLogger(typeof(FastDataGenerator));
8295
LogUserStructureType(logger, fdCfg.StructureType);
8396

8497
int oldCount = keys.Length;
8598

86-
DeduplicateKeys(fdCfg, keys, values, StringComparer.Ordinal, StringComparer.Ordinal, out keys, out values, out int newCount);
99+
StringComparer comparer = fdCfg.IgnoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
100+
DeduplicateKeys(fdCfg, keys, values, comparer, comparer, out keys, out values, out int newCount);
87101

88102
if (oldCount == newCount)
89103
LogNumberOfKeys(logger, newCount);
90104
else
91105
LogNumberOfUniqueKeys(logger, oldCount, newCount);
92106

93-
ReadOnlySpan<string> keySpan = keys.Span;
107+
keySpan = keys.Span;
94108

95109
const KeyType keyType = KeyType.String;
96110
LogKeyType(logger, keyType);

0 commit comments

Comments
 (0)