Bug 562164 - Add JUnit tests for IMemoryExporter implementations

Added "number of units to retrieve" parameter to ReadMemory#from
Fixed result compare for text-based formats
Returning back 64KiB DATA_PER_TRANSFER optimization
Reworked FileExport from "O extends AutoCloseable"
Renamed ReadMemory to IReadMemory

Change-Id: Id7eb51015884d5dbffa5e91e9601f5e6ddb52d90
Signed-off-by: Alexander Fedorov <alexander.fedorov@arsysop.ru>
diff --git a/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/EmulateMemory.java b/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/EmulateMemory.java
index c97d8c8..a686536 100644
--- a/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/EmulateMemory.java
+++ b/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/EmulateMemory.java
@@ -16,32 +16,56 @@
 import java.math.BigInteger;
 import java.util.LinkedHashMap;
 import java.util.Map;
-import java.util.Optional;
 
-import org.eclipse.cdt.debug.core.memory.transport.ReadMemory;
+import org.eclipse.cdt.debug.core.memory.transport.IReadMemory;
 import org.eclipse.cdt.debug.core.memory.transport.WriteMemory;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.MemoryByte;
 import org.eclipse.osgi.util.NLS;
 
 /**
  * Simple emulator of memory block to control the memory transport without real device
  *
  */
