[ТЕХНИЧЕСКАЯ СПЕЦИФИКАЦИЯ]

ИДЕС СПЕЦИФИКАЦИЯ

READONLY STRUCT • 16 БАЙТ • BASE32 КИРИЛЛИЦА

Версия: 1.0
|
Дата:
|
Статус: АКТУАЛЬНО
|
Язык: RU
ОСОБЕННОСТИ КОДИРОВКИ Строгая валидация алфавита

Ides использует только кириллицу и арабские цифры в Base32 кодировке. Латинские символы не поддерживаются. Методы парсинга строго валидируют алфавит.

◎ ОБЗОР ИДЕС Назначение и архитектура

Ides — 16-байтовый идентификатор (readonly struct), разработанный в НевелГраде. Поддерживает генерацию в форматах UUID v4 и ULID, использует ГПСЧ Xoshiro256** и предоставляет строковое представление в Base32 на кириллице.

§1.Обзор Ides

Ides — это неизменяемая структура данных (readonly struct) размером 16 байт (128 бит), созданная для экосистемы НевелГрад. Она объединяет алгоритмы генерации UUID v4 и ULID, обеспечивая лексикографическую сортируемость (в режиме ULID) и строковое представление в Base32 с использованием кириллицы.

Технические характеристики

  • Размер: 16 байт (128 бит)
  • Генерация: UUID v4 (122 бита энтропии) или ULID (48 бит времени + 80 бит энтропии)
  • Кодировка: Base32 (кириллица + арабские цифры, 26 символов)
  • ГПСЧ: Xoshiro256** (период 2^256 - 1), сидирование через RandomNumberGenerator
  • Потокобезопасность: Lock-free генерация за счет [ThreadStatic] состояний
  • Интеграция: EF Core (ValueConverters), System.Text.Json, TypeConverter

§2.Структура данных

Ides представляет собой readonly struct с явным размещением полей (StructLayout.Explicit) для прямого доступа к памяти:

CSHARP
[StructLayout(LayoutKind.Explicit, Size = 16)]
public readonly partial struct Ides : IEquatable<Ides>, IComparable<Ides>
{
[FieldOffset(0)] readonly byte b0;
[FieldOffset(1)] readonly byte b1;
// ... поля b2-b14 ...
[FieldOffset(15)] readonly byte b15;
}

Форматы представления

Ides может быть представлен в нескольких форматах:

Формат Размер Пример Описание
byte[] 16 байт 0x1A 0x2B ... Бинарное представление
string 26 символов 01АБВГДЕЖЗИКЛМНПРСТУФХЦЧ Base32 (кириллица)

§3.Генерация идентификаторов

ULID режим (время + случайность)

В режиме ULID первые 6 байт содержат timestamp (миллисекунды с Unix epoch), а оставшиеся 10 байт — случайные данные. Это обеспечивает сортируемость идентификаторов по времени создания.

CSHARP
// Генерация Ides в формате ULID
Ides id = Ides.NewIdes();
// Результат: 26 символов Base32 на кириллице
Console.WriteLine(id.ToString());
// Вывод: "07ЗЖ4РСТ01АБВГДЕЖЗИКЛМНПРСТУ"
// Получение времени создания
DateTimeOffset? created = id.Time;
Console.WriteLine($"Создан: {created:yyyy-MM-dd HH:mm:ss}");
💡 ПРИМЕЧАНИЕ Сортировка и БД

Идентификаторы в режиме ULID лексикографически сортируются по времени создания. Это позволяет использовать их в качестве кластеризованных первичных ключей (Clustered Primary Keys) в реляционных базах данных, минимизируя фрагментацию индексов.

UUID v4 режим (полная случайность)

В режиме UUID v4 все 16 байт генерируются случайно, с установкой битов версии (4) и варианта (10xx). Этот формат обеспечивает 122 бита энтропии и не содержит временных меток.

CSHARP
// Генерация Ides в формате UUID v4
Ides id = Ides.NewIdesV4();
// Результат: 26 символов Base32 на кириллице
Console.WriteLine(id.ToString());
// Вывод: "АБВГДЕЖЗИКЛМНПРСТУФХЦЧ01АБВГ"
// Время создания будет случайным (не имеет смысла для UUID v4)
DateTimeOffset? time = id.Time; // Случайное значение

§4.Алфавит Base32 (кириллица)

Ides использует строгую кириллическую кодировку Base32 без латинских символов. Алфавит состоит из 32 символов: 10 арабских цифр и 22 буквы кириллицы.

Цифры (0-9)

0123456789

Буквы (А-Ч)

АБВГДЕЖЗИКЛМНПРСТУФХЦЧ
⚠ ОГРАНИЧЕНИЕ Валидация алфавита

Алфавит Base32 строго ограничен кириллицей и цифрами. Методы Parse и TryParse вернут ошибку при наличии латинских символов (A-Z, a-z) или любых других знаков, не входящих в заданный алфавит.

§5.Примеры использования

Создание идентификаторов

CSHARP
// Генерация нового ULID
Ides ulid = Ides.NewIdes();
// Генерация нового UUID v4
Ides uuid = Ides.NewIdesV4();
// Создание из байтового массива
byte[] bytes = new byte[16] { 0x01, 0x23, 0x45, /* ... */ };
Ides fromBytes = new Ides(bytes);
// Парсинг из строки
Ides parsed = Ides.Parse("07ЗЖ4РСТ01АБВГДЕЖЗИКЛМНПРСТУ");
// Безопасный парсинг
if (Ides.TryParse("07ЗЖ4РСТ01АБВГДЕЖЗИКЛМНПРСТУ", out Ides safe))
{
Console.WriteLine($"Успешно: {safe}");
}

Конвертация между форматами

CSHARP
Ides id = Ides.NewIdes();
// В строку (Base32 кириллица)
string base32 = id.ToString();
// В байтовый массив
byte[] bytes = id.ToByteArray();
// В Base64
string base64 = id.ToBase64();
// Неявное преобразование
string str = id; // Ides -> string
Ides fromStr = "07ЗЖ..."; // string -> Ides
byte[] arr = id; // Ides -> byte[]

Сравнение и сортировка

CSHARP
Ides id1 = Ides.NewIdes();
await Task.Delay(10); // Ждем 10мс
Ides id2 = Ides.NewIdes();
// Сравнение
bool equal = id1 == id2; // false
bool notEqual = id1 != id2; // true
int cmp = id1.CompareTo(id2); // -1 (id1 < id2)
// Сортировка списка (ULID сортируются по времени)
List<Ides> list = new() { id2, id1, Ides.NewIdes() };
list.Sort(); // Автоматическая сортировка
// Хеш-код для словарей
int hash = id1.GetHashCode();
Dictionary<Ides, string> dict = new();
dict[id1] = "Значение";

§6.Интеграция с Entity Framework Core

Ides интегрируется с EF Core через value converters и comparers. Поддерживаются как обычные, так и nullable типы Ides?.

Настройка модели

CSHARP
public class MyDbContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Автоматическая настройка всех свойств типа Ides
modelBuilder.UseIdes();
// Для ASP.NET Core Identity
modelBuilder.UseIdesForIdentity<ApplicationUser>();
base.OnModelCreating(modelBuilder);
}
}
public class User
{
public Ides Id { get; set; } = Ides.NewIdes();
public Ides? ParentId { get; set; }
public string Name { get; set; }
}
🔧 АВТОМАТИЗАЦИЯ Единая настройка

Метод UseIdes() автоматически настраивает все свойства типа Ides и Ides? в модели, устанавливая тип колонки binary(16) и регистрируя конвертеры.

Миграции базы данных

CSHARP
// Создание таблицы с Ides
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<byte[]>(type: "binary(16)", nullable: false),
ParentId = table.Column<byte[]>(type: "binary(16)", nullable: true),
Name = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});

§7.JSON сериализация

Ides автоматически сериализуется в JSON как строка Base32 (26 символов кириллицы). Для работы требуется регистрация конвертера.

CSHARP
// Регистрация конвертера
var options = new JsonSerializerOptions
{
Converters = { new IdesJsonConverter() }
};
// Сериализация
Ides id = Ides.NewIdes();
string json = JsonSerializer.Serialize(id, options);
// Результат: "07ЗЖ4РСТ01АБВГДЕЖЗИКЛМНПРСТУ"
// Десериализация
Ides parsed = JsonSerializer.Deserialize<Ides>(json, options);
// В составе объекта
var user = new { Id = Ides.NewIdes(), Name = "Иван" };
string userJson = JsonSerializer.Serialize(user, options);
// {"Id":"07ЗЖ4РСТ01АБВГДЕЖЗИКЛМНПРСТУ","Name":"Иван"}

§8.Полный API

Статические методы

Ides NewIdes()

Генерирует новый Ides в формате ULID (время + случайность).

Ides NewIdesV4()

Генерирует новый Ides в формате UUID v4 (полная случайность).

Ides Parse(string base32)

Парсит строку Base32 (26 символов) в Ides. Выбрасывает исключение при ошибке.

bool TryParse(string base32, out Ides result)

Безопасный парсинг строки Base32. Возвращает false при ошибке.

Ides Coalesce(Ides? value, Ides? fallback = null)

Возвращает первое непустое значение или Ides.Empty.

Свойства экземпляра

ReadOnlySpan<byte> Bytes

Представление Ides как 16 байт (только чтение).

DateTimeOffset? Time

Время создания (имеет смысл только для ULID режима).

Методы экземпляра

byte[] ToByteArray()

Возвращает копию байтового представления (16 байт).

bool TryWriteBytes(Span<byte> destination)

Записывает байты в предоставленный буфер без выделения памяти.

string ToBase64(Base64FormattingOptions options = None)

Возвращает представление в формате Base64.

bool Equals(Ides other)

Сравнивает текущий Ides с другим (оптимизировано через SIMD).

int CompareTo(Ides other)

