Skip to content

Commit aa4f7ce

Browse files
committed
Harden rail fence cipher decoding
1 parent 53b1b1f commit aa4f7ce

2 files changed

Lines changed: 16 additions & 34 deletions

File tree

src/main/java/kyu3/RailFenceCipher.java

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package kyu3;
22

33

4-
import java.util.LinkedList;
54
import java.util.Map;
65
import java.util.TreeMap;
76

@@ -42,41 +41,28 @@ static String decode(String s, int n) {
4241
// First computes the exact size of each rail, splits ciphertext into
4342
// contiguous rail segments, then reconstructs plaintext by replaying the
4443
// same rail traversal cycle.
44+
if (s.isEmpty() || n >= s.length()) {
45+
return s;
46+
}
47+
4548
int[] size = new int[n];
4649
Counter counter = new Counter(n - 1);
4750
for (int i = 0; i < s.length(); i++) {
4851
int tik = counter.tik();
4952
size[tik]++;
5053
}
51-
Map<Integer, StringBuilder> map = new TreeMap<>();
52-
StringBuilder result = new StringBuilder();
54+
int[] position = new int[n];
55+
int start = 0;
5356
for (int i = 0; i < n; i++) {
54-
StringBuilder stringBuilder = new StringBuilder();
55-
if (i == 0) {
56-
stringBuilder.append(s, 0, size[0]);
57-
} else {
58-
int start = sumArr(size, i);
59-
int end = start + size[i];
60-
stringBuilder.append(s, start, end);
61-
}
62-
map.put(i, stringBuilder);
63-
}
64-
65-
Map<Integer, LinkedList<String>> map2 = new TreeMap<>();
66-
67-
for (Map.Entry<Integer, StringBuilder> e : map.entrySet()
68-
) {
69-
LinkedList<String> strings = new LinkedList<>();
70-
for (char c : e.getValue().toString().toCharArray()) {
71-
strings.add(String.valueOf(c));
72-
}
73-
map2.put(e.getKey(), strings);
57+
position[i] = start;
58+
start += size[i];
7459
}
7560

61+
StringBuilder result = new StringBuilder(s.length());
7662
Counter counterRes = new Counter(n - 1);
7763
for (int i = 0; i < s.length(); i++) {
7864
int tik = counterRes.tik();
79-
result.append(map2.get(tik).poll());
65+
result.append(s.charAt(position[tik]++));
8066
}
8167

8268
return result.toString();
@@ -88,16 +74,6 @@ private static void validateRailCount(int railCount) {
8874
}
8975
}
9076

91-
private static int sumArr(int[] arr, int i) {
92-
// Sum widths of all previous rails to determine the start offset
93-
// of the current rail segment.
94-
int result = 0;
95-
for (int j = 0; j < i; j++) {
96-
result += arr[j];
97-
}
98-
return result;
99-
}
100-
10177
/**
10278
* Create two functions to encode and then decode a string using the Rail Fence Cipher. This cipher is used to encode
10379
* a string by placing each character successively in a diagonal along a set of "rails". First start off moving diagonally

src/test/java/kyu3/RailFenceCipherTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ void shouldEncodeAndDecodeKnownExample() {
2121
assertEquals("WEAREDISCOVEREDFLEEATONCE", RailFenceCipher.decode(encoded, 3));
2222
}
2323

24+
@Test
25+
void shouldDecodeWithoutAllocatingForUnusedRails() {
26+
assertEquals("", RailFenceCipher.decode("", Integer.MAX_VALUE));
27+
assertEquals("abc", RailFenceCipher.decode("abc", Integer.MAX_VALUE));
28+
}
29+
2430
@ParameterizedTest
2531
@MethodSource("roundTripCases")
2632
void shouldRoundTripPlainTextForDifferentRails(String text, int rails) {

0 commit comments

Comments
 (0)