Легковесная библиотека на Kotlin для сверхкомпактного хранения и быстрой обработки идентификаторов (ID) в бинарных файлах.
Это идеальное решение для систем, где нужно хранить статусы миллионов объектов (например, «прочитано», «активен», «синхронизировано»), затрачивая всего 1 бит на запись.
Используйте Binary-IDs, если вам нужно хранить бинарные статусы (да/нет) для огромного количества элементов, и вы хотите, чтобы это работало быстрее, чем любая база данных, занимая при этом меньше места, чем обычный текстовый файл.
Если вам нужно хранить 10 000 000 статусов "да/нет", эта библиотека упакует их в 1.2 МБ, тогда как SQL-база потратит 400 МБ на ту же информацию.
- Плотность данных: Экстремально низкая потребность в памяти (1 бит на элемент). 1.000.000 ID занимают всего ~125 КБ на диске.
- Производительность: Атомарные операции (проверка/установка/снятие бита) — выполняются за O(1)
- Безопасность: Использует современное NIO API, обеспечивая корректное закрытие ресурсов и атомарную запись байтов.
- No Dependencies: Чистый Kotlin, никаких сторонних библиотек.
- Линейный рост: Файл всегда занимает место, пропорциональное максимальному сохраненному ID.
- Транзакционность: Каждая операция атомарна на уровне байта, но библиотека не поддерживает многошаговые транзакции (Rollback).
- IO Overhead: Каждая одиночная операция открывает и закрывает дескриптор файла.
- Формат данных: Битовая карта хранит только состояние «да/нет». Для любых дополнительных сведений нужна отдельная структура данных.
Каждый бит в файле соответствует одному ID:
0— ID свободен (не используется)1— ID занят (используется)
Позиция бита вычисляется как:
- байт:
ID / 8; - бит внутри байта:
ID % 8.
- Поиск первого свободного ID (findFirstZeroId()).
- Подсчёт занятых ID (readCount()).
- Проверка занятости конкретного ID (contains(id)).
- Установка/снятие флага занятости (update(id, state)).
- Итерация по всем занятым ID (readIds(action)).
Класс BitIds предназначен для задач, где нужно быстро проверять статус объекта (например: «загружено ли видео?», «отправлено ли уведомление пользователю?», «свободен ли индекс в БД?»).
Достаточно передать путь к файлу java.nio.file.Path. Файл создается автоматически при первой попытке записи.
val bitIds = BitIds(Path.of("storage/users.bits"))Вы можете включать или выключать биты (ID). Метод update устанавливает бит в 1 (true) или 0 (false). Возвращает true, если файл был изменен.
bitIds.update(id = 42, state = true) // Установить ID 42
bitIds.update(id = 42, state = false) // Сбросить ID 42Мгновенная проверка бита по смещению без считывания всего файла.
if (bitIds.contains(105)) {
println("ID 105 существует")
}Мгновенная проверка бита по смещению без считывания всего файла.
val nextAvailable = bitIds.findFirstZeroId()
bitIds.update(nextAvailable, true) // Резервируем// Посчитать количество установленных ID
val activeCount = bitIds.readCount()// Потоковая обработка всех ID
bitIds.readIds { id ->
println("Обработка существующего ID: $id")
} // Инициализация менеджеров ID для разных типов сущностей
val userIds = BitIds(storagePath("users.ids"))
val movieIds = BitIds(storagePath("movies.ids"))
// Регистрация ID (либо использование существующих ID)
val aliceId = userIds.getId() // Alice получает ID 0
val movieAvatarId = movieIds.getId() // Аватар получает ID 0
val movieMatrixId = movieIds.getId() // Матрица получает ID 1
// Создание файла для хранения связей (избранное Alice)
val aliceFavorites = BitIds(storagePath("user_${aliceId}_favorites.ids"))
// Добавление связей (установка битов в файле избранного)
aliceFavorites.update(movieAvatarId, true)
aliceFavorites.update(movieMatrixId, true)
println("Проверка наличия фильма 'Аватар' в избранном у Alice:")
println("Содержит ID $movieAvatarId? ${aliceFavorites.contains(movieAvatarId)}") // true
print("Все избранные фильмы Alice (ID): ")
// Чтение всех установленных ID
aliceFavorites.readIds { id -> print("$id ") } // Выведет 0 1
// Демонстрация другого сценария: хранение списка друзей
val bobId = userIds.getId() // Bob получает ID 1
val aliceFriends = BitIds(storagePath("user_${aliceId}_friends.ids"))
aliceFriends.update(bobId, true) // Alice добавляет Bob'а в друзья
println("У Alice в друзьях Bob (ID $bobId)? ${aliceFriends.contains(bobId)}") // true- Формат файла: Чистый бинарный массив (Bitmap). Отсутствие заголовков (headers) обеспечивает 100% полезную нагрузку.
- Маппинг данных: 1 бит = 1 ID. Индекс бита в файле соответствует значению ID.
- Порядок бит: Порядок Big-Endian (маска 0x80 соответствует младшему ID в байте).
- Буферизация: По умолчанию используется буфер 16 КБ для массовых операций.
- Версия Kotlin: Совместимо с Kotlin 1.9+ и современными JVM (21+).
- Лимиты: Максимальный поддерживаемый ID ограничен значением
Int.MAX_VALUE, что соответствует размеру файла в 256 МБ. - Аппаратное ускорение:
- Метод
readCountиспользует 64-битные регистры и инструкцию процессораPOPCNTдля мгновенного подсчета бит. - Поиск свободных ID (
findFirstZeroId) оптимизирован через интринсики поиска ведущих нулей.
- Метод
- Zero-Copy архитектура: Использование
FileChannelиDirectByteBufferминимизирует нагрузку на GC и исключает лишнее копирование данных в памяти. - Потоковая обработка: Метод
readIdsпозволяет обрабатывать миллиарды ID через callback-лямбду, потребляя фиксированный объем памяти (16 КБ буфера).