diff --git a/src/main/java/algorithms/sprint5/PyramidSort.java b/src/main/java/algorithms/sprint5/PyramidSort.java index 37368de..e92fe2d 100644 --- a/src/main/java/algorithms/sprint5/PyramidSort.java +++ b/src/main/java/algorithms/sprint5/PyramidSort.java @@ -9,6 +9,8 @@ //https://contest.yandex.ru/contest/24810/run-report/160623687/ public class PyramidSort { + private static final int MAX_PARTICIPANTS = 100_000; + private static final int MAX_LOGIN_BYTES = 1_024; /* * Принцип работы алгоритма: @@ -147,11 +149,21 @@ int nextInt() throws IOException { if (c == '-') { sign = -1; c = read(); + if (c <= ' ') { + throw new NumberFormatException("Expected digit after sign"); + } } int val = 0; while (c > ' ') { - val = val * 10 + c - '0'; + if (c < '0' || c > '9') { + throw new NumberFormatException("Invalid integer input"); + } + int digit = c - '0'; + if (val > (Integer.MAX_VALUE - digit) / 10) { + throw new NumberFormatException("Integer input is too large"); + } + val = val * 10 + digit; c = read(); } return val * sign; @@ -170,6 +182,9 @@ String next() throws IOException { int n = 0; while (c > ' ') { + if (n == MAX_LOGIN_BYTES) { + throw new IOException("Login token is too long"); + } if (n == tmp.length) { byte[] next = new byte[tmp.length * 2]; System.arraycopy(tmp, 0, next, 0, tmp.length); @@ -219,6 +234,9 @@ private static void run() throws Exception { FastOut out = new FastOut(System.out); int n = in.nextInt(); + if (n < 0 || n > MAX_PARTICIPANTS) { + throw new IllegalArgumentException("Participant count is out of range"); + } Participant[] a = new Participant[n]; for (int i = 0; i < n; i++) { diff --git a/src/test/java/algorithms/sprint5/PyramidSortTest.java b/src/test/java/algorithms/sprint5/PyramidSortTest.java new file mode 100644 index 0000000..cd12398 --- /dev/null +++ b/src/test/java/algorithms/sprint5/PyramidSortTest.java @@ -0,0 +1,66 @@ +package algorithms.sprint5; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +@Tag("unit") +class PyramidSortTest { + + @Test + void fastInRejectsOverflowingInteger() { + PyramidSort.FastIn in = fastIn("2147483648 "); + + assertThrows(NumberFormatException.class, in::nextInt); + } + + @Test + void fastInRejectsOversizedLoginToken() { + PyramidSort.FastIn in = fastIn("a".repeat(1_025) + " "); + + assertThrows(java.io.IOException.class, in::next); + } + + @Test + void runRejectsNegativeParticipantCountBeforeAllocation() throws Exception { + InputStream stdin = System.in; + System.setIn(new ByteArrayInputStream("-1\n".getBytes(StandardCharsets.UTF_8))); + try { + Method run = PyramidSort.class.getDeclaredMethod("run"); + run.setAccessible(true); + + InvocationTargetException ex = assertThrows(InvocationTargetException.class, () -> run.invoke(null)); + + assertEquals(IllegalArgumentException.class, ex.getCause().getClass()); + } finally { + System.setIn(stdin); + } + } + + @Test + void solvePreservesParticipantOrdering() { + PyramidSort.Participant[] participants = new PyramidSort.Participant[] { + new PyramidSort.Participant("alla", 4, 100), + new PyramidSort.Participant("gena", 6, 1000), + new PyramidSort.Participant("timofey", 4, 80) + }; + + PyramidSort.solve(participants); + + assertEquals("gena", participants[0].login); + assertEquals("timofey", participants[1].login); + assertEquals("alla", participants[2].login); + } + + private static PyramidSort.FastIn fastIn(String input) { + return new PyramidSort.FastIn(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8))); + } +}