blob: 0f22781901ed5d8a5b3b7aee145c787f17bf718b [file] [log] [blame]
/*
* Copyright 2017 HugeGraph Authors
*
* 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 com.baidu.hugegraph.computer.core.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Random;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.slf4j.Logger;
import com.baidu.hugegraph.computer.core.common.Constants;
import com.baidu.hugegraph.computer.core.store.entry.EntriesUtil;
import com.baidu.hugegraph.computer.suite.unit.UnitTestBase;
import com.baidu.hugegraph.testutil.Assert;
import com.baidu.hugegraph.util.Log;
public class BufferedFileTest {
private static final Logger LOG = Log.logger(
BufferedFileTest.class);
private static final int BUFFER_SIZE = 128;
@Test
public void testConstructor() throws IOException {
File file = createTempFile();
try {
try (BufferedFileOutput output = new BufferedFileOutput(file)) {
Assert.assertEquals(0, output.position());
}
try (BufferedFileInput input = new BufferedFileInput(file)) {
Assert.assertEquals(0, input.position());
}
Assert.assertThrows(IllegalArgumentException.class, () -> {
new BufferedFileOutput(new RandomAccessFile(file,
Constants.FILE_MODE_WRITE),
1);
}, e -> {
Assert.assertContains("The parameter bufferSize must be >= 8",
e.getMessage());
});
Assert.assertThrows(IllegalArgumentException.class, () -> {
new BufferedFileInput(new RandomAccessFile(file,
Constants.FILE_MODE_READ),
1);
}, e -> {
Assert.assertContains("The parameter bufferSize must be >= 8",
e.getMessage());
});
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testInt() throws IOException {
File file = createTempFile();
try {
try (BufferedFileOutput output = createOutput(file)) {
for (int i = -128; i <= 127; i++) {
output.writeInt(i);
}
output.writeInt(Integer.MAX_VALUE);
output.writeInt(Integer.MIN_VALUE);
}
try (BufferedFileInput input = createInput(file)) {
for (int i = -128; i <= 127; i++) {
Assert.assertEquals(i, input.readInt());
}
Assert.assertEquals(Integer.MAX_VALUE, input.readInt());
Assert.assertEquals(Integer.MIN_VALUE, input.readInt());
}
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testWriteIntWithPosition() throws IOException {
File file = createTempFile();
try {
try (BufferedFileOutput output = createOutput(file)) {
for (int i = -128; i <= 127; i++) {
output.writeInt(i);
}
output.writeFixedInt(0, 1);
output.writeFixedInt(12, 2);
// Next buffer
output.writeFixedInt(200, 3);
// Previous buffer
output.writeFixedInt(100, 4);
output.writeInt(Integer.MAX_VALUE);
output.writeInt(Integer.MIN_VALUE);
// Current buffer
output.writeInt(5);
output.writeFixedInt(output.position() - Integer.BYTES, 6);
}
try (BufferedFileInput input = createInput(file)) {
for (int i = 0; i < 256; i++) {
int expectValue = i - 128;
int position = i * 4;
int readValue = input.readInt();
if (position != 0 && position != 12 &&
position != 200 && position != 100 &&
position != 1000) {
Assert.assertEquals(expectValue, readValue);
}
}
input.seek(0);
Assert.assertEquals(1, input.readInt());
input.seek(12);
Assert.assertEquals(2, input.readInt());
input.seek(200);
Assert.assertEquals(3, input.readInt());
input.seek(100);
Assert.assertEquals(4, input.readInt());
input.seek(256 * 4);
Assert.assertEquals(Integer.MAX_VALUE, input.readInt());
Assert.assertEquals(Integer.MIN_VALUE, input.readInt());
Assert.assertEquals(6, input.readInt());
}
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testByteArray() throws IOException {
int loopTimes = 129;
byte[] array = UnitTestBase.randomBytes(10);
File file = createTempFile();
try (BufferedFileOutput output = createOutput(file)) {
for (int i = 0; i < loopTimes; i++) {
output.write(array);
}
}
byte[] arrayRead = new byte[10];
try (DataInputStream dis = new DataInputStream(
new FileInputStream(file))) {
for (int i = 0; i < loopTimes; i++) {
dis.readFully(arrayRead);
Assert.assertArrayEquals(array, arrayRead);
}
}
try (BufferedFileInput input = createInput(file)) {
for (int i = 0; i < loopTimes; i++) {
input.readFully(arrayRead);
Assert.assertArrayEquals(array, arrayRead);
}
}
FileUtils.deleteQuietly(file);
}
@Test
public void testLargeByteArray() throws IOException {
int loopTimes = 10;
int arraySize = 1280; // large than buffer size
byte[] array = UnitTestBase.randomBytes(arraySize);
File file = createTempFile();
try {
try (BufferedFileOutput output = createOutput(file)) {
for (int i = 0; i < loopTimes; i++) {
output.write(array);
}
}
byte[] arrayRead = new byte[arraySize];
try (BufferedFileInput input = createInput(file)) {
for (int i = 0; i < loopTimes; i++) {
input.readFully(arrayRead);
Assert.assertArrayEquals(array, arrayRead);
}
}
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testInputSeekAtRandom() throws IOException {
int size = 128;
File file = createTempFile();
try {
try (BufferedFileOutput output = createOutput(file)) {
for (int i = 0; i < size; i++) {
output.writeInt(i);
}
for (int i = size; i >= 0; i--) {
output.seek(i * 4);
output.writeInt(size - i);
}
}
Random random = new Random(1001);
try (BufferedFileInput input = createInput(file)) {
for (int i = 0; i <= 10; i++) {
long position = 4 * random.nextInt(size);
input.seek(position);
Assert.assertEquals(size - position / 4, input.readInt());
}
}
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testInputSeekOutRange() throws IOException {
File file = createTempFile();
try {
try (BufferedFileOutput output = createOutput(file)) {
output.writeInt(1);
output.writeInt(2);
output.writeInt(3);
}
try (BufferedFileInput input = createInput(file)) {
Assert.assertEquals(1, input.readInt());
input.skip(4);
Assert.assertThrows(EOFException.class, () -> {
input.seek(13); // Out of range
}, e -> {
Assert.assertContains("reach the end of file",
e.getMessage());
});
}
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testOutputSeekOutRange() throws IOException {
File file = createTempFile();
try {
try (BufferedFileOutput output = createOutput(file)) {
output.seek(100L);
output.writeInt(1);
output.seek(511L);
output.writeInt(2);
}
try (BufferedFileInput input = createInput(file)) {
input.seek(100L);
Assert.assertEquals(1, input.readInt());
input.seek(511L);
Assert.assertEquals(2, input.readInt());
}
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testSeekAtEnd() throws IOException {
File file = createTempFile();
try {
try (BufferedFileOutput output = createOutput(file)) {
for (int i = -128; i <= 127; i++) {
output.writeInt(i);
}
// Overwrite last 2 elements
output.seek(256 * 4 - 8);
output.writeInt(Integer.MAX_VALUE);
output.writeInt(Integer.MIN_VALUE);
}
try (BufferedFileInput input = createInput(file)) {
for (int i = -128; i <= 125; i++) {
Assert.assertEquals(i, input.readInt());
}
Assert.assertEquals(Integer.MAX_VALUE, input.readInt());
Assert.assertEquals(Integer.MIN_VALUE, input.readInt());
input.seek(input.position() - 8);
Assert.assertEquals(Integer.MAX_VALUE, input.readInt());
Assert.assertEquals(Integer.MIN_VALUE, input.readInt());
}
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testSkip() throws IOException {
File file = createTempFile();
try {
try (BufferedFileOutput output = createOutput(file)) {
for (int i = -128; i <= 127; i++) {
output.writeByte(i);
}
output.skip(4L);
output.writeByte(127);
output.skip(4L);
output.skip(1280L);
output.writeByte(1);
Assert.assertThrows(IllegalArgumentException.class, () -> {
output.skip(-1);
}, e -> {
e.getMessage().contains("The parameter bytesToSkip must " +
"be >= 0");
});
}
try (BufferedFileInput input = createInput(file)) {
for (int i = -128; i <= 127; i++) {
Assert.assertEquals(i, input.readByte());
}
input.skip(4);
Assert.assertEquals(127, input.readByte());
input.skip(4);
input.skip(1280);
Assert.assertEquals((byte) 1, input.readByte());
Assert.assertThrows(IllegalArgumentException.class, () -> {
input.skip(-1);
}, e -> {
e.getMessage().contains("The parameter bytesToSkip must " +
"be >= 0");
});
}
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testPosition() throws IOException {
int size = 1024;
File file = createTempFile();
try {
try (BufferedFileOutput output = createOutput(file)) {
for (int i = 0; i < size; i++) {
Assert.assertEquals(i * 4, output.position());
output.writeInt(i);
}
}
try (BufferedFileInput input = createInput(file)) {
for (int i = 0; i < size; i++) {
Assert.assertEquals(i * 4, input.position());
Assert.assertEquals(i, input.readInt());
}
Random random = new Random();
for (int i = 0; i < 10; i++) {
long position = 4 * random.nextInt(size);
input.seek(position);
Assert.assertEquals(position / 4, input.readInt());
Assert.assertEquals(position + 4, input.position());
}
}
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testAvailable() throws IOException {
int size = 1024;
File file = createTempFile();
try {
try (BufferedFileOutput output = createOutput(file)) {
for (int i = 0; i < size; i++) {
output.writeInt(i);
}
}
try (BufferedFileInput input = createInput(file)) {
for (int i = 0; i < size; i++) {
Assert.assertEquals(4096 - i * 4, input.available());
Assert.assertEquals(i, input.readInt());
}
}
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testLongPerformanceUnsafe() throws IOException {
long size = 1024;
long startTime;
long endTime;
long time;
File file = new File("long-unsafe.bin");
try {
startTime = System.currentTimeMillis();
try (BufferedFileOutput output = new BufferedFileOutput(file)) {
for (long i = 0; i < size / 8; i++) {
output.writeLong(i);
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Write {} bytes use BufferedFileOutput.writeLong " +
"takes {} ms", size, time);
startTime = System.currentTimeMillis();
try (BufferedFileInput input = new BufferedFileInput(file)) {
for (long i = 0; i < size / 8; i++) {
input.readLong();
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Read {} bytes use BufferedFileInput.readLong " +
"takes {} ms", size, time);
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testLongPerformanceNormal() throws IOException {
long size = 1024;
long startTime;
long endTime;
long time;
File file = new File("long-data.bin");
try {
startTime = System.currentTimeMillis();
try (DataOutputStream output = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(file)))) {
for (long i = 0; i < size / 8; i++) {
output.writeLong(i);
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Write {} bytes use DataOutputStream.writeLong" +
" takes {} ms", size, time);
startTime = System.currentTimeMillis();
try (DataInputStream input = new DataInputStream(
new BufferedInputStream(
new FileInputStream(file)))) {
for (long i = 0; i < size / 8; i++) {
input.readLong();
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Read {} bytes use DataInputStream.readLong " +
"takes {} ms", size, time);
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testIntPerformanceIntUnsafe() throws IOException {
int size = 1024;
long startTime;
long endTime;
File file = new File("int-unsafe.bin");
try {
startTime = System.currentTimeMillis();
try (BufferedFileOutput output = new BufferedFileOutput(file)) {
for (int i = 0; i < size / 4; i++) {
output.writeInt(i);
}
}
endTime = System.currentTimeMillis();
long time = endTime - startTime;
LOG.info("Write {} bytes use BufferedFileOutput.writeInt " +
"takes {} ms", size, time);
startTime = System.currentTimeMillis();
try (BufferedFileInput input = new BufferedFileInput(file)) {
for (int i = 0; i < size / 4; i++) {
input.readInt();
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Read {} bytes use BufferedFileInput.readInt " +
"takes {} ms", size, time);
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testIntPerformanceNormal() throws IOException {
int size = 1024;
long startTime;
long endTime;
long time;
File dataFile = new File("int-data-out.bin");
try {
startTime = System.currentTimeMillis();
try (DataOutputStream output = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(dataFile)))) {
for (int i = 0; i < size / 4; i++) {
output.writeInt(i);
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Write {} bytes use DataOutputStream.writeInt " +
"takes {} ms", size, time);
startTime = System.currentTimeMillis();
try (DataInputStream input = new DataInputStream(
new BufferedInputStream(
new FileInputStream(dataFile)))) {
for (int i = 0; i < size / 4; i++) {
input.readInt();
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Read {} bytes use DataInputStream.readInt " +
"takes {} ms", size, time);
} finally {
FileUtils.deleteQuietly(dataFile);
}
}
@Test
public void testByteArrayPerformanceUnsafe() throws IOException {
int size = 1024;
long startTime;
long endTime;
long time;
byte[] writeArray = UnitTestBase.randomBytes(16);
byte[] readArray = new byte[16];
File file = new File("int-unsafe-out.bin");
try {
startTime = System.currentTimeMillis();
try (BufferedFileOutput output = new BufferedFileOutput(file)) {
for (int i = 0; i < size / writeArray.length; i++) {
output.write(writeArray);
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Write {} bytes use BufferedFileOutput" +
".write takes {} ms", size, time);
startTime = System.currentTimeMillis();
try (BufferedFileInput input = new BufferedFileInput(file)) {
for (int i = 0; i < size / readArray.length; i++) {
input.readFully(readArray);
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Read {} bytes use BufferedFileInput.readFully " +
"takes {} ms", size, time);
} finally {
FileUtils.deleteQuietly(file);
}
}
@Test
public void testByteArrayPerformanceNormal() throws IOException {
int size = 1024;
long startTime;
long endTime;
long time;
byte[] writeArray = UnitTestBase.randomBytes(16);
byte[] readArray = new byte[16];
File dataFile = new File("int-data-out.bin");
try {
startTime = System.currentTimeMillis();
try (DataOutputStream output = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(dataFile)))) {
for (int i = 0; i < size / writeArray.length; i++) {
output.write(writeArray);
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Write {} bytes use DataOutputStream.write takes {} ms",
size, time);
startTime = System.currentTimeMillis();
try (DataInputStream input = new DataInputStream(
new BufferedInputStream(
new FileInputStream(dataFile)))) {
for (int i = 0; i < size / writeArray.length; i++) {
input.readFully(readArray);
}
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
LOG.info("Read {} bytes use DataInputStream.readFully takes {} ms",
size, time);
} finally {
FileUtils.deleteQuietly(dataFile);
}
}
@Test
public void testCompare() throws IOException {
// BufferedFileInput compare to BufferedFileInput
File file1 = createTempFile();
File file2 = createTempFile();
try (BufferedFileInput input1 = inputByString(file1, "apple");
BufferedFileInput input2 = inputByString(file2, "banana")) {
int result = input1.compare(0, input1.available(), input2, 0,
input2.available());
Assert.assertLt(0, result);
} finally {
FileUtils.deleteQuietly(file1);
FileUtils.deleteQuietly(file2);
}
// UnsafeBytesInput compare to BufferedFileInput
File file3 = createTempFile();
try (BufferedFileInput fileInput = inputByString(file3, "apple")) {
@SuppressWarnings("resource")
UnsafeBytesOutput output = new UnsafeBytesOutput(
Constants.SMALL_BUF_SIZE);
output.writeBytes("banana");
@SuppressWarnings("resource")
RandomAccessInput input = new UnsafeBytesInput(output.buffer());
int result = input.compare(0, input.available(), fileInput, 0,
fileInput.available());
Assert.assertGt(0, result);
} finally {
FileUtils.deleteQuietly(file3);
}
}
@Test
public void testCompareDiffRange() throws IOException {
File file1, file2;
// BufferedFileInput compare to other RandomAccessInput
file1 = createTempFile();
try (BufferedFileInput input1 = inputByString(file1, "hugegraph")) {
UnsafeBytesOutput output = new UnsafeBytesOutput(
Constants.SMALL_BUF_SIZE);
output.writeBytes("banana");
RandomAccessInput input = EntriesUtil.inputFromOutput(output);
int result = input1.compare(0, input1.available(), input, 0,
input.available());
Assert.assertGt(0, result);
} finally {
FileUtils.deleteQuietly(file1);
}
// Compare two BufferedFileInput
file1 = createTempFile();
file2 = createTempFile();
String content = "let's make baidu great again";
try (BufferedFileInput input1 = inputByString(file1, content, 10);
BufferedFileInput input2 = inputByString(file1, content, 10)) {
int result;
/*
* Compare range in buffer
* "le" compare to "et"
*/
result = input1.compare(0, 2, input2, 1, 2);
Assert.assertGt(0, result);
/*
* Compare range in input1 buffer but not in input2 buffer
* "le" compare to "aid"
*/
result = input1.compare(0, 2, input2, 12, 2);
Assert.assertGt(0, result);
/*
* Compare range not in buffer
* "aid" compare to "aid"
*/
result = input1.compare(12, 3, input2, 12, 3);
Assert.assertEquals(0, result);
/*
* Compare range not in input1 buffer but in input2 buffer
* "aid" compare to "let"
*/
result = input1.compare(12, 3, input2, 0, 3);
Assert.assertLt(0, result);
} finally {
FileUtils.deleteQuietly(file1);
FileUtils.deleteQuietly(file2);
}
}
private static File createTempFile() throws IOException {
return File.createTempFile(UUID.randomUUID().toString(), null);
}
private static BufferedFileOutput createOutput(File file)
throws FileNotFoundException {
return new BufferedFileOutput(new RandomAccessFile(file,
Constants.FILE_MODE_WRITE),
BUFFER_SIZE);
}
private static BufferedFileInput createInput(File file)
throws IOException {
return new BufferedFileInput(new RandomAccessFile(file,
Constants.FILE_MODE_READ),
BUFFER_SIZE);
}
private static BufferedFileInput inputByString(File file, String s)
throws IOException {
BufferedFileOutput output = new BufferedFileOutput(file);
output.writeBytes(s);
output.close();
return new BufferedFileInput(file);
}
private static BufferedFileInput inputByString(File file, String s,
int bufferCapacity)
throws IOException {
BufferedFileOutput output = new BufferedFileOutput(file);
output.writeBytes(s);
output.close();
RandomAccessFile randomAccessFile = new RandomAccessFile(
file, Constants.FILE_MODE_READ);
return new BufferedFileInput(randomAccessFile, bufferCapacity);
}
}