2024-12-09 21:11:46 +01:00
|
|
|
// Copyright (C) 2023-present sam/u1f320 (vulpine.solutions)
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published
|
|
|
|
// by the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2024-05-27 15:53:54 +02:00
|
|
|
using NodaTime;
|
|
|
|
|
|
|
|
namespace Foxnouns.Backend.Database;
|
|
|
|
|
|
|
|
public class SnowflakeGenerator : ISnowflakeGenerator
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// Singleton instance of SnowflakeGenerator. Where possible, use an injected ISnowflakeGenerator instead
|
|
|
|
/// (see IServiceCollection.AddSnowflakeGenerator).
|
|
|
|
/// </summary>
|
|
|
|
public static SnowflakeGenerator Instance { get; } = new();
|
|
|
|
|
|
|
|
private readonly byte _processId;
|
|
|
|
private long _increment;
|
|
|
|
|
|
|
|
public SnowflakeGenerator(int? processId = null)
|
|
|
|
{
|
|
|
|
processId ??= Environment.ProcessId;
|
|
|
|
_processId = (byte)(processId % 32);
|
|
|
|
_increment = Random.Shared.NextInt64() % 4096;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Generate a snowflake ID for the given timestamp.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="time">An optional timestamp. If null, use the current timestamp.</param>
|
|
|
|
/// <returns>A new snowflake ID.</returns>
|
|
|
|
public Snowflake GenerateSnowflake(Instant? time = null)
|
|
|
|
{
|
|
|
|
time ??= SystemClock.Instance.GetCurrentInstant();
|
2024-12-08 15:07:25 +01:00
|
|
|
long increment = Interlocked.Increment(ref _increment);
|
|
|
|
int threadId = Environment.CurrentManagedThreadId % 32;
|
|
|
|
long timestamp = time.Value.ToUnixTimeMilliseconds() - Snowflake.Epoch;
|
2024-05-27 15:53:54 +02:00
|
|
|
|
2024-10-02 00:28:07 +02:00
|
|
|
return (timestamp << 22)
|
|
|
|
| (uint)(_processId << 17)
|
|
|
|
| (uint)(threadId << 12)
|
|
|
|
| (increment % 4096);
|
2024-05-27 15:53:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class SnowflakeGeneratorServiceExtensions
|
|
|
|
{
|
2024-10-02 00:28:07 +02:00
|
|
|
public static IServiceCollection AddSnowflakeGenerator(
|
|
|
|
this IServiceCollection services,
|
|
|
|
int? processId = null
|
2024-12-08 15:07:25 +01:00
|
|
|
) => services.AddSingleton<ISnowflakeGenerator>(new SnowflakeGenerator(processId));
|
2024-10-02 00:28:07 +02:00
|
|
|
}
|