-final class EmulateMemory implements WriteMemory, ReadMemory {
+final class EmulateMemory implements WriteMemory, IReadMemory {
 
+	private final BigInteger addressable;
 	private final BigInteger base;
+	//FIXME: needs improvements, assumes identical pattern during write and read
 	private final Map<BigInteger, byte[]> storage;
 
-	EmulateMemory(BigInteger base) {
-		storage = new LinkedHashMap<>();
+	EmulateMemory(BigInteger addressable, BigInteger base) {
+		this.addressable = addressable;
 		this.base = base;
+		this.storage = new LinkedHashMap<>();
 	}
 
 	@Override
-	public byte[] from(BigInteger offset) throws DebugException {
-		return Optional.ofNullable(storage.get(offset)).orElseThrow(() -> failed(offset));
+	public MemoryByte[] from(BigInteger offset, long units) throws DebugException {
+		int length = (int) (units * addressable.longValue());
+		MemoryByte[] result = new MemoryByte[length];
+		int i = 0;
+		while (i < length) {
+			byte[] raw = storage.getOrDefault(offset.add(BigInteger.valueOf(i)), new byte[0]);
+			int obtained = raw.length;
+			if (obtained > 0) {
+				for (int j = 0; j < obtained; j++) {
+					result[i + j] = new MemoryByte(raw[j]);
+				}
+				i = i + obtained;
+			} else {
+				//unreachable with current test data
+				MemoryByte unavailable = new MemoryByte();
+				unavailable.setReadable(false);
+				for (int j = i; j < length; j++) {
+					result[i + j] = unavailable;
+				}
+				i = length;
+			}
+		}
+		return result;
 	}
 
 	@Override
diff --git a/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/PlainTextTransportTest.java b/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/PlainTextTransportTest.java
index a286138..1e9dc99 100644
--- a/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/PlainTextTransportTest.java
+++ b/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/PlainTextTransportTest.java
@@ -18,6 +18,7 @@
 import java.math.BigInteger;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.List;
 import java.util.function.Consumer;
 
 import org.eclipse.cdt.debug.core.memory.transport.ExportRequest;
@@ -58,7 +59,7 @@
 	}
 
 	private void transport(String name, BigInteger end) throws CoreException, IOException {
-		EmulateMemory memory = new EmulateMemory(base);
+		EmulateMemory memory = new EmulateMemory(BigInteger.valueOf(1), base);
 		Consumer<BigInteger> scroll = new CollectScrolls();
 		File input = new InputFile(name).get();
 		new PlainTextImport(input, new ImportRequest(base, start, memory), scroll)//
@@ -66,11 +67,11 @@
 		File output = new OutputFile(name).get();
 		new PlainTextExport(output, new ExportRequest(start, end, BigInteger.ONE, memory))//
 				.run(new NullProgressMonitor());
-		Assert.assertArrayEquals(read(input), read(output));
+		Assert.assertEquals(read(input), read(output));
 	}
 
-	private byte[] read(File file) throws IOException {
-		return Files.readAllBytes(Paths.get(file.toString()));
+	private List<String> read(File file) throws IOException {
+		return Files.readAllLines(Paths.get(file.toString()));
 	}
 
 }
diff --git a/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/RAWBinaryTransportTest.java b/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/RAWBinaryTransportTest.java
index 83607c8..34efd39 100644
--- a/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/RAWBinaryTransportTest.java
+++ b/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/RAWBinaryTransportTest.java
@@ -58,7 +58,7 @@
 	}
 
 	private void transport(String name, BigInteger end) throws CoreException, IOException {
-		EmulateMemory memory = new EmulateMemory(base);
+		EmulateMemory memory = new EmulateMemory(BigInteger.valueOf(1), base);
 		Consumer<BigInteger> scroll = new CollectScrolls();
 		File input = new InputFile(name).get();
 		new RAWBinaryImport(input, new ImportRequest(base, start, memory), scroll)//
diff --git a/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/SRecordTransportTest.java b/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/SRecordTransportTest.java
index cac4cfa..aaf6666 100644
--- a/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/SRecordTransportTest.java
+++ b/memory/org.eclipse.cdt.debug.core.memory.tests/src/org/eclipse/cdt/debug/core/memory/tests/SRecordTransportTest.java
@@ -18,6 +18,7 @@
 import java.math.BigInteger;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.List;
 import java.util.function.Consumer;
 
 import org.eclipse.cdt.debug.core.memory.transport.ExportRequest;
@@ -58,7 +59,7 @@
 	}
 
 	private void transport(String name, BigInteger end) throws CoreException, IOException {
-		EmulateMemory memory = new EmulateMemory(base);
+		EmulateMemory memory = new EmulateMemory(BigInteger.valueOf(1), base);
 		Consumer<BigInteger> scroll = new CollectScrolls();
 		File input = new InputFile(name).get();
 		File output = new OutputFile(name).get();
@@ -74,8 +75,9 @@
 		Assert.assertArrayEquals(read(input), read(output));
 	}
 
-	private byte[] read(File file) throws IOException {
-		return Files.readAllBytes(Paths.get(file.toString()));
+	private String[] read(File file) throws IOException {
+		List<String> lines = Files.readAllLines(Paths.get(file.toString()));
+		return lines.toArray(new String[lines.size()]);
 	}
 
 }
diff --git a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/ExportRequest.java b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/ExportRequest.java
index ff92e8c..de773a2 100644
--- a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/ExportRequest.java
+++ b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/ExportRequest.java
@@ -27,9 +27,9 @@
 	private final BigInteger start;
 	private final BigInteger end;
 	private final BigInteger addressable;
-	private final ReadMemory read;
+	private final IReadMemory read;
 
-	public ExportRequest(BigInteger start, BigInteger end, BigInteger addressable, ReadMemory read) {
+	public ExportRequest(BigInteger start, BigInteger end, BigInteger addressable, IReadMemory read) {
 		this.start = start;
 		this.end = end;
 		this.addressable = addressable;
@@ -64,7 +64,7 @@
 	 *
 	 * @return reader
 	 */
-	public ReadMemory read() {
+	public IReadMemory read() {
 		return read;
 	}
 }
diff --git a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/FileExport.java b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/FileExport.java
index 1f801fc..72fd720 100644
--- a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/FileExport.java
+++ b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/FileExport.java
@@ -13,8 +13,11 @@
  *******************************************************************************/
 package org.eclipse.cdt.debug.core.memory.transport;
 
+import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.math.BigInteger;
 
 import org.eclipse.cdt.debug.internal.core.memory.transport.Messages;
@@ -32,12 +35,12 @@
  *
  * @since 0.1
  */
-public abstract class FileExport<O extends AutoCloseable> implements ICoreRunnable {
+public abstract class FileExport implements ICoreRunnable {
 
 	protected final BigInteger start;
 	protected final BigInteger end;
 	protected final BigInteger addressable;
-	protected final ReadMemory read;
+	protected final IReadMemory read;
 
 	private final File file;
 
@@ -51,7 +54,7 @@
 
 	@Override
 	public void run(IProgressMonitor monitor) throws CoreException {
-		try (O writer = output(file)) {
+		try (OutputStream output = output(file)) {
 			BigInteger jobs = end.subtract(start).divide(chunkSize());
 			BigInteger factor = BigInteger.ONE;
 			if (jobs.compareTo(BigInteger.valueOf(0x7FFFFFFF)) > 0) {
@@ -59,7 +62,8 @@
 				jobs = jobs.divide(factor);
 			}
 			monitor.beginTask(Messages.FileExport_task_transferring, jobs.intValue());
-			transfer(writer, factor, monitor);
+			transfer(output, factor, monitor);
+			output.flush();
 		} catch (IOException ex) {
 			requestFailed(Messages.FileExport_e_write_file, ex);
 		} catch (DebugException ex) {
@@ -72,13 +76,16 @@
 	}
 
 	/**
-	 * Creates the writer for the given file
+	 * Creates the output stream for the given file
 	 *
 	 * @param file to export to
 	 * @return writer instance
 	 * @throws IOException
 	 */
-	protected abstract O output(File file) throws IOException;
+	protected OutputStream output(File file) throws IOException {
+		file.getParentFile().mkdirs();
+		return new BufferedOutputStream(new FileOutputStream(file));
+	}
 
 	/**
 	 * Determines the data chunk to use for export
@@ -87,7 +94,7 @@
 	 */
 	protected abstract BigInteger chunkSize();
 
-	protected abstract void transfer(O output, BigInteger factor, IProgressMonitor monitor)
+	protected abstract void transfer(OutputStream output, BigInteger factor, IProgressMonitor monitor)
 			throws IOException, DebugException;
 
 	protected String transferring(BigInteger length, BigInteger address) {
diff --git a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/IReadMemory.java b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/IReadMemory.java
new file mode 100644
index 0000000..c08e2b6
--- /dev/null
+++ b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/IReadMemory.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2020 ArSysOp and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Alexander Fedorov (ArSysOp) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.core.memory.transport;
+
+import java.math.BigInteger;
+
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.MemoryByte;
+
+/**
+ * Reads an array of bytes using the given offset
+ *
+ * @since 0.1
+ */
+public interface IReadMemory {
+
+	/**
+	 * Reads an array of bytes from a memory starting from the given offset. If requested to retrieve data beyond the memory
+	 * boundaries, implementations should return memory bytes with the <code>READABLE</code> bit turned off for each byte outside the
+	 * of the accessible range. An exception should not be thrown in this case.
+	 *
+	 * @param offset zero based offset at which to start retrieving bytes in terms of addressable units
+	 * @param units the number of addressable units to retrieve
+	 * @return the obtained data, {@link MemoryByte#isReadable()} needs to be checked
+	 * @throws DebugException if unable to retrieve the specified bytes due to a failure communicating with the target
+	 *
+	 * @see {@link MemoryByte}
+	 */
+	MemoryByte[] from(BigInteger offset, long units) throws DebugException;
+
+}
diff --git a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/ReadMemory.java b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/ReadMemory.java
deleted file mode 100644
index f98cb00..0000000
--- a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/core/memory/transport/ReadMemory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2020 ArSysOp and others.
- *
- * This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License 2.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- *     Alexander Fedorov (ArSysOp) - initial API and implementation
- *******************************************************************************/
-package org.eclipse.cdt.debug.core.memory.transport;
-
-import java.math.BigInteger;
-
-import org.eclipse.debug.core.DebugException;
-
-/**
- * Reads an array of bytes using the given offset
- *
- * @since 0.1
- */
-public interface ReadMemory {
-
-	/**
-	 * Reads an array of bytes from a memory starting from the given offset.
-	 *
-	 * @param offset
-	 * @return the obtained data
-	 * @throws DebugException
-	 */
-	byte[] from(BigInteger offset) throws DebugException;
-
-}
diff --git a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/PlainTextExport.java b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/PlainTextExport.java
index 5ae44d4..b278338 100644
--- a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/PlainTextExport.java
+++ b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/PlainTextExport.java
@@ -15,28 +15,23 @@
 package org.eclipse.cdt.debug.internal.core.memory.transport;
 
 import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.math.BigInteger;
 
 import org.eclipse.cdt.debug.core.memory.transport.ExportRequest;
 import org.eclipse.cdt.debug.core.memory.transport.FileExport;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.MemoryByte;
 
-public final class PlainTextExport extends FileExport<FileWriter> {
+public final class PlainTextExport extends FileExport {
 
 	public PlainTextExport(File output, ExportRequest request) {
 		super(output, request);
 	}
 
 	@Override
-	protected FileWriter output(File file) throws IOException {
-		file.getParentFile().mkdirs();
-		return new FileWriter(file);
-	}
-
-	@Override
 	protected BigInteger chunkSize() {
 		// These variables control how the output will be formatted
 		// The output data is split by chunks of 1 addressable unit size.
@@ -49,7 +44,7 @@
 	}
 
 	@Override
-	protected void transfer(FileWriter output, BigInteger factor, IProgressMonitor monitor)
+	protected void transfer(OutputStream output, BigInteger factor, IProgressMonitor monitor)
 			throws IOException, DebugException {
 		// These variables control how the output will be formatted
 		// The output data is split by chunks of 1 addressable unit size.
@@ -69,17 +64,18 @@
 					buf.append(" "); //$NON-NLS-1$
 				}
 				BigInteger from = transferAddress.add(dataCellSize.multiply(BigInteger.valueOf(i)));
-				byte[] bytes = read.from(from);
+				MemoryByte[] bytes = read.from(from, dataCellSize.longValue());
 				for (int byteIndex = 0; byteIndex < bytes.length; byteIndex++) {
-					String bString = BigInteger.valueOf(0xFF & bytes[byteIndex]).toString(16);
+					//FIXME: check MemoryByte#isReadable
+					String bString = BigInteger.valueOf(0xFF & bytes[byteIndex].getValue()).toString(16);
 					if (bString.length() == 1) {
 						buf.append("0"); //$NON-NLS-1$
 					}
 					buf.append(bString);
 				}
 			}
-			output.write(buf.toString().toUpperCase());
-			output.write("\n"); //$NON-NLS-1$
+			output.write(buf.toString().toUpperCase().getBytes());
+			output.write("\n".getBytes()); //$NON-NLS-1$
 			transferAddress = transferAddress.add(length);
 			jobCount = jobCount.add(BigInteger.ONE);
 			if (jobCount.compareTo(factor) == 0) {
diff --git a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/RAWBinaryExport.java b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/RAWBinaryExport.java
index 854c009..4ce08f9 100644
--- a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/RAWBinaryExport.java
+++ b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/RAWBinaryExport.java
@@ -15,33 +15,29 @@
 package org.eclipse.cdt.debug.internal.core.memory.transport;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.math.BigInteger;
 
 import org.eclipse.cdt.debug.core.memory.transport.ExportRequest;
 import org.eclipse.cdt.debug.core.memory.transport.FileExport;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.MemoryByte;
 
-public final class RAWBinaryExport extends FileExport<FileOutputStream> {
+public final class RAWBinaryExport extends FileExport {
 
 	public RAWBinaryExport(File input, ExportRequest request) {
 		super(input, request);
 	}
 
 	@Override
-	protected FileOutputStream output(File file) throws IOException {
-		return new FileOutputStream(file);
-	}
-
-	@Override
 	protected BigInteger chunkSize() {
 		return BigInteger.valueOf(1024);
 	}
 
 	@Override
-	protected void transfer(FileOutputStream output, BigInteger factor, IProgressMonitor monitor)
+	protected void transfer(OutputStream output, BigInteger factor, IProgressMonitor monitor)
 			throws IOException, DebugException {
 		BigInteger transferAddress = start;
 		BigInteger jobCount = BigInteger.ZERO;
@@ -52,8 +48,11 @@
 				length = end.subtract(transferAddress);
 			}
 			monitor.subTask(transferring(length, transferAddress));
-			byte[] byteValues = read.from(transferAddress);
-			output.write(byteValues);
+			MemoryByte[] byteValues = read.from(transferAddress, length.longValue() / addressable.longValue());
+			for (MemoryByte memoryByte : byteValues) {
+				//FIXME: check MemoryByte#isReadable
+				output.write(memoryByte.getValue());
+			}
 			transferAddress = transferAddress.add(length);
 			jobCount = jobCount.add(BigInteger.ONE);
 			if (jobCount.compareTo(factor) == 0) {
diff --git a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/ReadMemoryBlock.java b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/ReadMemoryBlock.java
index 3ce1ec7..12a9685 100644
--- a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/ReadMemoryBlock.java
+++ b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/ReadMemoryBlock.java
@@ -15,7 +15,7 @@
 
 import java.math.BigInteger;
 
-import org.eclipse.cdt.debug.core.memory.transport.ReadMemory;
+import org.eclipse.cdt.debug.core.memory.transport.IReadMemory;
 import org.eclipse.debug.core.DebugException;
 import org.eclipse.debug.core.model.IMemoryBlockExtension;
 import org.eclipse.debug.core.model.MemoryByte;
@@ -24,24 +24,17 @@
  * Reads memory from the given {@link IMemoryBlockExtension}
  *
  */
-public final class ReadMemoryBlock implements ReadMemory {
+public final class ReadMemoryBlock implements IReadMemory {
 
 	private final IMemoryBlockExtension memory;
-	private final long unit;
 
 	public ReadMemoryBlock(IMemoryBlockExtension memory) {
 		this.memory = memory;
-		this.unit = BigInteger.valueOf(1).longValue();
 	}
 
 	@Override
-	public byte[] from(BigInteger offset) throws DebugException {
-		MemoryByte[] received = memory.getBytesFromOffset(offset, unit);
-		byte[] bytes = new byte[received.length];
-		for (int i = 0; i < received.length; i++) {
-			bytes[i] = received[i].getValue();
-		}
-		return bytes;
+	public MemoryByte[] from(BigInteger offset, long units) throws DebugException {
+		return memory.getBytesFromOffset(offset, units);
 	}
 
 }
diff --git a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/SRecordExport.java b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/SRecordExport.java
index b8729b0..6cabd21 100644
--- a/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/SRecordExport.java
+++ b/memory/org.eclipse.cdt.debug.core.memory/src/org/eclipse/cdt/debug/internal/core/memory/transport/SRecordExport.java
@@ -15,45 +15,42 @@
 package org.eclipse.cdt.debug.internal.core.memory.transport;
 
 import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.math.BigInteger;
 
 import org.eclipse.cdt.debug.core.memory.transport.ExportRequest;
 import org.eclipse.cdt.debug.core.memory.transport.FileExport;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.MemoryByte;
 
-public final class SRecordExport extends FileExport<FileWriter> {
+public final class SRecordExport extends FileExport {
 
 	public SRecordExport(File input, ExportRequest request) {
 		super(input, request);
 	}
 
 	@Override
-	protected FileWriter output(File file) throws IOException {
-		return new FileWriter(file);
-	}
-
-	@Override
 	protected BigInteger chunkSize() {
 		// FIXME 4 byte default
 		return BigInteger.valueOf(16);
 	}
 
 	@Override
-	protected void transfer(FileWriter output, BigInteger factor, IProgressMonitor monitor)
+	protected void transfer(OutputStream output, BigInteger factor, IProgressMonitor monitor)
 			throws IOException, DebugException {
 		final BigInteger DATA_PER_RECORD = chunkSize();
+		final BigInteger DATA_PER_TRANSFER = BigInteger.valueOf(4096).multiply(DATA_PER_RECORD);
 		BigInteger jobCount = BigInteger.ZERO;
 		BigInteger transferAddress = start;
 		while (transferAddress.compareTo(end) < 0 && !monitor.isCanceled()) {
-			BigInteger length = DATA_PER_RECORD;
+			BigInteger length = DATA_PER_TRANSFER;
 			if (end.subtract(transferAddress).compareTo(length) < 0) {
 				length = end.subtract(transferAddress);
 			}
 			monitor.subTask(transferring(length, transferAddress));
-			byte[] bytes = read.from(transferAddress);
+			MemoryByte[] bytes = read.from(transferAddress, length.longValue() / addressable.longValue());
 			BigInteger sRecordAddress = transferAddress;
 			BigInteger sRecordEndAddress = transferAddress.add(length);
 			while (sRecordAddress.compareTo(sRecordEndAddress) < 0 && !monitor.isCanceled()) {
@@ -61,7 +58,7 @@
 				if (sRecordEndAddress.subtract(sRecordAddress).compareTo(sRecordDataLength) < 0) {
 					sRecordDataLength = end.subtract(sRecordAddress);
 				}
-				output.write("S3"); // FIXME 4 byte address //$NON-NLS-1$
+				output.write("S3".getBytes()); // FIXME 4 byte address //$NON-NLS-1$
 
 				StringBuilder buf = new StringBuilder();
 				BigInteger sRecordLength = BigInteger.valueOf(4); // address size
@@ -80,7 +77,8 @@
 				final int byteOffset = sRecordAddress.subtract(transferAddress).intValue();
 				final int byteLength = byteOffset + sRecordDataLength.intValue();
 				for (int byteIndex = byteOffset; byteIndex < byteLength; byteIndex++) {
-					String bString = BigInteger.valueOf(0xFF & bytes[byteIndex]).toString(16);
+					//FIXME: check MemoryByte#isReadable
+					String bString = BigInteger.valueOf(0xFF & bytes[byteIndex].getValue()).toString(16);
 					if (bString.length() == 1) {
 						buf.append("0"); //$NON-NLS-1$
 					}
@@ -101,8 +99,8 @@
 					buf.append("0"); //$NON-NLS-1$
 				}
 				buf.append(bString);
-				output.write(buf.toString().toUpperCase());
-				output.write("\n"); //$NON-NLS-1$
+				output.write(buf.toString().toUpperCase().getBytes());
+				output.write("\n".getBytes()); //$NON-NLS-1$
 				sRecordAddress = sRecordAddress.add(sRecordDataLength);
 				jobCount = jobCount.add(BigInteger.ONE);
 				if (jobCount.compareTo(factor) == 0) {
diff --git a/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/PlainTextExporter.java b/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/PlainTextExporter.java
index cbd6188..b3d4c88 100644
--- a/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/PlainTextExporter.java
+++ b/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/PlainTextExporter.java
@@ -18,7 +18,7 @@
 import java.math.BigInteger;
 
 import org.eclipse.cdt.debug.core.memory.transport.ExportRequest;
-import org.eclipse.cdt.debug.core.memory.transport.ReadMemory;
+import org.eclipse.cdt.debug.core.memory.transport.IReadMemory;
 import org.eclipse.cdt.debug.internal.core.memory.transport.PlainTextExport;
 import org.eclipse.cdt.debug.internal.core.memory.transport.ReadMemoryBlock;
 import org.eclipse.cdt.debug.internal.core.memory.transport.TransportJob;
@@ -451,7 +451,7 @@
 
 	@Override
 	public void exportMemory() {
-		ReadMemory read = new ReadMemoryBlock((IMemoryBlockExtension) fMemoryBlock);
+		IReadMemory read = new ReadMemoryBlock((IMemoryBlockExtension) fMemoryBlock);
 		BigInteger addressable = new AddressableSize((IMemoryBlockExtension) fMemoryBlock).get();
 		ExportRequest request = new ExportRequest(fStartAddress, fEndAddress, addressable, read);
 		PlainTextExport memoryExport = new PlainTextExport(fOutputFile, request);
diff --git a/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/RAWBinaryExporter.java b/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/RAWBinaryExporter.java
index ddb9089..2706f28 100644
--- a/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/RAWBinaryExporter.java
+++ b/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/RAWBinaryExporter.java
@@ -18,7 +18,7 @@
 import java.math.BigInteger;
 
 import org.eclipse.cdt.debug.core.memory.transport.ExportRequest;
-import org.eclipse.cdt.debug.core.memory.transport.ReadMemory;
+import org.eclipse.cdt.debug.core.memory.transport.IReadMemory;
 import org.eclipse.cdt.debug.internal.core.memory.transport.RAWBinaryExport;
 import org.eclipse.cdt.debug.internal.core.memory.transport.ReadMemoryBlock;
 import org.eclipse.cdt.debug.internal.core.memory.transport.TransportJob;
@@ -454,7 +454,7 @@
 
 	@Override
 	public void exportMemory() {
-		ReadMemory read = new ReadMemoryBlock((IMemoryBlockExtension) fMemoryBlock);
+		IReadMemory read = new ReadMemoryBlock((IMemoryBlockExtension) fMemoryBlock);
 		BigInteger addressable = new AddressableSize((IMemoryBlockExtension) fMemoryBlock).get();
 		ExportRequest request = new ExportRequest(fStartAddress, fEndAddress, addressable, read);
 		RAWBinaryExport memoryExport = new RAWBinaryExport(fOutputFile, request);
diff --git a/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/SRecordExporter.java b/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/SRecordExporter.java
index 80deee9..e7dafc5 100644
--- a/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/SRecordExporter.java
+++ b/memory/org.eclipse.cdt.debug.ui.memory.transport/src/org/eclipse/cdt/debug/ui/memory/transport/SRecordExporter.java
@@ -19,7 +19,7 @@
 import java.math.BigInteger;
 
 import org.eclipse.cdt.debug.core.memory.transport.ExportRequest;
-import org.eclipse.cdt.debug.core.memory.transport.ReadMemory;
+import org.eclipse.cdt.debug.core.memory.transport.IReadMemory;
 import org.eclipse.cdt.debug.internal.core.memory.transport.ReadMemoryBlock;
 import org.eclipse.cdt.debug.internal.core.memory.transport.SRecordExport;
 import org.eclipse.cdt.debug.internal.core.memory.transport.TransportJob;
@@ -489,7 +489,7 @@
 
 	@Override
 	public void exportMemory() {
-		ReadMemory read = new ReadMemoryBlock((IMemoryBlockExtension) fMemoryBlock);
+		IReadMemory read = new ReadMemoryBlock((IMemoryBlockExtension) fMemoryBlock);
 		BigInteger addressable = new AddressableSize((IMemoryBlockExtension) fMemoryBlock).get();
 		ExportRequest request = new ExportRequest(fStartAddress, fEndAddress, addressable, read);
 		SRecordExport memoryExport = new SRecordExport(fOutputFile, request);