Skip to content

Commit cbfee18

Browse files
committed
[FS] Use InputStream.transferTo() instead of custom transfers
This reduces the amount of code and can also improve the performance, depending on the type of the source and target stream. For example, since Java-18, if an 'FileInputStream' is transfered to an 'FileOutputStream' the copying is performed using FileChannels, which is faster than just the plain read and write of the content bytes. The only drawback of the new implementation is that precise progress-reporting is not possible anymore. But before it wasn't properly done anyways.
1 parent 034c7cc commit cbfee18

File tree

5 files changed

+13
-91
lines changed

5 files changed

+13
-91
lines changed

resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/filesystem/provider/FileStore.java

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -54,45 +54,6 @@ public abstract class FileStore extends PlatformObject implements IFileStore {
5454
*/
5555
protected static final String[] EMPTY_STRING_ARRAY = {};
5656

57-
/**
58-
* Transfers the contents of an input stream to an output stream, using a large
59-
* buffer.
60-
*
61-
* @param source The input stream to transfer
62-
* @param destination The destination stream of the transfer
63-
* @param length the size of the file or -1 if not known
64-
* @param path A path representing the data being transferred for use in error
65-
* messages.
66-
* @param monitor A progress monitor
67-
*/
68-
private static final void transferStreams(InputStream source, OutputStream destination, long length, String path, IProgressMonitor monitor) throws CoreException {
69-
byte[] buffer = new byte[8192];
70-
SubMonitor subMonitor = SubMonitor.convert(monitor, length >= 0 ? 1 + (int) (length / buffer.length) : 1000);
71-
try (source; destination) {
72-
while (true) {
73-
int bytesRead = -1;
74-
try {
75-
bytesRead = source.read(buffer);
76-
} catch (IOException e) {
77-
String msg = NLS.bind(Messages.failedReadDuringWrite, path);
78-
Policy.error(EFS.ERROR_READ, msg, e);
79-
}
80-
try {
81-
if (bytesRead == -1) {
82-
destination.close();
83-
break;
84-
}
85-
destination.write(buffer, 0, bytesRead);
86-
} catch (IOException e) {
87-
String msg = NLS.bind(Messages.couldNotWrite, path);
88-
Policy.error(EFS.ERROR_WRITE, msg, e);
89-
}
90-
subMonitor.worked(1);
91-
}
92-
} catch (IOException e) { // ignore
93-
}
94-
}
95-
9657
/**
9758
* The default implementation of {@link IFileStore#childInfos(int, IProgressMonitor)}.
9859
* Subclasses should override this method where a more efficient implementation
@@ -197,17 +158,19 @@ protected void copyDirectory(IFileInfo sourceInfo, IFileStore destination, int o
197158
* </ul>
198159
*/
199160
protected void copyFile(IFileInfo sourceInfo, IFileStore destination, int options, IProgressMonitor monitor) throws CoreException {
200-
if ((options & EFS.OVERWRITE) == 0 && destination.fetchInfo().exists())
161+
if ((options & EFS.OVERWRITE) == 0 && destination.fetchInfo().exists()) {
201162
Policy.error(EFS.ERROR_EXISTS, NLS.bind(Messages.fileExists, destination));
202-
long length = sourceInfo.getLength();
163+
}
203164
String sourcePath = toString();
204165
SubMonitor subMonitor = SubMonitor.convert(monitor, NLS.bind(Messages.copying, sourcePath), 100);
205166
try (InputStream in = openInputStream(EFS.NONE, subMonitor.newChild(1)); //
206167
OutputStream out = destination.openOutputStream(EFS.NONE, subMonitor.newChild(1));) {
207-
transferStreams(in, out, length, sourcePath, subMonitor.newChild(98));
168+
in.transferTo(out);
169+
subMonitor.worked(93);
208170
transferAttributes(sourceInfo, destination);
171+
subMonitor.worked(5);
209172
} catch (IOException e) {
210-
// ignore
173+
Policy.error(EFS.ERROR_WRITE, NLS.bind(Messages.failedCopy, sourcePath), e);
211174
} catch (CoreException e) {
212175
//if we failed to write, try to cleanup the half written file
213176
if (!destination.fetchInfo(0, null).exists())

resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/Messages.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public class Messages extends NLS {
3434
public static String failedCreateAccessDenied;
3535
public static String failedMove;
3636
public static String failedCopy;
37-
public static String failedReadDuringWrite;
3837
public static String fileExists;
3938
public static String fileNotFound;
4039
public static String moving;

resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/messages.properties

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ failedCreateAccessDenied=Cannot create file, access denied: {0}.
2626
failedCreateWrongType=Cannot create file because existing file of wrong type exists: {0}.
2727
failedMove = Critical failure moving '{0}' to '{1}'. Content is lost.
2828
failedCopy = Could not copy file '{0}' to '{1}'.
29-
failedReadDuringWrite = Could not read from source when writing file: {0}
3029
fileExists = File already exists on disk: {0}.
3130
fileNotFound = File not found: {0}.
3231
moving = Moving: {0}.

resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,7 +1204,7 @@ public void updateLocalSync(ResourceInfo info, long localSyncInfo) {
12041204
* @see IResource#KEEP_HISTORY
12051205
*/
12061206
public void write(IFile target, InputStream content, IFileInfo fileInfo, int updateFlags, boolean append, IProgressMonitor monitor) throws CoreException {
1207-
SubMonitor subMonitor = SubMonitor.convert(monitor, 4);
1207+
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
12081208
try (content) {
12091209
Resource targetResource = (Resource) target;
12101210
IFileStore store = getStore(target);
@@ -1215,7 +1215,8 @@ public void write(IFile target, InputStream content, IFileInfo fileInfo, int upd
12151215
boolean opened = false;
12161216
try (OutputStream out = store.openOutputStream(options, subMonitor.split(1))) {
12171217
opened = true;
1218-
FileUtil.transferStreams(content, out, store.toString(), subMonitor.split(1));
1218+
content.transferTo(out);
1219+
subMonitor.worked(95);
12191220
} catch (CoreException e) {
12201221
// On Windows an attempt to open an output stream on a hidden file results in
12211222
// FileNotFoundException.
@@ -1231,19 +1232,20 @@ public void write(IFile target, InputStream content, IFileInfo fileInfo, int upd
12311232
// set hidden=false and retry:
12321233
fileInfo.setAttribute(EFS.ATTRIBUTE_HIDDEN, false);
12331234
store.putInfo(fileInfo, EFS.SET_ATTRIBUTES, subMonitor.split(1));
1234-
try (OutputStream out = store.openOutputStream(options, null)) {
1235+
try (OutputStream out = store.openOutputStream(options, subMonitor.split(1))) {
12351236
// restore Hidden Attribute:
12361237
fileInfo.setAttribute(EFS.ATTRIBUTE_HIDDEN, true);
12371238
store.putInfo(fileInfo, EFS.SET_ATTRIBUTES, subMonitor.split(1));
1238-
FileUtil.transferStreams(content, out, store.toString(), subMonitor.split(1));
1239+
content.transferTo(out);
1240+
subMonitor.worked(92);
12391241
}
12401242
}
12411243
} catch (IOException e) {
1242-
// Exception on OutputStream.close()
12431244
String msg = NLS.bind(Messages.localstore_couldNotWrite, store.toString());
12441245
throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, IPath.fromOSString(store.toString()), msg, e);
12451246
}
12461247
finishWrite(targetResource, store);
1248+
subMonitor.worked(4);
12471249
} catch (IOException streamCloseIgnored) {
12481250
// ignore Exception on InputStream.close()
12491251
}

resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/FileUtil.java

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@
1616
*******************************************************************************/
1717
package org.eclipse.core.internal.utils;
1818

19-
import java.io.ByteArrayInputStream;
2019
import java.io.IOException;
21-
import java.io.InputStream;
22-
import java.io.OutputStream;
2320
import java.net.URI;
2421
import java.nio.ByteBuffer;
2522
import java.nio.CharBuffer;
@@ -31,18 +28,13 @@
3128
import org.eclipse.core.filesystem.IFileStore;
3229
import org.eclipse.core.filesystem.IFileSystem;
3330
import org.eclipse.core.filesystem.URIUtil;
34-
import org.eclipse.core.internal.resources.ResourceException;
3531
import org.eclipse.core.internal.resources.Workspace;
3632
import org.eclipse.core.resources.IFile;
37-
import org.eclipse.core.resources.IResourceStatus;
3833
import org.eclipse.core.resources.ResourceAttributes;
3934
import org.eclipse.core.runtime.CoreException;
4035
import org.eclipse.core.runtime.IPath;
41-
import org.eclipse.core.runtime.IProgressMonitor;
4236
import org.eclipse.core.runtime.Platform;
43-
import org.eclipse.core.runtime.SubMonitor;
4437
import org.eclipse.core.runtime.content.IContentDescription;
45-
import org.eclipse.osgi.util.NLS;
4638

4739
/**
4840
* Static utility methods for manipulating Files and URIs.
@@ -306,39 +298,6 @@ public static IPath toPath(URI uri) {
306298
return null;
307299
}
308300

309-
public static final void transferStreams(InputStream source, OutputStream destination, String path,
310-
IProgressMonitor monitor) throws CoreException {
311-
SubMonitor subMonitor = SubMonitor.convert(monitor);
312-
try (source) {
313-
try (destination) {
314-
if (source instanceof ByteArrayInputStream) {
315-
// ByteArrayInputStream does overload transferTo avoiding buffering
316-
((ByteArrayInputStream) source).transferTo(destination);
317-
subMonitor.split(1);
318-
} else {
319-
byte[] buffer = new byte[8192];
320-
while (true) {
321-
int bytesRead = -1;
322-
try {
323-
bytesRead = source.read(buffer);
324-
} catch (IOException e) {
325-
String msg = NLS.bind(Messages.localstore_failedReadDuringWrite, path);
326-
throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, IPath.fromOSString(path), msg, e);
327-
}
328-
if (bytesRead == -1) {
329-
break;
330-
}
331-
destination.write(buffer, 0, bytesRead);
332-
subMonitor.split(1);
333-
}
334-
}
335-
}
336-
} catch (IOException e) {
337-
String msg = NLS.bind(Messages.localstore_couldNotWrite, path);
338-
throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, IPath.fromOSString(path), msg, e);
339-
}
340-
}
341-
342301
public static char[] readAllChars(IFile file) throws CoreException {
343302
byte[] content = file.readAllBytes();
344303
Charset charset = getCharset(file, content);

0 commit comments

Comments
 (0)