Skip to content

Commit c096ca3

Browse files
authored
create new practice exercise: xorcism (#994)
* add exercise metadata * start working on tests * add source stubs * test varying input types and statefulness * update io tests appropriately * ignore all but first test (to the degree practicable) * fix write link; emphasize key repetition; rewrap * add test: munge_output_has_definite_len This test is primarily a guard against students who come up with a creative solution to the generic bounds of `munge` which accepts an input whose length is not known in advance. They may be tempted to remove the `ExactSizeIterator` bound from `MungeOutput`. This test prevents them from doing so. * fix equal input bytes * capitalize all instances of XOR in readme
1 parent 4d7c84d commit c096ca3

File tree

10 files changed

+835
-0
lines changed

10 files changed

+835
-0
lines changed

config.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,18 @@
809809
"board_state"
810810
]
811811
},
812+
{
813+
"slug": "xorcism",
814+
"uuid": "0520ad82-75d9-450c-9267-d9758b3b0513",
815+
"core": false,
816+
"unlocked_by": "luhn",
817+
"difficulty": 7,
818+
"topics": [
819+
"bitwise",
820+
"generics",
821+
"lifetimes"
822+
]
823+
},
812824
{
813825
"slug": "rectangles",
814826
"uuid": "cc4ccd99-1c97-4ee7-890c-d629b4e1e46d",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The point of this exercise is for students to figure out the appropriate
2+
function signatures and generic bounds for their implementations, so we
3+
cannot provide those.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
Write a streaming adaptor which contains a reference to a key, and bitwise-XORs
2+
it with arbitrary data.
3+
4+
XOR is a fundamental binary operation: for each bit in the inputs, set the
5+
corresponding bit of the output to `1` if the input bits are different. If both
6+
inputs are `1` or both are `0`, then the corresponding output bit is `0`.
7+
8+
When XORing a document with a key, the key is repeated as many times as
9+
necessary, producing an output document equal in length to the input document.
10+
11+
XORing a document with a key has been used for cryptography as recently as the
12+
early 1900s. While this is thoroughly obsolete as a method for hiding data, it
13+
can be surprisingly useful for generating noisy random-seeming data without
14+
needing the complication of true randomness. It is still used occasionally in
15+
modern cryptography for certain ciphers: the cipher itself is just a mechanism
16+
for generating a very random, infinitely long key, which is XOR'd with the
17+
document.
18+
19+
One interesting property of XOR encryption is that it is symmetrical: XORing any
20+
number with itself produces `0`, and XORing any number with `0` returns the
21+
input number unchanged. Therefore, to decrypt a document which has been
22+
XOR-encrypted, XOR-encrypt it again using the same key.
23+
24+
## Nonallocation
25+
26+
It is not practical to write a test which ensures that your struct holds a
27+
reference to the key instead of copying it. Likewise, it is not practical to
28+
prove with a test that neither `munge` nor `munge_in_place`, nor any of their
29+
helper functions, allocate on the heap. Nevertheless, you should attempt to
30+
write your solution in this way.
31+
32+
## Implementation
33+
34+
You will need to write a `struct Xorcism` which holds a reference to a key. That
35+
struct must provide two methods: `munge_in_place` and `munge`. The former
36+
adjusts a byte buffer in-place. The latter is an iterator adaptor: it accepts an
37+
arbitrary iterator of data, and returns a new iterator of data.
38+
39+
This exercise's stub signatures are largely correct in syntax, but they do not
40+
compile: a large part of the point of this exercise is for you to get familiar
41+
with using lifetimes and generics, so you will need to fill them in on your own.
42+
Another goal of this exercise is for you to figure out an appropriate
43+
factorization which enables you to implement both of those methods with minimal
44+
duplication of effort. Don't be afraid to introduce additional helpers!
45+
46+
## Useful Traits
47+
48+
These traits will be useful:
49+
50+
- [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html)
51+
- [`Borrow`](https://doc.rust-lang.org/std/borrow/trait.Borrow.html)
52+
- [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html)
53+
- [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html)
54+
- [`Sized`](https://doc.rust-lang.org/std/marker/trait.Sized.html)
55+
56+
## Bonus Tests
57+
58+
This exercise contains bonus tests, behind the `io` feature flag. To enable
59+
them, run
60+
61+
```sh
62+
cargo test --features io
63+
```
64+
65+
For these tests, you will need to implement a method `reader` with the signature
66+
67+
```rust
68+
fn reader(self, impl Read) -> impl Read
69+
```
70+
71+
and a method `writer` with the signature
72+
73+
```rust
74+
fn writer(self, impl Write) -> impl Write
75+
```
76+
77+
These functions each convert the `Xorcism` struct into a stream adaptor in the
78+
appropriate direction. They use these traits:
79+
80+
- [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html)
81+
- [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
xorcism has tests generated by macro.
2+
This breaks the count-ignores.sh script.

exercises/xorcism/.meta/metadata.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
blurb: "Implement zero-copy streaming adaptors"
3+
source: "Peter Goodspeed-Niklaus"

exercises/xorcism/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "xorcism"
3+
version = "0.1.0"
4+
edition = "2018"
5+
6+
7+
[dependencies]
8+
9+
[features]
10+
io = []
11+
12+
[dev-dependencies]
13+
hexlit = "0.3.0"
14+
rstest = "0.6.4"
15+
rstest_reuse = "0.1.0"

exercises/xorcism/README.md

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Xorcism
2+
3+
Write a streaming adaptor which contains a reference to a key, and bitwise-XORs
4+
it with arbitrary data.
5+
6+
XOR is a fundamental binary operation: for each bit in the inputs, set the
7+
corresponding bit of the output to `1` if the input bits are different. If both
8+
inputs are `1` or both are `0`, then the corresponding output bit is `0`.
9+
10+
When XORing a document with a key, the key is repeated as many times as
11+
necessary, producing an output document equal in length to the input document.
12+
13+
XORing a document with a key has been used for cryptography as recently as the
14+
early 1900s. While this is thoroughly obsolete as a method for hiding data, it
15+
can be surprisingly useful for generating noisy random-seeming data without
16+
needing the complication of true randomness. It is still used occasionally in
17+
modern cryptography for certain ciphers: the cipher itself is just a mechanism
18+
for generating a very random, infinitely long key, which is XOR'd with the
19+
document.
20+
21+
One interesting property of XOR encryption is that it is symmetrical: XORing any
22+
number with itself produces `0`, and XORing any number with `0` returns the
23+
input number unchanged. Therefore, to decrypt a document which has been
24+
XOR-encrypted, XOR-encrypt it again using the same key.
25+
26+
## Nonallocation
27+
28+
It is not practical to write a test which ensures that your struct holds a
29+
reference to the key instead of copying it. Likewise, it is not practical to
30+
prove with a test that neither `munge` nor `munge_in_place`, nor any of their
31+
helper functions, allocate on the heap. Nevertheless, you should attempt to
32+
write your solution in this way.
33+
34+
## Implementation
35+
36+
You will need to write a `struct Xorcism` which holds a reference to a key. That
37+
struct must provide two methods: `munge_in_place` and `munge`. The former
38+
adjusts a byte buffer in-place. The latter is an iterator adaptor: it accepts an
39+
arbitrary iterator of data, and returns a new iterator of data.
40+
41+
This exercise's stub signatures are largely correct in syntax, but they do not
42+
compile: a large part of the point of this exercise is for you to get familiar
43+
with using lifetimes and generics, so you will need to fill them in on your own.
44+
Another goal of this exercise is for you to figure out an appropriate
45+
factorization which enables you to implement both of those methods with minimal
46+
duplication of effort. Don't be afraid to introduce additional helpers!
47+
48+
## Useful Traits
49+
50+
These traits will be useful:
51+
52+
- [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html)
53+
- [`Borrow`](https://doc.rust-lang.org/std/borrow/trait.Borrow.html)
54+
- [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html)
55+
- [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html)
56+
- [`Sized`](https://doc.rust-lang.org/std/marker/trait.Sized.html)
57+
58+
## Bonus Tests
59+
60+
This exercise contains bonus tests, behind the `io` feature flag. To enable
61+
them, run
62+
63+
```sh
64+
cargo test --features io
65+
```
66+
67+
For these tests, you will need to implement a method `reader` with the signature
68+
69+
```rust
70+
fn reader(self, impl Read) -> impl Read
71+
```
72+
73+
and a method `writer` with the signature
74+
75+
```rust
76+
fn writer(self, impl Write) -> impl Write
77+
```
78+
79+
These functions each convert the `Xorcism` struct into a stream adaptor in the
80+
appropriate direction. They use these traits:
81+
82+
- [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html)
83+
- [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html)
84+
85+
## Rust Installation
86+
87+
Refer to the [exercism help page][help-page] for Rust installation and learning
88+
resources.
89+
90+
## Writing the Code
91+
92+
Execute the tests with:
93+
94+
```bash
95+
$ cargo test
96+
```
97+
98+
All but the first test have been ignored. After you get the first test to
99+
pass, open the tests source file which is located in the `tests` directory
100+
and remove the `#[ignore]` flag from the next test and get the tests to pass
101+
again. Each separate test is a function with `#[test]` flag above it.
102+
Continue, until you pass every test.
103+
104+
If you wish to run all ignored tests without editing the tests source file, use:
105+
106+
```bash
107+
$ cargo test -- --ignored
108+
```
109+
110+
To run a specific test, for example `some_test`, you can use:
111+
112+
```bash
113+
$ cargo test some_test
114+
```
115+
116+
If the specific test is ignored use:
117+
118+
```bash
119+
$ cargo test some_test -- --ignored
120+
```
121+
122+
To learn more about Rust tests refer to the [online test documentation][rust-tests]
123+
124+
Make sure to read the [Modules][modules] chapter if you
125+
haven't already, it will help you with organizing your files.
126+
127+
## Further improvements
128+
129+
After you have solved the exercise, please consider using the additional utilities, described in the [installation guide](https://exercism.io/tracks/rust/installation), to further refine your final solution.
130+
131+
To format your solution, inside the solution directory use
132+
133+
```bash
134+
cargo fmt
135+
```
136+
137+
To see, if your solution contains some common ineffective use cases, inside the solution directory use
138+
139+
```bash
140+
cargo clippy --all-targets
141+
```
142+
143+
## Submitting the solution
144+
145+
Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer.
146+
147+
## Feedback, Issues, Pull Requests
148+
149+
The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help!
150+
151+
If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md).
152+
153+
[help-page]: https://exercism.io/tracks/rust/learning
154+
[modules]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html
155+
[cargo]: https://doc.rust-lang.org/book/ch14-00-more-about-cargo.html
156+
[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html
157+
158+
## Source
159+
160+
Peter Goodspeed-Niklaus
161+
162+
## Submitting Incomplete Solutions
163+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

0 commit comments

Comments
 (0)