Skip to content

Commit

Permalink
Merge pull request #1 from yegor256/master
Browse files Browse the repository at this point in the history
Syncing fork.
  • Loading branch information
paulodamaso authored Apr 30, 2018
2 parents 287c998 + eb31449 commit 404bd06
Show file tree
Hide file tree
Showing 2 changed files with 270 additions and 0 deletions.
144 changes: 144 additions & 0 deletions src/main/java/org/cactoos/io/TailInput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017-2018 Yegor Bugayenko
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.cactoos.io;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.cactoos.Input;
import org.cactoos.scalar.MinOf;
import org.cactoos.text.FormattedText;

/**
* Input showing only last N bytes of the stream.
*
* <p>There is no thread-safety guarantee.
*
* @author Krzysztof Krason (Krzysztof.Krason@gmail.com)
* @version $Id$
* @since 0.30
*/
public final class TailInput implements Input {

/**
* Input to decorate.
*/
private final Input input;

/**
* Number of last bytes to show from the input.
*/
private final int count;

/**
* Maximum number of bytes to read at once.
*/
private final int max;

/**
* Constructor.
* @param inpt Input to decorate
* @param bytes Number of last bytes to show from input
*/
public TailInput(final Input inpt, final int bytes) {
// @checkstyle MagicNumber (1 line)
this(inpt, bytes, 16384);
}

/**
* Constructor.
* @param inpt Input to decorate
* @param bytes Number of last bytes to show from input
* @param maximum Maximum number of bytes to read at once
*/
public TailInput(final Input inpt, final int bytes, final int maximum) {
this.input = inpt;
this.count = bytes;
this.max = maximum;
}

@Override
public InputStream stream() throws IOException {
if (this.max < this.count) {
throw new IllegalArgumentException(
new FormattedText(
"Can't tail %d bytes if buffer is set to %d",
this.count, this.max
).asString()
);
}
final byte[] buffer = new byte[this.max];
final byte[] response = new byte[this.count];
int num = 0;
final InputStream strm = this.input.stream();
for (int read = strm.read(buffer); read > 0; read = strm.read(buffer)) {
if (read < this.max && read < this.count) {
num = this.copyPartial(buffer, response, num, read);
} else {
num = this.copy(buffer, response, read);
}
}
return new ByteArrayInputStream(response, 0, num);
}

/**
* Copy full buffer to response.
* @param buffer The buffer array
* @param response The response array
* @param read Number of bytes read in buffer
* @return Number of bytes in the response buffer
*/
private int copy(final byte[] buffer, final byte[] response,
final int read) {
System.arraycopy(
buffer, read - this.count, response, 0, this.count
);
return new MinOf(this.count, read).intValue();
}

/**
* Copy buffer to response for read count smaller then buffer size.
* @param buffer The buffer array
* @param response The response array
* @param num Number of bytes in response array from previous read
* @param read Number of bytes read in the buffer
* @return New count of bytes in the response array
* @checkstyle ParameterNumberCheck (3 lines)
*/
private int copyPartial(final byte[] buffer, final byte[] response,
final int num, final int read) {
final int result;
if (num > 0) {
System.arraycopy(
response, read, response, 0, this.count - read
);
System.arraycopy(buffer, 0, response, this.count - read, read);
result = this.count;
} else {
System.arraycopy(buffer, 0, response, 0, read);
result = read;
}
return result;
}
}
126 changes: 126 additions & 0 deletions src/test/java/org/cactoos/io/TailInputTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017-2018 Yegor Bugayenko
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.cactoos.io;

import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.Test;

/**
* Tests for {@link TailInput}.
* @author Krzysztof Krason (Krzysztof.Krason@gmail.com)
* @version $Id$
* @since 0.30
* @checkstyle JavadocMethodCheck (500 lines)
*/
public final class TailInputTest {

@Test
public void tailsOnLongStream() throws IOException {
final int size = 4;
final byte[] bytes = this.generate(size);
MatcherAssert.assertThat(
new BytesOf(
new TailInput(new InputOf(new BytesOf(bytes)), size - 1)
).asBytes(),
Matchers.equalTo(Arrays.copyOfRange(bytes, 1, bytes.length))
);
}

@Test
public void tailsOnExactStream() throws IOException {
final int size = 4;
final byte[] bytes = this.generate(size);
MatcherAssert.assertThat(
new BytesOf(
new TailInput(new InputOf(new BytesOf(bytes)), size)
).asBytes(),
Matchers.equalTo(bytes)
);
}

@Test
public void tailsOnExactStreamAndBuffer() throws IOException {
final int size = 4;
final byte[] bytes = this.generate(size);
MatcherAssert.assertThat(
new BytesOf(
new TailInput(new InputOf(new BytesOf(bytes)), size, size)
).asBytes(),
Matchers.equalTo(bytes)
);
}

@Test
public void tailsOnShorterStream() throws IOException {
final int size = 4;
final byte[] bytes = this.generate(size);
MatcherAssert.assertThat(
new BytesOf(
new TailInput(new InputOf(new BytesOf(bytes)), size + 1)
).asBytes(),
Matchers.equalTo(bytes)
);
}

@Test
public void tailsOnStreamLongerThanBufferAndBytes() throws IOException {
final int size = 4;
final byte[] bytes = this.generate(size);
MatcherAssert.assertThat(
new BytesOf(
new TailInput(
new InputOf(new BytesOf(bytes)), size - 1, size - 1
)
).asBytes(),
Matchers.equalTo(
Arrays.copyOfRange(bytes, 1, bytes.length)
)
);
}

@Test(expected = IllegalArgumentException.class)
public void failsIfBufferSizeSmallerThanTailSize() throws IOException {
final int size = 4;
new BytesOf(
new TailInput(
new InputOf(new BytesOf(this.generate(size))), size, size - 1
)
).asBytes();
}

/**
* Generate random byte array.
* @param size Size of array
* @return Generated array
*/
private byte[] generate(final int size) {
final byte[] bytes = new byte[size];
new Random().nextBytes(bytes);
return bytes;
}
}

0 comments on commit 404bd06

Please sign in to comment.