Skip to content

Commit 0a8ea39

Browse files
committed
IO-382 Chunked IO for large arrays
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/io/trunk@1489238 13f79535-47bb-0310-9956-ffa450edef68
1 parent 4b4037f commit 0a8ea39

4 files changed

Lines changed: 217 additions & 0 deletions

File tree

src/changes/changes.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ The <action> type attribute can be add,update,fix,remove.
4747
<body>
4848
<!-- The release date is the date RC is cut -->
4949
<release version="2.5" date="2013-??-??" description="New features and bug fixes.">
50+
<action issue="IO-382" dev="sebb" type="add">
51+
Chunked IO for large arrays.
52+
Added writeChunked(byte[], OutputStream) and writeChunked(char[] Writer)
53+
Added ChunkedOutputStream, ChunkedWriter
54+
</action>
5055
<action issue="IO-385" dev="sebb" type="fix">
5156
FileUtils.doCopyFile can potentially loop for ever
5257
Exit loop if no data to copy

src/main/java/org/apache/commons/io/IOUtils.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,32 @@ public static void write(final byte[] data, final OutputStream output)
13391339
}
13401340
}
13411341

1342+
/**
1343+
* Writes bytes from a <code>byte[]</code> to an <code>OutputStream</code> using chunked writes.
1344+
* This is intended for writing very large byte arrays which might otherwise cause excessive
1345+
* memory usage if the native code has to allocate a copy.
1346+
*
1347+
* @param data the byte array to write, do not modify during output,
1348+
* null ignored
1349+
* @param output the <code>OutputStream</code> to write to
1350+
* @throws NullPointerException if output is null
1351+
* @throws IOException if an I/O error occurs
1352+
* @since 2.5
1353+
*/
1354+
public static void writeChunked(final byte[] data, final OutputStream output)
1355+
throws IOException {
1356+
if (data != null) {
1357+
int bytes = data.length;
1358+
int offset = 0;
1359+
while(bytes > 0) {
1360+
int chunk = Math.min(bytes, DEFAULT_BUFFER_SIZE);
1361+
output.write(data, offset, chunk);
1362+
bytes -= chunk;
1363+
offset += chunk;
1364+
}
1365+
}
1366+
}
1367+
13421368
/**
13431369
* Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code>
13441370
* using the default character encoding of the platform.
@@ -1420,6 +1446,31 @@ public static void write(final char[] data, final Writer output) throws IOExcept
14201446
}
14211447
}
14221448

1449+
/**
1450+
* Writes chars from a <code>char[]</code> to a <code>Writer</code> using chunked writes.
1451+
* This is intended for writing very large byte arrays which might otherwise cause excessive
1452+
* memory usage if the native code has to allocate a copy.
1453+
*
1454+
* @param data the char array to write, do not modify during output,
1455+
* null ignored
1456+
* @param output the <code>Writer</code> to write to
1457+
* @throws NullPointerException if output is null
1458+
* @throws IOException if an I/O error occurs
1459+
* @since 2.5
1460+
*/
1461+
public static void writeChunked(final char[] data, final Writer output) throws IOException {
1462+
if (data != null) {
1463+
int bytes = data.length;
1464+
int offset = 0;
1465+
while(bytes > 0) {
1466+
int chunk = Math.min(bytes, DEFAULT_BUFFER_SIZE);
1467+
output.write(data, offset, chunk);
1468+
bytes -= chunk;
1469+
offset += chunk;
1470+
}
1471+
}
1472+
}
1473+
14231474
/**
14241475
* Writes chars from a <code>char[]</code> to bytes on an
14251476
* <code>OutputStream</code>.
@@ -2825,4 +2876,5 @@ public static void readFully(final ReadableByteChannel input, final ByteBuffer b
28252876
throw new EOFException("Length to read: " + expected + " actual: " + actual);
28262877
}
28272878
}
2879+
28282880
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.io.output;
18+
19+
import java.io.FilterOutputStream;
20+
import java.io.IOException;
21+
import java.io.OutputStream;
22+
23+
/**
24+
* OutputStream which breaks larger output blocks into chunks.
25+
* Native code may need to copy the input array; if the write buffer
26+
* is very large this can cause OOME.
27+
*
28+
* @since 2.5
29+
*/
30+
public class ChunkedOutputStream extends FilterOutputStream {
31+
32+
/**
33+
* The default chunk size to use, i.e. {@value} bytes.
34+
*/
35+
private static final int DEFAULT_CHUNK_SIZE = 1024 * 4;
36+
37+
/**
38+
* The maximum chunk size to us when writing data arrays
39+
*/
40+
private final int chunkSize;
41+
42+
/**
43+
* Creates a new stream that uses the specified chunk size.
44+
*
45+
* @param stream the stream to wrap
46+
* @param chunkSize the chunk size to use; must be a positive number.
47+
* @throws IllegalArgumentException if the chunk size is &lt;= 0
48+
*/
49+
public ChunkedOutputStream(final OutputStream stream, int chunkSize) {
50+
super(stream);
51+
if (chunkSize <= 0) {
52+
throw new IllegalArgumentException();
53+
}
54+
this.chunkSize = chunkSize;
55+
}
56+
57+
/**
58+
* Creates a new stream that uses a chunk size of {@link #DEFAULT_CHUNK_SIZE}
59+
* @param stream the stream to wrap
60+
*/
61+
public ChunkedOutputStream(final OutputStream stream) {
62+
this(stream, DEFAULT_CHUNK_SIZE);
63+
}
64+
65+
/**
66+
* writes the data buffer in chunks to the underlying stream
67+
*/
68+
@Override
69+
public void write(byte[] data, int srcOffset, int length) throws IOException {
70+
int bytes = length;
71+
int dstOffset = srcOffset;
72+
while(bytes > 0) {
73+
int chunk = Math.min(bytes, chunkSize);
74+
out.write(data, dstOffset, chunk);
75+
bytes -= chunk;
76+
dstOffset += chunk;
77+
}
78+
}
79+
80+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.io.output;
18+
19+
import java.io.FilterWriter;
20+
import java.io.IOException;
21+
import java.io.Writer;
22+
23+
/**
24+
* OutputStream which breaks larger output blocks into chunks.
25+
* Native code may need to copy the input array; if the write buffer
26+
* is very large this can cause OOME.
27+
*
28+
* @since 2.5
29+
*/
30+
public class ChunkedWriter extends FilterWriter {
31+
32+
/**
33+
* The default chunk size to use, i.e. {@value} bytes.
34+
*/
35+
private static final int DEFAULT_CHUNK_SIZE = 1024 * 4;
36+
37+
/**
38+
* The maximum chunk size to us when writing data arrays
39+
*/
40+
private final int chunkSize;
41+
42+
/**
43+
* Creates a new writer that uses the specified chunk size.
44+
*
45+
* @param writer the writer to wrap
46+
* @param chunkSize the chunk size to use; must be a positive number.
47+
* @throws IllegalArgumentException if the chunk size is &lt;= 0
48+
*/
49+
public ChunkedWriter(final Writer writer, int chunkSize) {
50+
super(writer);
51+
if (chunkSize <= 0) {
52+
throw new IllegalArgumentException();
53+
}
54+
this.chunkSize = chunkSize;
55+
}
56+
57+
/**
58+
* Creates a new writer that uses a chunk size of {@link #DEFAULT_CHUNK_SIZE}
59+
* @param writer the writer to wrap
60+
*/
61+
public ChunkedWriter(final Writer writer) {
62+
this(writer, DEFAULT_CHUNK_SIZE);
63+
}
64+
65+
/**
66+
* writes the data buffer in chunks to the underlying writer
67+
*/
68+
@Override
69+
public void write(char[] data, int srcOffset, int length) throws IOException {
70+
int bytes = length;
71+
int dstOffset = srcOffset;
72+
while(bytes > 0) {
73+
int chunk = Math.min(bytes, chunkSize);
74+
out.write(data, dstOffset, chunk);
75+
bytes -= chunk;
76+
dstOffset += chunk;
77+
}
78+
}
79+
80+
}

0 commit comments

Comments
 (0)