Scrivi un adattatore di stream che contiene un riferimento a una chiave e che applica a essa un XOR bit a bit con dati arbitrari.
L'XOR è un'operazione binaria fondamentale: per ogni bit degli input, imposta il bit corrispondente dell'output a 1 se i bit di input sono diversi. Se entrambi gli input sono 1 o entrambi sono 0, il bit di output corrispondente è 0.
Quando si applica l'XOR tra un documento e una chiave, la chiave viene ripetuta tutte le volte necessarie, producendo un documento di output lungo quanto quello di input.
L'XOR di un documento con una chiave è stato usato per la crittografia fino ai primi del Novecento. Sebbene sia del tutto obsoleto come metodo per nascondere dati, può essere sorprendentemente utile per generare dati rumorosi che sembrano casuali senza la complicazione della vera casualità. È ancora usato occasionalmente nella crittografia moderna per certi cifrari: il cifrario stesso è solo un meccanismo per generare una chiave infinita e molto casuale, che viene combinata con il documento tramite XOR.
Una proprietà interessante della cifratura XOR è che è simmetrica: l'XOR di un numero con se stesso produce 0, e l'XOR di un numero con 0 restituisce il numero di input invariato. Quindi, per decifrare un documento cifrato con XOR, basta applicargli di nuovo la cifratura XOR usando la stessa chiave.
Non è pratico scrivere un test che verifichi che la struct contenga un riferimento alla chiave invece di copiarla. Allo stesso modo, non è pratico dimostrare con un test che né munge né munge_in_place, né le loro funzioni ausiliarie, allocano sull'heap. Tuttavia, dovresti provare a scrivere la soluzione in questo modo.
Dovrai scrivere una struct Xorcism che contenga un riferimento a una chiave. Questa struct deve fornire due metodi: munge_in_place e munge. Il primo modifica un buffer di byte sul posto. Il secondo è un adattatore di iteratori: accetta un iteratore di dati arbitrario e restituisce un nuovo iteratore di dati.
Le firme degli stub di questo esercizio sono in gran parte corrette nella sintassi, ma non compilano: buona parte dello scopo di questo esercizio è acquisire familiarità con l'uso dei lifetime e dei generici, quindi dovrai completarle da solo. Un altro obiettivo è capire una fattorizzazione appropriata che ti permetta di implementare entrambi i metodi con un minimo di duplicazione del lavoro. Non aver paura di introdurre funzioni ausiliarie aggiuntive!
Questi trait saranno utili:
Questo esercizio contiene dei test bonus, dietro il feature flag io. Per abilitarli, esegui
cargo test --features io
Per questi test, dovrai implementare un metodo reader con la firma
fn reader(self, impl Read) -> impl Read
e un metodo writer con la firma
fn writer(self, impl Write) -> impl Write
Ognuna di queste funzioni converte la struct Xorcism in un adattatore di stream nella direzione appropriata. Usano questi trait:
munge
A causa dell'uso della funzionalità impl Trait, la gestione della durata può
essere un po' complicata quando si implementa il metodo munge. Potresti
trovare più semplice scrivere una struct propria con un'implementazione di
Iterator e restituire quel tipo concreto, almeno per iniziare. Alla fine, è
una buona idea provare a implementare la soluzione usando direttamente i
combinatori di Iterator.
Iscriviti a Exercism per imparare e padroneggiare Rust con 99 esercizi e il mentoring di persone reali, tutto gratis.