Skip to content

Commit

Permalink
Base level improvements to Settings, DataProtection, Cipher password …
Browse files Browse the repository at this point in the history
…hashing (Argon2)

Some changes necessitated class assembly relocations
  • Loading branch information
ritchiecarroll committed Apr 3, 2024
1 parent 22ff2ac commit 6602ef2
Show file tree
Hide file tree
Showing 49 changed files with 6,156 additions and 187 deletions.
99 changes: 66 additions & 33 deletions src/Gemstone/ArrayExtensions/ArrayExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,21 @@ namespace Gemstone.ArrayExtensions;
/// </summary>
public static class ArrayExtensions
{
/// <summary>
/// Zero the given buffer in a way that will not be optimized away.
/// </summary>
/// <param name="buffer">Buffer to zero.</param>
/// <typeparam name="T"><see cref="Type"/> of array.</typeparam>
public static void Zero<T>(this T[] buffer)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer));

// Zero buffer
for (int i = 0; i < buffer.Length; i++)
buffer[i] = default!;
}

/// <summary>
/// Validates that the specified <paramref name="startIndex"/> and <paramref name="length"/> are valid within the given <paramref name="array"/>.
/// </summary>
Expand Down Expand Up @@ -101,7 +116,7 @@ private static void RaiseValidationError<T>(T[]? array, int startIndex, int leng
/// <param name="array">Source array.</param>
/// <param name="startIndex">Offset into <paramref name="array"/> array.</param>
/// <param name="length">Length of <paramref name="array"/> array to copy at <paramref name="startIndex"/> offset.</param>
/// <returns>A array of data copied from the specified portion of the source array.</returns>
/// <returns>An array of data copied from the specified portion of the source array.</returns>
/// <remarks>
/// <para>
/// Returned array will be extended as needed to make it the specified <paramref name="length"/>, but
Expand Down Expand Up @@ -272,7 +287,10 @@ public static T[] Combine<T>(this T[] source, int sourceOffset, int sourceCount,
/// </para>
/// </remarks>
/// <typeparam name="T"><see cref="Type"/> of array.</typeparam>
public static T[] Combine<T>(this T[] source, T[] other1, T[] other2) => new[] { source, other1, other2 }.Combine();
public static T[] Combine<T>(this T[] source, T[] other1, T[] other2)
{
return new[] { source, other1, other2 }.Combine();
}

/// <summary>
/// Combines arrays together into a single array.
Expand All @@ -294,7 +312,10 @@ public static T[] Combine<T>(this T[] source, int sourceOffset, int sourceCount,
/// </para>
/// </remarks>
/// <typeparam name="T"><see cref="Type"/> of array.</typeparam>
public static T[] Combine<T>(this T[] source, T[] other1, T[] other2, T[] other3) => new[] { source, other1, other2, other3 }.Combine();
public static T[] Combine<T>(this T[] source, T[] other1, T[] other2, T[] other3)
{
return new[] { source, other1, other2, other3 }.Combine();
}

/// <summary>
/// Combines arrays together into a single array.
Expand All @@ -317,7 +338,10 @@ public static T[] Combine<T>(this T[] source, int sourceOffset, int sourceCount,
/// </para>
/// </remarks>
/// <typeparam name="T"><see cref="Type"/> of array.</typeparam>
public static T[] Combine<T>(this T[] source, T[] other1, T[] other2, T[] other3, T[] other4) => new[] { source, other1, other2, other3, other4 }.Combine();
public static T[] Combine<T>(this T[] source, T[] other1, T[] other2, T[] other3, T[] other4)
{
return new[] { source, other1, other2, other3, other4 }.Combine();
}

/// <summary>
/// Combines array of arrays together into a single array.
Expand Down Expand Up @@ -447,33 +471,33 @@ public static int IndexOfSequence<T>(this T[] array, T[] sequenceToFind, int sta
// Search for first item in the sequence, if this doesn't exist then sequence doesn't exist
int index = Array.IndexOf(array, sequenceToFind[0], startIndex, length);

if (sequenceToFind.Length > 1)
{
bool foundSequence = false;
if (sequenceToFind.Length <= 1)
return index;

while (index > -1 && !foundSequence)
bool foundSequence = false;

while (index > -1 && !foundSequence)
{
// See if next bytes in sequence match
for (int x = 1; x < sequenceToFind.Length; x++)
{
// See if next bytes in sequence match
for (int x = 1; x < sequenceToFind.Length; x++)
// Make sure there's enough array remaining to accommodate this item
if (index + x < startIndex + length)
{
// Make sure there's enough array remaining to accommodate this item
if (index + x < startIndex + length)
{
// If sequence doesn't match, search for next first-item
if (array[index + x].CompareTo(sequenceToFind[x]) != 0)
{
index = Array.IndexOf(array, sequenceToFind[0], index + 1, startIndex + length - (index + 1));
break;
}

// If each item to find matched, we found the sequence
foundSequence = x == sequenceToFind.Length - 1;
}
else
// If sequence doesn't match, search for next first-item
if (array[index + x].CompareTo(sequenceToFind[x]) != 0)
{
// Ran out of array, return -1
index = -1;
index = Array.IndexOf(array, sequenceToFind[0], index + 1, startIndex + length - (index + 1));
break;
}

// If each item to find matched, we found the sequence
foundSequence = x == sequenceToFind.Length - 1;
}
else
{
// Ran out of array, return -1
index = -1;
}
}
}
Expand Down Expand Up @@ -566,11 +590,11 @@ public static int CountOfSequence<T>(this T[] array, T[] sequenceToCount, int st
if (index < 0)
return 0;

// Occurances counter
// Occurrences counter
int foundCount = 0;

// Search when the first array element is found, and the sequence can fit in the search range
bool searching = (index > -1) && (sequenceToCount.Length <= startIndex + searchLength - index);
bool searching = sequenceToCount.Length <= startIndex + searchLength - index;

while (searching)
{
Expand All @@ -595,7 +619,7 @@ public static int CountOfSequence<T>(this T[] array, T[] sequenceToCount, int st
}

// Continue searching if the array remaining can accommodate the sequence to find
searching = (index > -1) && (sequenceToCount.Length <= startIndex + searchLength - index);
searching = index > -1 && sequenceToCount.Length <= startIndex + searchLength - index;
}

return foundCount;
Expand Down Expand Up @@ -653,7 +677,7 @@ public static int CompareTo<T>(this T[]? source, T[]? other) where T : IComparab
int length1 = source.Length;
int length2 = other.Length;

// If array lengths are unequal, array with largest number of elements is assumed to be largest
// If array lengths are unequal, array with the largest number of elements is assumed to be largest
if (length1 != length2)
return length1.CompareTo(length2);

Expand Down Expand Up @@ -787,7 +811,10 @@ public static int CompareTo<T>(this T[]? source, int sourceOffset, T[]? other, i
/// to use the Linq function <see cref="Enumerable.Concat{T}"/> if you simply need to
/// iterate over the combined buffers.
/// </remarks>
public static byte[] Combine(this byte[] source, byte[] other1, byte[] other2) => new[] { source, other1, other2 }.Combine();
public static byte[] Combine(this byte[] source, byte[] other1, byte[] other2)
{
return new[] { source, other1, other2 }.Combine();
}

/// <summary>
/// Combines buffers together as a single image.
Expand All @@ -803,7 +830,10 @@ public static int CompareTo<T>(this T[]? source, int sourceOffset, T[]? other, i
/// to use the Linq function <see cref="Enumerable.Concat{T}"/> if you simply need to
/// iterate over the combined buffers.
/// </remarks>
public static byte[] Combine(this byte[] source, byte[] other1, byte[] other2, byte[] other3) => new[] { source, other1, other2, other3 }.Combine();
public static byte[] Combine(this byte[] source, byte[] other1, byte[] other2, byte[] other3)
{
return new[] { source, other1, other2, other3 }.Combine();
}

/// <summary>
/// Combines buffers together as a single image.
Expand All @@ -820,7 +850,10 @@ public static int CompareTo<T>(this T[]? source, int sourceOffset, T[]? other, i
/// to use the Linq function <see cref="Enumerable.Concat{T}"/> if you simply need to
/// iterate over the combined buffers.
/// </remarks>
public static byte[] Combine(this byte[] source, byte[] other1, byte[] other2, byte[] other3, byte[] other4) => new[] { source, other1, other2, other3, other4 }.Combine();
public static byte[] Combine(this byte[] source, byte[] other1, byte[] other2, byte[] other3, byte[] other4)
{
return new[] { source, other1, other2, other3, other4 }.Combine();
}

/// <summary>
/// Combines an array of buffers together as a single image.
Expand Down
125 changes: 125 additions & 0 deletions src/Gemstone/Caching/MemoryCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//******************************************************************************************************
// MemoryCache.cs - Gbtc
//
// Copyright © 2024, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
// file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 03/17/2024 - Ritchie Carroll
// Generated original version of source code.
//
//******************************************************************************************************
// ReSharper disable StaticMemberInGenericType

using System;
using System.Runtime.Caching;
using Gemstone.TypeExtensions;

namespace Gemstone.Caching;

/// <summary>
/// Represents a generic memory cache for a specific type <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">Type of value to cache.</typeparam>
/// <remarks>
/// Each type T should be unique unless cache can be safely shared.
/// </remarks>
internal static class MemoryCache<T>
{
// Desired use case is one static MemoryCache per type T:
private static readonly MemoryCache s_memoryCache;

static MemoryCache()
{
// Reflected type name is used to ensure unique cache name for generic types
string cacheName = $"{nameof(Gemstone)}Cache:{typeof(T).GetReflectedTypeName()}";
s_memoryCache = new MemoryCache(cacheName);
}

/// <summary>
/// Try to get a value from the memory cache.
/// </summary>
/// <param name="cacheName">Name to use as cache key -- this should be unique per <typeparamref name="T"/>.</param>
/// <param name="value">Value from cache if already cached; otherwise, default value for <typeparamref name="T"/>.</param>
/// <returns></returns>
public static bool TryGet(string cacheName, out T? value)
{
if (s_memoryCache.Get(cacheName) is not Lazy<T> cachedValue)
{
value = default;
return false;
}

value = cachedValue.Value;
return true;
}

/// <summary>
/// Gets or adds a value, based on result of <paramref name="valueFactory"/>, to the memory cache. Cache defaults to a 1-minute expiration.
/// </summary>
/// <param name="cacheName">Name to use as cache key -- this should be unique per <typeparamref name="T"/>.</param>
/// <param name="valueFactory">Function to generate value to add to cache -- only called if value is not already cached.</param>
/// <returns>
/// Value from cache if already cached; otherwise, new value generated by <paramref name="valueFactory"/>.
/// </returns>
public static T GetOrAdd(string cacheName, Func<T> valueFactory)
{
return GetOrAdd(cacheName, 1.0D, valueFactory);
}

/// <summary>
/// Gets or adds a value, based on result of <paramref name="valueFactory"/>, to the memory cache.
/// </summary>
/// <param name="cacheName">Name to use as cache key -- this should be unique per <typeparamref name="T"/>.</param>
/// <param name="expirationTime">Expiration time, in minutes, for cached value.</param>
/// <param name="valueFactory">Function to generate value to add to cache -- only called if value is not already cached.</param>
/// <returns>
/// Value from cache if already cached; otherwise, new value generated by <paramref name="valueFactory"/>.
/// </returns>
public static T GetOrAdd(string cacheName, double expirationTime, Func<T> valueFactory)
{
Lazy<T> newValue = new(valueFactory);
Lazy<T>? oldValue;

try
{
// Race condition exists here such that memory cache being referenced may
// be disposed between access and method invocation - hence the try/catch
oldValue = s_memoryCache.AddOrGetExisting(cacheName, newValue, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(expirationTime) }) as Lazy<T>;
}
catch
{
oldValue = null;
}

try
{
return (oldValue ?? newValue).Value;
}
catch
{
s_memoryCache.Remove(cacheName);
throw;
}
}

/// <summary>
/// Removes a value from the memory cache.
/// </summary>
/// <param name="cacheName">Specific named memory cache instance to remove from cache.</param>
public static void Remove(string cacheName)
{
s_memoryCache.Remove(cacheName);
}
}
8 changes: 4 additions & 4 deletions src/Gemstone/Collections/HashHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,14 @@ public static bool IsPrime(int candidate)

for (int divisor = 3; divisor <= limit; divisor += 2)
{
if ((candidate % divisor) == 0)
if (candidate % divisor == 0)
return false;
}

return true;
}

return (candidate == 2);
return candidate == 2;
}

public static int GetPrime(int min)
Expand All @@ -110,9 +110,9 @@ public static int GetPrime(int min)

// outside of our predefined table.
// compute the hard way.
for (int i = (min | 1); i < int.MaxValue; i += 2)
for (int i = min | 1; i < int.MaxValue; i += 2)
{
if (IsPrime(i) && ((i - 1) % HashPrime != 0))
if (IsPrime(i) && (i - 1) % HashPrime != 0)
return i;
}

Expand Down
13 changes: 12 additions & 1 deletion src/Gemstone/Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Gemstone.Console;
Expand Down Expand Up @@ -85,6 +87,8 @@ public enum UpdateType
/// </summary>
public static class Common
{
private static string? s_applicationName;

/// <summary>
/// Determines if the current system is a POSIX style environment.
/// </summary>
Expand All @@ -95,12 +99,19 @@ public static class Common
/// </para>
/// <para>
/// This property will return <c>true</c> for both MacOSX and Unix environments. Use the Platform property
/// of the <see cref="System.Environment.OSVersion"/> to determine more specific platform type, e.g.,
/// of the <see cref="Environment.OSVersion"/> to determine more specific platform type, e.g.,
/// MacOSX or Unix.
/// </para>
/// </remarks>
public static readonly bool IsPosixEnvironment = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);


/// <summary>
/// Gets the name of the current application.
/// </summary>
public static string ApplicationName =>
s_applicationName ??= Assembly.GetEntryAssembly()?.GetName().Name ?? Process.GetCurrentProcess().ProcessName;

/// <summary>
/// Converts <paramref name="value"/> to a <see cref="string"/> using an appropriate <see cref="TypeConverter"/>.
/// </summary>
Expand Down
Loading

0 comments on commit 6602ef2

Please sign in to comment.