diff --git a/README.md b/README.md index 024c56c..29b7789 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,213 @@ -### 📦 Поля структуры `Command`: +В байтах (C++ layout): -| Поле | Назначение | -| ---------------- | -------------------------------------------------- | -| `op` | Тип операции: `PUT`, `DELETE`, `FIND` | -| `key` | Хэш ключа `Hash128` | -| `value` | Буфер под значение (для PUT) | -| `value_size` | Размер значения в байтах | -| `response` | Буфер под ответ от сервера (используется при FIND) | -| `response_size` | Размер ответа в байтах | -| `response_ready` | Флаг: 0 (ожидание), 1 (ответ готов) | +| Смещение | Размер | Тип данных | Описание | +| :------- | :----- | :----------------------------------- | :-------------------------------------------------------- | +| 0 | 1 | `uint8_t` (atomic) | `ready` (0 = не готова, 1 = готова) | +| 1 | 1 | `uint8_t` | `op` — код операции (`PUT` = 0, `DELETE` = 1, `FIND` = 2) | +| 2 | 16 | `uint8_t[16]` | `key` — хэш ключа (Hash128, 128 бит) | +| 18 | 4 | `uint32_t` | `value_size` — длина данных `value` | +| 22 | 1024 | `uint8_t[1024]` | `value` — содержимое для записи (PUT) | +| 1046 | 4 | `uint32_t` | `response_size` — длина ответа в `response` | +| 1050 | 1024 | `uint8_t[1024]` | `response` — содержимое ответа после FIND | +| 2074 | 1 | `uint8_t` (atomic) | `response_ready` (0 = нет ответа, 1 = ответ готов) | +| --- | --- | Итого: **2075 байт** на одну команду | | + +> ⚠️ Реально в памяти компилятор **добавит выравнивание**.\ +> Обычно команда занимает **2080 байт** с учетом паддинга (например, до 8 байт). --- -### 🔥 Пошаговый обмен: +## 2. Структура очереди `SharedCommandQueue` -1. Клиент заполняет `Command` структуры (устанавливает поля, сбрасывает `response_ready = 0`). -2. Клиент `push_batch()` пачку команд в `SharedCommandQueue`. -3. Сервер `try_pop_batch()`, обрабатывает. -4. Сервер пишет результат обратно в поля `Command` (`response`, `response_size`, `response_ready=1`). -5. Клиент ждет готовности (`response_ready.load() == 1`) и читает результат. +| Смещение | Размер | Тип данных | Описание | +| :------- | :------- | :---------------- | :----------------------------- | +| 0 | 8 | `size_t` (atomic) | `head` (где следующий push) | +| 8 | 8 | `size_t` (atomic) | `tail` (где следующий pop) | +| 16 | 8 | `size_t` | `capacity` (количество слотов) | +| 24 | 2080 × N | `Command[N]` | Массив команд | + +> **N** = максимальное количество команд в очереди. Например: 64. + +--- + +# 📦 Процесс записи команды (PUT, FIND, DELETE) + +1. Клиент читает `head`. +2. Вычисляет `next_head = (head + 1) % capacity`. +3. Проверяет, что `next_head != tail` (иначе очередь заполнена, нужно ждать). +4. Пишет данные в слот `Command` по индексу `head`: + - ставит `ready = 0` + - заполняет `op`, `key`, `value_size`, `value` + - обнуляет `response_size`, `response_ready` +5. После полной записи устанавливает `ready = 1` (memory\_order\_release). +6. После этого обновляет `head = next_head` (memory\_order\_release). + +--- + +# 🧩 Процесс чтения команды сервером + +1. Сервер читает `tail`. +2. Если `tail == head`, значит нет команд (ждет). +3. Читает слот `Command` по индексу `tail`. +4. Проверяет `ready == 1`. +5. Выполняет команду: + - для `PUT`, `DELETE` ничего не нужно возвращать + - для `FIND` после обработки: + - заполняет `response` + - устанавливает `response_size` + - устанавливает `response_ready = 1` (memory\_order\_release) +6. После обработки увеличивает `tail = (tail + 1) % capacity` (memory\_order\_release). + +--- + +# 📑 Коды операций + +| Код | Операция | +| :-- | :------- | +| 0 | PUT | +| 1 | DELETE | +| 2 | FIND | + +--- + +# 📈 Диаграмма + +``` +Client (head) Server (tail) + ↓ ↑ +[ empty ][ empty ][ empty ][ empty ][ empty ] + ↑ write PUT -> ready=1 ↑ read -> process -> tail++ + ↑ write FIND -> ready=1 ↑ read -> process -> response_ready=1 +``` + +--- + +# 📋 Минимальная инструкция для других языков + +1. Открыть или создать POSIX shared memory (`/shm_rates1`). +2. Маппить размер (например: 24 + 2080×64 байта = \~133 120 байт). +3. Знать layout структуры `SharedCommandQueue` и `Command`. +4. Атомарно обновлять `head`/`tail` через обычные load/store (x86/arm архитектуры обеспечивают это через aligned операции на 64 бита). + +--- + +# ⚙️ Примерная формула расчета памяти: + +```text +total_size = 24 + (round_up(sizeof(Command)) × capacity) +``` + +- **24 байта** на `head` + `tail` + `capacity` +- **capacity** = число команд +- **sizeof(Command)** = около 2080 байт + +--- + +# ❗ Важно + +- `head` и `tail` — должны быть атомарными. +- Доступ к слотам только по индексам `(head % capacity)` или `(tail % capacity)`. +- Все указанные поля фиксированы и известны по смещению. +- Все строки (`value`, `response`) — это просто байтовые массивы без `\0`. + +--- + +# 📦 Памятка: Структура SharedCommandQueue в памяти + +### Общая структура: + +| Смещение (байты) | Размер | Описание | +| :--------------- | :------- | :--------------------------------- | +| 0 | 8 | `head` (size\_t, atomic) | +| 8 | 8 | `tail` (size\_t, atomic) | +| 16 | 8 | `capacity` (size\_t, НЕ atomic) | +| 24 | 2080 × N | Команды Command\[] (N штук подряд) | + +--- + +# 📦 Памятка: Структура одного `Command` + +| Смещение | Размер | Описание | +| :-------- | :--------- | :----------------------------------- | +| 0 | 1 byte | `ready` (atomic\) | +| 1 | 1 byte | `op` (uint8\_t) | +| 2 | 16 bytes | `key` (Hash128, 128 бит) | +| 18 | 4 bytes | `value_size` (uint32\_t) | +| 22 | 1024 bytes | `value` (байтовый массив данных) | +| 1046 | 4 bytes | `response_size` (uint32\_t) | +| 1050 | 1024 bytes | `response` (ответ от сервера) | +| 2074 | 1 byte | `response_ready` (atomic\) | +| 2075–2079 | 5 bytes | padding (выравнивание до 8 байт) | + +> ⚡ Суммарный размер одной команды с паддингом = **2080 байт**. + +--- + +# 📋 HEX Layout всего буфера (SharedCommandQueue + команды) + +``` +00:00 - 00:07 -> head (size_t, LE) +00:08 - 00:0F -> tail (size_t, LE) +00:10 - 00:17 -> capacity (size_t, LE) + +00:18 - 00:18+2080*N -> N команд, каждая: + 00:00 -> ready (1 byte) + 00:01 -> op (1 byte) + 00:02 - 00:11 -> key (16 bytes) + 00:12 - 00:15 -> value_size (4 bytes, LE) + 00:16 - 04:15 -> value (1024 bytes) + 04:16 - 04:19 -> response_size (4 bytes, LE) + 04:20 - 08:1F -> response (1024 bytes) + 08:20 -> response_ready (1 byte) + 08:21 - 08:27 -> padding (5 bytes) +``` + +--- + +# 🧪 Мини-псевдокод для любого языка + +```pseudo +ptr = mmap(...) + +head = read_uint64(ptr, 0) +tail = read_uint64(ptr, 8) +capacity = read_uint64(ptr, 16) + +command_base = 24 + +// Пример: прочитать первую команду (index = 0) +offset = command_base + index * 2080 + +ready = read_uint8(ptr, offset + 0) +op = read_uint8(ptr, offset + 1) +key = read_bytes(ptr, offset + 2, 16) +value_size = read_uint32(ptr, offset + 18) +value = read_bytes(ptr, offset + 22, value_size) +response_ready = read_uint8(ptr, offset + 2074) +response_size = read_uint32(ptr, offset + 1046) +response = read_bytes(ptr, offset + 1050, response_size) +``` + +--- + +# 📊 Краткая таблица памяти: + +| Поле | Тип | Офсет | Размер | +| :------- | :----------- | :---- | :----- | +| head | size\_t (8B) | 0 | 8B | +| tail | size\_t (8B) | 8 | 8B | +| capacity | size\_t (8B) | 16 | 8B | +| commands | Command\[N] | 24 | 2080×N | + +--- + +# 📢 Важные правила при использовании + +- Всегда выравнивание по 8 байтам (иначе UB на ARM/M1). +- Использовать little-endian (x86, arm64 всегда LE). +- При копировании строк следить за value\_size / response\_size. +- Проверять флаг `ready` перед чтением команды. +- После обработки команды — инкрементировать `tail` мод capacity. +- Ответ через `response` + `response_size` + `response_ready` только для `FIND`. + +--- \ No newline at end of file