Lexical.Utils
This is a namespace for various utility class libraries.
Class Libraries.
Contents:
Lexical.Utils.Permutation
Lexical.Utils.Permutation is a class library for unit testing. It's a toolkit for feeding different variations of parameters.
PermutationSetup is populated with properties and possible values.
PermutationSetup permutation = new PermutationSetup();
// Add property "Color"
permutation.Add("Color", "Red");
permutation.Add("Color", "Green");
permutation.Add("Color", "Blue");
// Add property "Number"
permutation.Add("Number", "10");
permutation.Add("Number", "100");
permutation.Add("Number", "1000");
// Add property "Size"
permutation.Add("Size", "Small");
permutation.Add("Size", "Medium");
permutation.Add("Size", "Large");
Every scenario can be iterated.
foreach (Scenario scenario in permutation.Scenarios)
{
string color = scenario["Color"].Name;
string number = scenario["Number"].Name;
string size = scenario["Size"].Name;
Console.WriteLine($"Color={color}, Number={number}, Size={size}");
}
The result. (Click here)
Color=Red, Number=10, Size=Small
Color=Green, Number=10, Size=Small
Color=Blue, Number=10, Size=Small
Color=Red, Number=100, Size=Small
Color=Green, Number=100, Size=Small
Color=Blue, Number=100, Size=Small
Color=Red, Number=1000, Size=Small
Color=Green, Number=1000, Size=Small
Color=Blue, Number=1000, Size=Small
Color=Red, Number=10, Size=Medium
Color=Green, Number=10, Size=Medium
Color=Blue, Number=10, Size=Medium
Color=Red, Number=100, Size=Medium
Color=Green, Number=100, Size=Medium
Color=Blue, Number=100, Size=Medium
Color=Red, Number=1000, Size=Medium
Color=Green, Number=1000, Size=Medium
Color=Blue, Number=1000, Size=Medium
Color=Red, Number=10, Size=Large
Color=Green, Number=10, Size=Large
Color=Blue, Number=10, Size=Large
Color=Red, Number=100, Size=Large
Color=Green, Number=100, Size=Large
Color=Blue, Number=100, Size=Large
Color=Red, Number=1000, Size=Large
Color=Green, Number=1000, Size=Large
Color=Blue, Number=1000, Size=Large
Each value can be associated with an object. The object is initialized with a delegate Func<Run, object>, and can be cleaned with another delegate Action<Run>.
PermutationSetup permutation = new PermutationSetup();
// Add property "Color"
permutation.Add("Color", "Red", null, run => Color.Red, run => { /*finalizer here*/ });
permutation.Add("Color", "Green", null, run => Color.Green);
permutation.Add("Color", "Blue", null, run => Color.Blue);
// Add property "Number"
permutation.Add("Number", "10", null, run => 10);
permutation.Add("Number", "100", null, run => 100);
permutation.Add("Number", "1000", null, run => 1000);
// Add property "Size"
permutation.Add("Size", "Small", null, run => Size.Small);
permutation.Add("Size", "Medium", null, run => Size.Medium);
permutation.Add("Size", "Large", null, run => Size.Large);
Use Run() to execute the initializers and finalizers.
foreach (Scenario scenario in permutation.Scenarios)
{
using (Run run = scenario.Run().Initialize())
{
Color color = (Color)run["Color"];
int number = (int)run["Number"];
Size size = (Size)run["Size"];
Console.WriteLine($"Color={color}, Number={number}, Size={size}");
}
}
Enums used in this permutation
enum Color { Red, Green, Blue }
enum Size { Small, Medium, Large }
Tuples
Tuples are not entirely trivial. There is variance in the features that are needed in different situations.
In this package there are tuples that come from the permutations of the following features.
Value-Typed and Reference-Typed
There are value-typed struct versions and reference-typed class versions of each tuple. Reference-typed tuples have suffix Object, for example "PairObject".
// Stack allocated
var pair_stack = new Pair<int, int>(1, 2);
// Heap allocated
var pair_heap = new PairObject<int, int>(1, 2);
Mutability
Tuples that are mutable have suffix Mutable. Immutable tuples don't have a suffix. Immutable tuples cache their hashcode, and mutable calculate as needed.
// Immutable
var pair_immutable = new Pair<int, int>(1, 2);
int hash_is_cached = pair_immutable.GetHashCode();
// Mutable
var pair_mutable = new PairMutable<int, int>(2, 3);
pair_mutable.a = 4;
pair_mutable.b = 6;
int hash_is_calculated = pair_mutable.GetHashCode();
Set Tuples
The elements of set tuples are interchangeable, the order of elements doesn't matter in hash-equals comparisons. That means that PairSet(1, 2) is equal to PairSet(2, 1). The name suffix is "Set", for example "PairSet".
// Create pairs
var pair1 = new PairSet<int>(1, 2);
var pair2 = new PairSet<int>(2, 1);
// Get equality comparer singleton instance
IEqualityComparer<PairSet<int>> equalsComparer = PairSet<int>.EqualityComparer.Default;
// Compare
bool areEqual = equalsComparer.Equals(pair1, pair2);
EqualityComparer
Each tuple class comes with an EqualityComparer implementation. There is a singleton instance TupleType.EqualityComparer.Default.
// Create pairs
var pair1 = new Pair<int, int>(1, 2);
var pair2 = new Pair<int, int>(2, 3);
// Get equality comparer singleton instance
IEqualityComparer<Pair<int, int>> equalsComparer = Pair<int, int>.EqualityComparer.Default;
// Compare
bool areEqual = equalsComparer.Equals(pair1, pair2);
Tuple types implement IEquatable<TupleType> any way, so they comparable as is. This makes value typed tuples comparable without boxing.
// Compare
bool areEqual = pair1.Equals(pair2);
Comparer
Each non-Set tuple class comes with a Comparer implementation. There is a singleton instance TupleType.Comparer.Default.
// Create pairs
var pair1 = new Pair<int, int>(1, 2);
var pair2 = new Pair<int, int>(2, 3);
// Get equality comparer singleton instance
IComparer<Pair<int, int>> comparer = Pair<int, int>.Comparer.Default;
// Compare
int c = comparer.Compare(pair1, pair2);
Tuple types implement IComparable<TupleType> any way, so they comparable as is. This makes value typed tuples comparable without boxing.
// Compare
int c = pair1.CompareTo(pair2);
Argument Count
There is a separate tuple type for cases with different number of arguments. The stem of the name is based on the count.
- Container
- Pair
- Triple
- Quad
- Pento
- Sextim
- Septim
- Octet
// One argument
var container = new ContainerMutableObject<string>("Hello");
container.a = "New Hello";
// Two arguments
var pair = new PairMutable<int, int>(1, 2);
// Three arguments
var triple = new TripleSet<int>(5, 6, 7);
// Four arguments
var quad = new QuadObject<byte, byte, byte, byte>(1, 2, 3, 4);
Classes
Name | Type | Hashcode | Mutability | Elements Ordered |
---|---|---|---|---|
Container<A> | struct | cached | immutable | are ordered |
ContainerObject<A> | class | cached | immutable | are ordered |
ContainerMutable<A> | struct | not cached | mutable | are ordered |
ContainerMutableObject<A> | class | not cached | mutable | are ordered |
Pair<A, B> | struct | cached | immutable | are ordered |
PairObject<A, B> | class | cached | immutable | are ordered |
PairMutable<A, B> | struct | not cached | mutable | are ordered |
PairMutableObject<A, B> | class | not cached | mutable | are ordered |
PairSet<T> | struct | cached | immutable | are interchangeable |
PairSetObject<T> | class | cached | immutable | are interchangeable |
PairSetMutable<T> | struct | not cached | mutable | are interchangeable |
PairSetMutableObject<T> | class | not cached | mutable | are interchangeable |
Triple<A, B, C> | struct | cached | immutable | are ordered |
TripleObject<A, B, C> | class | cached | immutable | are ordered |
TripleMutable<A, B, C> | struct | not cached | mutable | are ordered |
TripleMutableObject<A, B, C> | class | not cached | mutable | are ordered |
TripleSet<T> | struct | cached | immutable | are interchangeable |
TripleSetObject<T> | class | cached | immutable | are interchangeable |
TripleSetMutable<T> | struct | not cached | mutable | are interchangeable |
TripleSetMutableObject<T> | class | not cached | mutable | are interchangeable |
Quad<A, B, C, D> | struct | cached | immutable | are ordered |
QuadObject<A, B, C, D> | class | cached | immutable | are ordered |
QuadMutable<A, B, C, D> | struct | not cached | mutable | are ordered |
QuadMutableObject<A, B, C, D> | class | not cached | mutable | are ordered |
QuadSet<T> | struct | cached | immutable | are interchangeable |
QuadSetObject<T> | class | cached | immutable | are interchangeable |
QuadSetMutable<T> | struct | not cached | mutable | are interchangeable |
QuadSetMutableObject<T> | class | not cached | mutable | are interchangeable |
Pento<A, B, C, D, E> | struct | cached | immutable | are ordered |
PentoObject<A, B, C, D, E> | class | cached | immutable | are ordered |
PentoMutable<A, B, C, D, E> | struct | not cached | mutable | are ordered |
PentoMutableObject<A, B, C, D, E> | class | not cached | mutable | are ordered |
Sextim<A, B, C, D, E, F> | struct | cached | immutable | are ordered |
SextimObject<A, B, C, D, E, F> | class | cached | immutable | are ordered |
SextimMutable<A, B, C, D, E, F> | struct | not cached | mutable | are ordered |
SextimMutableObject<A, B, C, D, E, F> | class | not cached | mutable | are ordered |
Septim<A, B, C, D, E, F, G> | struct | cached | immutable | are ordered |
SeptimObject<A, B, C, D, E, F, G> | class | cached | immutable | are ordered |
SeptimMutable<A, B, C, D, E, F, G> | struct | not cached | mutable | are ordered |
SeptimMutableObject<A, B, C, D, E, F, G> | class | not cached | mutable | are ordered |
Octet<A, B, C, D, E, F, G, H> | struct | cached | immutable | are ordered |
OctetObject<A, B, C, D, E, F, G, H> | class | cached | immutable | are ordered |
OctetMutable<A, B, C, D, E, F, G, H> | struct | not cached | mutable | are ordered |
OctetMutableObject<A, B, C, D, E, F, G, H> | class | not cached | mutable | are ordered |
UnicodeString
UnicodeString addresses issues that come when handing strings with different encodings.
Construct
UnicodeString can be constructed from
- IEnumerable<byte>, byte[]
- IEnumerable<char>, char[], String
- IEnumerable<int>., int[]
UnicodeString is a wrapper that keeps reference to the source object that it was constructed from.
// Construct from String, IEnumerable<byte>, IEnumerable<char>, IEnumerable<int>
UnicodeString str = new UnicodeString("European castle \uD83C\uDFF0");
UnicodeString str8 = new UnicodeString(new byte[] { 69, 117, 114, 111, 112, 101, 97, 110, 32, 99, 97, 115, 116, 108, 101, 32, 240, 159, 143, 176 });
UnicodeString str16 = new UnicodeString(new char[] { 'E', 'u', 'r', 'o', 'p', 'e', 'a', 'n', ' ', 'c', 'a', 's', 't', 'l', 'e', ' ', '\uD83C', '\uDFF0' });
UnicodeString str32 = new UnicodeString(new int[] { 69, 117, 114, 111, 112, 101, 97, 110, 32, 99, 97, 115, 116, 108, 101, 32, 127984 });
Uniform use
Regardless of what source object is, as long as the encoding describes same unicode codepoints, UnicodeString describes the same uniform content.
Console.WriteLine($"Src={str8.Source.GetType().Name}, Str=\"{str8}\", Length={str8.Length}, Hashcode={str8.GetHashCode()}");
Console.WriteLine($"Src={str16.Source.GetType().Name}, Str=\"{str16}\", Length={str16.Length}, Hashcode={str16.GetHashCode()}");
Console.WriteLine($"Src={str32.Source.GetType().Name}, Str=\"{str32}\", Length={str32.Length}, Hashcode={str32.GetHashCode()}");
Encoding
Stack allocated IEnumerables and IEnumerators to UTF8, UTF16 and UTF32 encodings can be used.
// Encode to utf-8, utf-16, utf-32 codepoints
foreach (byte codepoint in str) { }
foreach (char codepoint in str) { }
foreach (int codepoint in str) { }
// Get stack allocated enumerator encoders
for (UTF8Enumerator enumr = str8.GetEnumeratorUTF8(); enumr.MoveNext();) { }
for (UTF16Enumerator enumr = str8.GetEnumeratorUTF16(); enumr.MoveNext();) { }
for (UTF32Enumerator enumr = str8.GetEnumerator(); enumr.MoveNext();) { }
// Length to different encodings can be calculated
int utf8length = str8.UTF8Length;
int utf16length = str8.UTF16Length;
int utf32length = str8.Length;
And converted to arrays of different encodings.
// Convert to utf 8/16/32 codepoints
Console.WriteLine($"Utf8 [ {String.Join(", ", str.ToUtf8Array())} ]");
Console.WriteLine($"Utf16 [ {String.Join(", ", str.ToUtf16Array())} ]");
Console.WriteLine($"Utf32 [ {String.Join(", ", str.ToArray())} ]");
Hash-Equals
UnicodeStrings can be compared for unicode equality.
// Compare for unicode equality and hashcode
var equals1 = str8.Equals(str32);
var equals2 = str16.Equals(str8);
var hashcode_equal1 = str8.GetHashCode() == str16.GetHashCode();
var hashcode_equal2 = str16.GetHashCode() == str8.GetHashCode();
Assign to IList
UnicodeString can be assigned to
- IEnumerable<byte>, IList<byte>
- IEnumerable<char>, IList<char>
- IEnumerable<int>, IList<int>
// Assign UTF8 to ILists of various encodings
IList<byte> utf8_to_utf8 = str8;
IList<char> utf8_to_utf16 = str8;
IList<int> utf8_to_utf32 = str8;
// Assign UTF16 to ILists of various encodings
IList<byte> utf16_to_utf8 = str16;
IList<char> utf16_to_utf16 = str16;
IList<int> utf16_to_utf32 = str16;
// Assign UTF32 to ILists of various encodings
IList<byte> utf32_to_utf8 = str32;
IList<char> utf32_to_utf16 = str32;
IList<int> utf32_to_utf32 = str32;
File Scanner
FileScanner is a tool that scans directories recursively searching for files. It uses concurrent threads.
FileScanner fileScanner = new FileScanner();
It needs to be populated with filters, such as wildcard pattern with .AddFilename(string).
fileScanner.AddFilename("*.zip");
Or regular expressions.
fileScanner.AddRegex(".", new Regex(@".*\.zip"));
Or glob pattern.
fileScanner.AddGlobPattern(".", "**.zip");
Search is started when IEnumerator<string> is taken from the scanner.
foreach (string filepath in fileScanner)
{
Console.WriteLine(filepath);
}
Exceptions that occur at real-time can be captured into concurrent collection.
fileScanner.errors = new ConcurrentBag<Exception>();
Glob Pattern
Glob pattern is a file matching pattern, an alternatite to wildcard pattern.
Glob pattern uses the following notation:
- "*" matches to string of characters within the same directory.
- "?" matches to any character except directory separator.
- "**" matches to any characters, including directory separators.
Regex globPattern = new GlobPattern("**/*.dll/**.resources");
Test matching. "somefile.zip/" is excluded because of the separator in the pattern.
string[] files = new[]
{
"somefile.zip",
"somefile.zip/somefile.ext",
"somefile.zip/somefile.ext/someresource",
"somefile.zip/somelib.dll",
"somefile.zip/somelib.dll/someresource.resources",
"somelib.dll",
"somelib.dll/someresource.resources",
};
foreach (string filename in files.Where(fn => globPattern.IsMatch(fn)))
Console.WriteLine(filename);
The result.
somefile.zip/somelib.dll/someresource.resources