123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- /* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ==================================================================== */
-
- package org.apache.poi.util;
-
- import static org.junit.jupiter.api.Assertions.assertArrayEquals;
- import static org.junit.jupiter.api.Assertions.assertEquals;
- import static org.junit.jupiter.api.Assertions.assertFalse;
- import static org.junit.jupiter.api.Assertions.assertNotNull;
- import static org.junit.jupiter.api.Assertions.assertThrows;
- import static org.junit.jupiter.api.Assertions.assertTrue;
-
- import java.io.ByteArrayInputStream;
- import java.io.EOFException;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.PushbackInputStream;
- import java.nio.ByteBuffer;
- import java.nio.channels.ReadableByteChannel;
- import java.nio.charset.StandardCharsets;
- import java.util.Random;
-
- import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
- import org.apache.poi.EmptyFileException;
- import org.junit.jupiter.api.AfterAll;
- import org.junit.jupiter.api.BeforeAll;
- import org.junit.jupiter.api.Test;
- import org.junit.jupiter.api.parallel.Isolated;
-
- @Isolated // this test changes global static BYTE_ARRAY_MAX_OVERRIDE
- final class TestIOUtils {
- private static File TMP;
- private static final long LENGTH = 300 + new Random().nextInt(9000);
-
- @BeforeAll
- public static void setUp() throws IOException {
- TMP = File.createTempFile("poi-ioutils-", "");
- try (OutputStream os = new FileOutputStream(TMP)) {
- for (int i = 0; i < LENGTH; i++) {
- os.write(0x01);
- }
- }
- }
-
- @AfterAll
- public static void tearDown() {
- if (TMP != null) {
- assertTrue(TMP.delete());
- }
- }
-
- private static InputStream data123() {
- return new ByteArrayInputStream(new byte[]{1,2,3});
- }
-
- @Test
- void testPeekFirst8Bytes() throws Exception {
- assertArrayEquals("01234567".getBytes(StandardCharsets.UTF_8),
- IOUtils.peekFirst8Bytes(new ByteArrayInputStream("0123456789".getBytes(StandardCharsets.UTF_8))));
- }
-
- @Test
- void testPeekFirst8BytesWithPushbackInputStream() throws Exception {
- assertArrayEquals("01234567".getBytes(StandardCharsets.UTF_8),
- IOUtils.peekFirst8Bytes(new PushbackInputStream(new ByteArrayInputStream("0123456789".getBytes(StandardCharsets.UTF_8)), 8)));
- }
-
- @Test
- void testPeekFirst8BytesTooLessAvailable() throws Exception {
- assertArrayEquals(new byte[] { 1, 2, 3, 0, 0, 0, 0, 0}, IOUtils.peekFirst8Bytes(data123()));
- }
-
- @Test
- void testPeekFirst8BytesEmpty() {
- assertThrows(EmptyFileException.class, () ->
- IOUtils.peekFirst8Bytes(new ByteArrayInputStream(new byte[0])));
- }
-
- @Test
- void testToByteArray() throws Exception {
- assertArrayEquals(new byte[] { 1, 2, 3}, IOUtils.toByteArray(data123()));
- }
-
- @Test
- void testToByteArrayToSmall() {
- assertThrows(IOException.class, () -> IOUtils.toByteArray(data123(), 10));
- }
-
- @Test
- void testToByteArrayMaxLengthToSmall() {
- assertThrows(IOException.class, () -> IOUtils.toByteArray(data123(), 10, 10));
- }
-
- @Test
- void testToByteArrayNegativeLength() {
- assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(data123(), -1));
- }
-
- @Test
- void testToByteArrayNegativeMaxLength() {
- assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(data123(), 10, -1));
- }
-
- @Test
- void testToByteArrayByteBuffer() {
- assertArrayEquals(new byte[] { 1, 2, 3},
- IOUtils.toByteArray(ByteBuffer.wrap(new byte[]{1, 2, 3}), 10));
- }
-
- @Test
- void testToByteArrayByteBufferNonArray() {
- ByteBuffer buffer = ByteBuffer.allocate(3);
- buffer.put(new byte[] { 1, 2, 3});
- buffer.position(0);
- assertFalse(buffer.asReadOnlyBuffer().hasArray());
- assertEquals(3, buffer.asReadOnlyBuffer().remaining());
-
- assertArrayEquals(new byte[] { 1, 2, 3},
- IOUtils.toByteArray(buffer.asReadOnlyBuffer(), 3));
- }
-
- @Test
- void testToByteArrayByteBufferToSmall() {
- assertArrayEquals(new byte[] { 1, 2, 3, 4, 5, 6, 7},
- IOUtils.toByteArray(ByteBuffer.wrap(new byte[]{1, 2, 3, 4, 5, 6, 7}), 3));
- }
-
- @Test
- void testSkipFully() throws IOException {
- try (InputStream is = new FileInputStream(TMP)) {
- long skipped = IOUtils.skipFully(is, 20000L);
- assertEquals(LENGTH, skipped);
- }
- }
-
- @Test
- void testSkipFullyGtIntMax() throws IOException {
- try (InputStream is = new FileInputStream(TMP)) {
- long skipped = IOUtils.skipFully(is, Integer.MAX_VALUE + 20000L);
- assertEquals(LENGTH, skipped);
- }
- }
-
- @Test
- void testSkipFullyByteArray() throws IOException {
- UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
- try (InputStream is = new FileInputStream(TMP)) {
- assertEquals(LENGTH, IOUtils.copy(is, bos));
- long skipped = IOUtils.skipFully(bos.toInputStream(), 20000L);
- assertEquals(LENGTH, skipped);
- }
- }
-
- @Test
- void testSkipFullyByteArrayGtIntMax() throws IOException {
- UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
- try (InputStream is = new FileInputStream(TMP)) {
- assertEquals(LENGTH, IOUtils.copy(is, bos));
- long skipped = IOUtils.skipFully(bos.toInputStream(), Integer.MAX_VALUE + 20000L);
- assertEquals(LENGTH, skipped);
- }
- }
-
- @Test
- void testCopyToFile() throws IOException {
- File dest = File.createTempFile("poi-ioutils-", "");
- try {
- try (InputStream is = new FileInputStream(TMP)) {
- assertEquals(LENGTH, IOUtils.copy(is, dest));
- }
-
- try (FileInputStream strOrig = new FileInputStream(TMP);
- FileInputStream strDest = new FileInputStream(dest)) {
- byte[] bytesOrig = new byte[(int)LENGTH];
- byte[] bytesDest = new byte[(int)LENGTH];
- IOUtils.readFully(strOrig, bytesOrig);
- IOUtils.readFully(strDest, bytesDest);
- assertArrayEquals(bytesOrig, bytesDest);
- }
- } finally {
- assertTrue(dest.delete());
- }
- }
-
- @Test
- void testCopyToInvalidFile() throws IOException {
- try (InputStream is = new FileInputStream(TMP)) {
- assertThrows(RuntimeException.class,
- () -> {
- // try with two different paths so we fail on both Unix and Windows
- IOUtils.copy(is, new File("/notexisting/directory/structure"));
- IOUtils.copy(is, new File("c:\\note&/()\"§=§%&!§$81§0_:;,.-'#*+~`?ß´ß0´ß9243xisting\\directory\\structure"));
- });
- }
- }
-
- @Test
- void testSkipFullyBug61294() throws IOException {
- long skipped = IOUtils.skipFully(new ByteArrayInputStream(new byte[0]), 1);
- assertEquals(-1L, skipped);
- }
-
- @Test
- void testZeroByte() throws IOException {
- long skipped = IOUtils.skipFully((new ByteArrayInputStream(new byte[0])), 100);
- assertEquals(-1L, skipped);
- }
-
- @Test
- void testSkipZero() throws IOException {
- try (InputStream is = new FileInputStream(TMP)) {
- long skipped = IOUtils.skipFully(is, 0);
- assertEquals(0, skipped);
- }
- }
-
- @Test
- void testSkipNegative() throws IOException {
- try (InputStream is = new FileInputStream(TMP)) {
- assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(is, -1));
- }
- }
-
- @Test
- void testMaxLengthTooLong() throws IOException {
- try (InputStream is = new FileInputStream(TMP)) {
- assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(is, Integer.MAX_VALUE, 100));
- }
- }
-
- @Test
- void testMaxLengthIgnored() throws IOException {
- try (InputStream is = new FileInputStream(TMP)) {
- int len = IOUtils.toByteArray(is, 90, Integer.MAX_VALUE).length;
- assertEquals(90, len);
- len = IOUtils.toByteArray(is, 90, 100).length;
- assertEquals(90, len);
- len = IOUtils.toByteArray(is, Integer.MAX_VALUE, Integer.MAX_VALUE).length;
- assertTrue(len > 300-2*90);
- }
- }
-
- @Test
- void testMaxLengthInvalid() throws IOException {
- try (InputStream is = new FileInputStream(TMP)) {
- assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(is, 90, 80));
- }
- }
-
- @Test
- void testWonkyInputStream() throws IOException {
- long skipped = IOUtils.skipFully(new WonkyInputStream(), 10000);
- assertEquals(10000, skipped);
- }
-
- @Test
- void testSetMaxOverride() throws IOException {
- ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
- byte[] bytes = IOUtils.toByteArray(stream);
- assertNotNull(bytes);
- assertEquals("abc", new String(bytes, StandardCharsets.UTF_8));
- }
-
- @Test
- void testSetMaxOverrideLimit() throws IOException {
- IOUtils.setByteArrayMaxOverride(30 * 1024 * 1024);
- try {
- ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
- byte[] bytes = IOUtils.toByteArray(stream);
- assertNotNull(bytes);
- assertEquals("abc", new String(bytes, StandardCharsets.UTF_8));
- } finally {
- IOUtils.setByteArrayMaxOverride(-1);
- }
- }
-
- @Test
- void testSetMaxOverrideOverLimit() {
- IOUtils.setByteArrayMaxOverride(2);
- try {
- ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
- assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(stream));
- } finally {
- IOUtils.setByteArrayMaxOverride(-1);
- }
- }
-
- @Test
- void testSetMaxOverrideWithLength() throws IOException {
- ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
- byte[] bytes = IOUtils.toByteArray(stream, 3, 100);
- assertNotNull(bytes);
- assertEquals("abc", new String(bytes, StandardCharsets.UTF_8));
- }
-
- @Test
- void testSetMaxOverrideLimitWithLength() throws IOException {
- IOUtils.setByteArrayMaxOverride(30 * 1024 * 1024);
- try {
- ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
- byte[] bytes = IOUtils.toByteArray(stream, 3, 100);
- assertNotNull(bytes);
- assertEquals("abc", new String(bytes, StandardCharsets.UTF_8));
- } finally {
- IOUtils.setByteArrayMaxOverride(-1);
- }
- }
-
- @Test
- void testSetMaxOverrideOverLimitWithLength() {
- IOUtils.setByteArrayMaxOverride(2);
- try {
- ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
- assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(stream, 3, 100));
- } finally {
- IOUtils.setByteArrayMaxOverride(-1);
- }
- }
-
- @Test
- void testSafelyAllocate() {
- byte[] bytes = IOUtils.safelyAllocate(30, 200);
- assertNotNull(bytes);
- assertEquals(30, bytes.length);
- }
-
- @Test
- void testSafelyAllocateLimit() {
- IOUtils.setByteArrayMaxOverride(40);
- try {
- byte[] bytes = IOUtils.safelyAllocate(30, 200);
- assertNotNull(bytes);
- assertEquals(30, bytes.length);
- } finally {
- IOUtils.setByteArrayMaxOverride(-1);
- }
- }
-
- @Test
- void testReadFully() throws IOException {
- byte[] bytes = new byte[2];
- assertEquals(2, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 0, 2));
- assertArrayEquals(new byte[] {1,2}, bytes);
- }
-
- @Test
- void testReadFullyEOF() throws IOException {
- byte[] bytes = new byte[2];
- assertEquals(2, IOUtils.readFully(new NullInputStream(2), bytes, 0, 4));
- assertArrayEquals(new byte[] {0,0}, bytes);
- }
-
- @Test
- void testReadFullyEOFZero() throws IOException {
- byte[] bytes = new byte[2];
- assertEquals(-1, IOUtils.readFully(new NullInputStream(0), bytes, 0, 4));
- assertArrayEquals(new byte[] {0,0}, bytes);
- }
-
- @Test
- void testReadFullySimple() throws IOException {
- byte[] bytes = new byte[2];
- assertEquals(2, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes));
- assertArrayEquals(new byte[] {1,2}, bytes);
- }
-
- @Test
- void testReadFullyOffset() throws IOException {
- byte[] bytes = new byte[3];
- assertEquals(2, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 1, 2));
- assertArrayEquals(new byte[] {0, 1,2}, bytes);
- }
-
- @Test
- void testReadFullyAtLength() throws IOException {
- byte[] bytes = new byte[3];
- assertEquals(3, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 0, 3));
- assertArrayEquals(new byte[] {1,2, 3}, bytes);
- }
-
-
- @Test
- void testReadFullyChannel() throws IOException {
- ByteBuffer bytes = ByteBuffer.allocate(2);
- assertEquals(2, IOUtils.readFully(new SimpleByteChannel(new byte[]{1, 2, 3}), bytes));
- assertArrayEquals(new byte[] {1,2}, bytes.array());
- assertEquals(2, bytes.position());
- }
-
- @Test
- void testReadFullyChannelEOF() throws IOException {
- ByteBuffer bytes = ByteBuffer.allocate(2);
- assertEquals(-1, IOUtils.readFully(new EOFByteChannel(false), bytes));
- assertArrayEquals(new byte[] {0,0}, bytes.array());
- assertEquals(0, bytes.position());
- }
-
- @Test
- void testReadFullyChannelEOFException() {
- ByteBuffer bytes = ByteBuffer.allocate(2);
- assertThrows(IOException.class,
- () -> IOUtils.readFully(new EOFByteChannel(true), bytes));
- }
-
- @Test
- void testReadFullyChannelSimple() throws IOException {
- ByteBuffer bytes = ByteBuffer.allocate(2);
- assertEquals(2, IOUtils.readFully(new SimpleByteChannel(new byte[] {1, 2, 3}), bytes));
- assertArrayEquals(new byte[] {1,2}, bytes.array());
- assertEquals(2, bytes.position());
- }
-
- @Test
- public void testChecksum() {
- assertEquals(0L, IOUtils.calculateChecksum(new byte[0]));
- assertEquals(3057449933L, IOUtils.calculateChecksum(new byte[] { 1, 2, 3, 4}));
- }
-
- @Test
- public void testChecksumStream() throws IOException {
- assertEquals(0L, IOUtils.calculateChecksum(new NullInputStream(0)));
- assertEquals(0L, IOUtils.calculateChecksum(new NullInputStream(1)));
- assertEquals(3057449933L, IOUtils.calculateChecksum(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4})));
- assertThrows(EOFException.class,
- () -> IOUtils.calculateChecksum(new NullInputStream(1, true)));
- }
-
- /**
- * This returns 0 for the first call to skip and then reads
- * as requested. This tests that the fallback to read() works.
- */
- private static class WonkyInputStream extends InputStream {
- int skipCalled;
- int readCalled;
-
- @Override
- public int read() {
- readCalled++;
- return 0;
- }
-
- @Override
- public int read(byte[] arr, int offset, int len) {
- readCalled++;
- return len;
- }
-
- @Override
- public long skip(long len) {
- skipCalled++;
- if (skipCalled == 1) {
- return 0;
- } else if (skipCalled > 100) {
- return len;
- } else {
- return 100;
- }
- }
-
- @Override
- public int available() {
- return 100000;
- }
- }
-
- private static class EOFByteChannel implements ReadableByteChannel {
- private final boolean throwException;
-
- public EOFByteChannel(boolean throwException) {
- this.throwException = throwException;
- }
-
- @Override
- public int read(ByteBuffer dst) throws IOException {
- if (throwException) {
- throw new IOException("EOF");
- }
-
- return -1;
- }
-
- @Override
- public boolean isOpen() {
- return false;
- }
-
- @Override
- public void close() throws IOException {
-
- }
- }
-
- private static class SimpleByteChannel extends InputStream implements ReadableByteChannel {
- private final byte[] bytes;
-
- public SimpleByteChannel(byte[] bytes) {
- this.bytes = bytes;
- }
-
- @Override
- public int read() throws IOException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int read(ByteBuffer dst) throws IOException {
- int toRead = Math.min(bytes.length, dst.capacity());
- dst.put(bytes, 0, toRead);
- return toRead;
- }
-
- @Override
- public boolean isOpen() {
- return false;
- }
- }
-
- public static class NullInputStream extends InputStream {
- private final int bytes;
- private final boolean exception;
-
- private int position;
-
- public NullInputStream(int bytes) {
- this(bytes, false);
- }
-
- public NullInputStream(int bytes, boolean exception) {
- this.bytes = bytes;
- this.exception = exception;
- }
-
- @Override
- public int read() throws IOException {
- if (position >= bytes) {
- return handleReturn();
- }
-
- position++;
- return 0;
- }
-
- private int handleReturn() throws EOFException {
- if (exception) {
- throw new EOFException();
- } else {
- return -1;
- }
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int toRead = Math.min(b.length, len);
- if (toRead > (bytes - position)) {
- return handleReturn();
- }
- toRead = Math.min(toRead, (bytes - position));
-
- position += toRead;
- return toRead;
- }
- }
- }
|