Skip to content

A lightweight library for ultra-compact storage and fast processing of identifiers (IDs) in binary files.

License

Notifications You must be signed in to change notification settings

QiXi/binary-ids

Repository files navigation

Binary-IDs Library 🚀

Легковесная библиотека на 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

// Потоковая обработка всех 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 КБ буфера).

About

A lightweight library for ultra-compact storage and fast processing of identifiers (IDs) in binary files.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages