Напишіть потоковий адаптер, який містить посилання на ключ і порозрядно виконує над ним XOR із довільними даними.
XOR - фундаментальна бінарна операція: для кожного біта вхідних даних встановлюємо відповідний біт вихідних даних у 1, якщо вхідні біти різняться. Якщо обидва вхідні біти дорівнюють 1 або обидва дорівнюють 0, то відповідний вихідний біт - 0.
Коли ми виконуємо XOR документа з ключем, ключ повторюється стільки разів, скільки потрібно, і утворюється вихідний документ, довжина якого дорівнює довжині вхідного.
Шифрування документа XOR-ом із ключем використовували в криптографії навіть на початку 1900-х років. Хоча як метод приховування даних XOR цілком застарів, він може стати напрочуд корисним для генерування шумних даних, які здаються випадковими, без потреби ускладнювати все справжньою випадковістю. Його досі подеколи використовують у сучасній криптографії для деяких шифрів: сам шифр - це лише механізм для генерування дуже випадкового, нескінченно довгого ключа, який потім застосовують до документа через XOR.
Одна цікава властивість XOR-шифрування - його симетричність: XOR будь-якого числа із самим собою дає 0, а XOR будь-якого числа з 0 повертає вхідне число без змін. Тож, щоб розшифрувати документ, зашифрований XOR-ом, потрібно знову зашифрувати його XOR-ом із тим самим ключем.
Непрактично писати тест, який перевіряє, що наша структура містить посилання на ключ, а не копіює його. Так само непрактично доводити тестом, що ні munge, ні munge_in_place, ні будь-яка з їхніх допоміжних функцій не виділяють памʼять у купі. І все ж спробуйте написати своє рішення саме так.
Потрібно написати struct Xorcism, що містить посилання на ключ. Ця структура повинна надавати два методи: munge_in_place і munge. Перший змінює буфер байтів на місці. Другий - це адаптер ітератора: він приймає довільний ітератор даних і повертає новий ітератор даних.
Сигнатури заглушок у цій вправі здебільшого синтаксично правильні, але вони не компілюються: значною мірою сенс цієї вправи в тому, щоб ми навчилися користуватися часами життя та узагальненнями, тож заповнити їх доведеться самостійно. Ще одна мета цієї вправи - знайти доречне розбиття на частини, яке дасть змогу реалізувати обидва ці методи з мінімальним дублюванням роботи. Не бійтеся додавати допоміжні функції!
Ось трейти, які стануть у пригоді:
Ця вправа містить бонусні тести за прапорцем можливості io. Щоб увімкнути їх, виконайте
cargo test --features io
Для цих тестів потрібно реалізувати метод reader із такою сигнатурою
fn reader(self, impl Read) -> impl Read
і метод writer із такою сигнатурою
fn writer(self, impl Write) -> impl Write
Кожна з цих функцій перетворює структуру Xorcism на потоковий адаптер у відповідному напрямку. Вони використовують такі трейти:
munge
Через використання можливості impl Trait керування часом життя може бути трохи складним, коли ми реалізуємо метод munge. Може виявитися легше написати власний struct із реалізацією Iterator і повернути цей конкретний тип, принаймні для початку. Зрештою, варто спробувати реалізувати рішення безпосередньо за допомогою комбінаторів Iterator.