Сравнивает два Ides для сортировки (оптимизировано через SIMD).

§9.Производительность

Замеры производительности выполнены с помощью BenchmarkDotNet (.NET 10.0) на процессоре Intel Core i7-13850HX. Сравнение проводится с системным System.Guid. Значения указаны для среднего времени выполнения (Mean) и объема выделенной памяти (Allocated).

Генерация (Generation)

Метод Ides Guid
NewIdes() / NewGuid() 24.25 ns0 B 38.63 ns0 B
NewIdesV4() (UUID v4) 5.94 ns0 B

Форматирование (Formatting)

Метод Ides Guid
ToString() 12.32 ns80 B 6.12 ns96 B
TryFormat() 7.21 ns0 B

Парсинг (Parsing)

Метод Ides Guid
Parse(string) 55.21 ns0 B 12.75 ns0 B
TryParse(Span) 48.76 ns0 B

Сравнение и сериализация

Метод Ides Guid
Equals() 0.14 ns0 B ~0.00 ns0 B
CompareTo() 0.006 ns0 B
ToByteArray() 3.27 ns40 B 3.36 ns40 B

§10.Сравнение с аналогами

Характеристика System.Guid Ides
Размер 16 байт 16 байт
Сортируемость ✅ (ULID режим)
Кириллица
UUID v4
EF Core ✅ (UseIdes)

§11.Практические примеры

Первичный ключ в базе данных

CSHARP
public class Article
{
public Ides Id { get; set; } = Ides.NewIdes();
public string Title { get; set; }
public string Content { get; set; }
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
}
// Использование в репозитории
public class ArticleRepository
{
public async Task<Article> CreateAsync(string title, string content)
{
var article = new Article
{
Id = Ides.NewIdes(), // Автоматическая генерация
Title = title,
Content = content
};
_context.Articles.Add(article);
await _context.SaveChangesAsync();
return article;
}
}

Использование в API

CSHARP
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(Ides id)
{
// Ides автоматически парсится из URL
var user = await _userService.GetByIdAsync(id);
if (user == null) return NotFound();
return Ok(new UserDto
{
Id = user.Id, // Сериализуется как Base32 кириллица
Name = user.Name
});
}
[HttpPost]
public async Task<ActionResult<UserDto>> CreateUser(CreateUserRequest request)
{
var user = new User
{
Id = Ides.NewIdes(), // Генерация на сервере
Name = request.Name,
Email = request.Email
};
await _userService.CreateAsync(user);
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}
}

§12.Часто задаваемые вопросы

❓ Почему Ides не использует стандартный Guid?

Стандартный System.Guid жестко привязан к спецификации RFC 4122/9562 и использует латинский алфавит для строкового представления. Ides реализует собственную структуру для поддержки кириллического Base32 и гибридной генерации (ULID/UUID v4) в рамках стандартов НевелГрад.

❓ Можно ли конвертировать Ides в Guid и обратно?

Прямые операторы приведения между Ides и System.Guid отсутствуют. Конвертация возможна через байтовое представление: new Guid(ides.ToByteArray()) и new Ides(guid.ToByteArray()).

❓ Какой режим выбрать: ULID или UUID v4?

Используйте ULID (NewIdes()), если требуется лексикографическая сортировка по времени создания (например, для кластеризованных индексов в БД). Используйте UUID v4 (NewIdesV4()), если требуется максимальная энтропия и отсутствие временных меток (например, для токенов, сессий или идентификаторов корреляции).

❓ Безопасен ли генератор случайных чисел?

Генератор Xoshiro256** инициализируется (сидируется) через криптографически стойкий RandomNumberGenerator. Сам по себе Xoshiro256** не является криптографически стойким (CSPRNG), поэтому для генерации секретов, токенов доступа или ключей шифрования следует использовать RandomNumberGenerator напрямую.

❓ Поддерживается ли работа в многопоточной среде?

Да. Состояние ГПСЧ Xoshiro256 хранится в [ThreadStatic] полях. Каждый поток использует собственный изолированный экземпляр генератора, что исключает конкуренцию за ресурсы (lock-free) и гарантирует потокобезопасность.

[СВЯЗЬ] ВОССТАНОВЛЕНИЕ СВЯЗИ С СЕРВЕРОМ...

[ПОВТОР] ПОВТОРНОЕ ПОДКЛЮЧЕНИЕ НЕ УДАЛОСЬ... ПОПЫТКА ЧЕРЕЗ СЕК.

[ОШИБКА] НЕ УДАЛОСЬ ВОССТАНОВИТЬ СВЯЗЬ.
ПРОВЕРЬТЕ СЕТЬ ИЛИ ПЕРЕЗАГРУЗИТЕ СТРАНИЦУ.

[ПАУЗА] СЕССИЯ ПРИОСТАНОВЛЕНА СЕРВЕРОМ.

[ОШИБКА] НЕ УДАЛОСЬ ВОЗОБНОВИТЬ СЕССИЮ.
ТРЕБУЕТСЯ ПЕРЕЗАГРУЗКА.