Xorcism

Xorcism

Difícil

Instrucciones

Escribe un adaptador de stream que contenga una referencia a una clave y que le aplique XOR bit a bit con datos arbitrarios.

XOR es una operación binaria fundamental: para cada bit de las entradas, pon el bit correspondiente de la salida a 1 si los bits de entrada son diferentes. Si ambas entradas son 1 o ambas son 0, el bit correspondiente de la salida es 0.

Cuando se aplica XOR a un documento con una clave, la clave se repite tantas veces como sea necesario, lo que produce un documento de salida de la misma longitud que el documento de entrada.

El XOR de un documento con una clave se ha usado en criptografía hasta principios del siglo XX. Aunque está completamente obsoleto como método para ocultar datos, puede resultar sorprendentemente útil para generar datos ruidosos que parecen aleatorios sin necesidad de complicarse con la aleatoriedad verdadera. Todavía se usa de vez en cuando en la criptografía moderna para ciertos cifrados: el cifrado en sí no es más que un mecanismo para generar una clave muy aleatoria e infinitamente larga, a la que se le aplica XOR con el documento.

Una propiedad interesante del cifrado XOR es que es simétrico: aplicar XOR de un número consigo mismo produce 0, y aplicar XOR de un número con 0 devuelve el número de entrada sin cambios. Por lo tanto, para descifrar un documento que se ha cifrado con XOR, vuelve a cifrarlo con XOR usando la misma clave.

Sin asignación de memoria

No es práctico escribir un test que garantice que tu struct contiene una referencia a la clave en lugar de copiarla. Asimismo, tampoco es práctico demostrar con un test que ni munge ni munge_in_place, ni ninguna de sus funciones auxiliares, reservan memoria en el heap. No obstante, deberías intentar escribir tu solución de esta manera.

Implementación

Tendrás que escribir una struct Xorcism que contenga una referencia a una clave. Esa struct debe proporcionar dos métodos: munge_in_place y munge. El primero ajusta un búfer de bytes in situ. El segundo es un adaptador de iterador: acepta un iterador de datos arbitrario y devuelve un iterador de datos nuevo.

Las firmas del esqueleto de este ejercicio son en gran medida correctas sintácticamente, pero no compilan: gran parte del objetivo de este ejercicio es que te familiarices con el uso de lifetimes y genéricos, así que tendrás que completarlas por tu cuenta. Otro objetivo de este ejercicio es que descubras una factorización adecuada que te permita implementar ambos métodos con una duplicación de esfuerzo mínima. ¡No tengas miedo de añadir funciones auxiliares adicionales!

Traits útiles

Estos traits te resultarán útiles:

Tests adicionales

Este ejercicio contiene tests adicionales, tras la feature flag io. Para activarlos, ejecuta

cargo test --features io

Para estos tests, tendrás que implementar un método reader con la firma

fn reader(self, impl Read) -> impl Read

y un método writer con la firma

fn writer(self, impl Write) -> impl Write

Estas funciones convierten la struct Xorcism en un adaptador de stream en la dirección adecuada. Usan estos traits:

Tiempo de vida del valor devuelto de munge

Debido al uso de la característica impl Trait, la gestión de los tiempos de vida puede resultar un poco complicada al implementar el método munge. Puede que te resulte más fácil escribir una struct propia con una implementación de Iterator y devolver ese tipo concreto, al menos para empezar. En última instancia, es buena idea intentar implementar la solución usando directamente los combinadores de Iterator.


Fuente

Peter Goodspeed-Niklaus
Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Rust Exercism

¿Listo para empezar Xorcism?

Regístrate en Exercism para aprender y dominar Rust con 99 ejercicios y mentoría humana real, todo gratis.