Γράψε έναν προσαρμογέα ροής που περιέχει μια αναφορά σε ένα κλειδί και κάνει bitwise XOR με αυτό πάνω σε αυθαίρετα δεδομένα.
Το XOR είναι μια θεμελιώδης δυαδική πράξη: για κάθε bit στις εισόδους, θέτει το αντίστοιχο bit της εξόδου σε 1 αν τα bit εισόδου είναι διαφορετικά. Αν και οι δύο είσοδοι είναι 1 ή και οι δύο είναι 0, τότε το αντίστοιχο bit της εξόδου είναι 0.
Όταν κάνεις XOR σε ένα έγγραφο με ένα κλειδί, το κλειδί επαναλαμβάνεται όσες φορές χρειάζεται, παράγοντας ένα έγγραφο εξόδου ίσου μήκους με το έγγραφο εισόδου.
Το XOR ενός εγγράφου με ένα κλειδί έχει χρησιμοποιηθεί για κρυπτογραφία ακόμη και μέχρι τις αρχές του 1900. Αν και αυτό είναι εντελώς ξεπερασμένο ως μέθοδος απόκρυψης δεδομένων, μπορεί να φανεί εντυπωσιακά χρήσιμο για την παραγωγή θορυβωδών δεδομένων που μοιάζουν τυχαία, χωρίς να χρειάζεται η πολυπλοκότητα της αληθινής τυχαιότητας. Χρησιμοποιείται ακόμη περιστασιακά στη σύγχρονη κρυπτογραφία για ορισμένους κρυπταλγόριθμους: ο ίδιος ο κρυπταλγόριθμος είναι απλώς ένας μηχανισμός για την παραγωγή ενός πολύ τυχαίου, άπειρου μήκους κλειδιού, το οποίο υφίσταται XOR με το έγγραφο.
Μια ενδιαφέρουσα ιδιότητα της κρυπτογράφησης με XOR είναι ότι είναι συμμετρική: το XOR οποιουδήποτε αριθμού με τον εαυτό του παράγει 0, και το XOR οποιουδήποτε αριθμού με το 0 επιστρέφει τον αριθμό εισόδου αμετάβλητο. Επομένως, για να αποκρυπτογραφήσεις ένα έγγραφο που έχει κρυπτογραφηθεί με XOR, αρκεί να το κρυπτογραφήσεις ξανά με XOR χρησιμοποιώντας το ίδιο κλειδί.
Δεν είναι πρακτικό να γράψεις ένα test που να εξασφαλίζει ότι το struct σου κρατά μια αναφορά στο κλειδί αντί να το αντιγράφει. Παρομοίως, δεν είναι πρακτικό να αποδείξεις με ένα test ότι ούτε η munge ούτε η munge_in_place, ούτε καμία από τις βοηθητικές συναρτήσεις τους, δεσμεύουν μνήμη στον heap. Παρ' όλα αυτά, θα πρέπει να επιχειρήσεις να γράψεις τη λύση σου με αυτόν τον τρόπο.
Θα χρειαστεί να γράψεις ένα struct Xorcism που κρατά μια αναφορά σε ένα κλειδί. Αυτό το struct πρέπει να παρέχει δύο μεθόδους: τη munge_in_place και τη munge. Η πρώτη τροποποιεί επί τόπου ένα buffer από bytes. Η δεύτερη είναι ένας προσαρμογέας iterator: δέχεται έναν αυθαίρετο iterator δεδομένων και επιστρέφει έναν νέο iterator δεδομένων.
Οι υπογραφές του stub αυτής της άσκησης είναι ως επί το πλείστον σωστές συντακτικά, αλλά δε μεταγλωττίζονται: μεγάλο μέρος του νοήματος αυτής της άσκησης είναι να εξοικειωθείς με τη χρήση lifetimes και generics, οπότε θα χρειαστεί να τις συμπληρώσεις εσύ. Ένας ακόμη στόχος αυτής της άσκησης είναι να βρεις μια κατάλληλη παραγοντοποίηση που σου επιτρέπει να υλοποιήσεις και τις δύο αυτές μεθόδους με ελάχιστη επανάληψη προσπάθειας. Μη διστάσεις να προσθέσεις επιπλέον βοηθητικές συναρτήσεις!
Αυτά τα traits θα σου φανούν χρήσιμα:
Αυτή η άσκηση περιέχει bonus tests, πίσω από το feature flag io. Για να τα ενεργοποιήσεις, τρέξε
cargo test --features io
Για αυτά τα tests, θα χρειαστεί να υλοποιήσεις μια μέθοδο reader με την υπογραφή
fn reader(self, impl Read) -> impl Read
και μια μέθοδο writer με την υπογραφή
fn writer(self, impl Write) -> impl Write
Αυτές οι συναρτήσεις μετατρέπουν καθεμία το struct Xorcism σε προσαρμογέα ροής προς την κατάλληλη κατεύθυνση. Χρησιμοποιούν αυτά τα traits:
munge
Λόγω της χρήσης της δυνατότητας impl Trait, η διαχείριση της διάρκειας ζωής μπορεί να
είναι λίγο περίπλοκη όταν υλοποιείς τη μέθοδο munge. Μπορεί να σου φανεί πιο εύκολο να
γράψεις τη δική σου struct με μια υλοποίηση του Iterator και να επιστρέψεις αυτόν τον
συγκεκριμένο τύπο, τουλάχιστον για να ξεκινήσεις. Στο τέλος πάντων, είναι καλή ιδέα να
δοκιμάσεις να υλοποιήσεις τη λύση χρησιμοποιώντας απευθείας συνδυαστές του Iterator.
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Rust με 99 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.