DroidFish: Fixed out of memory error when receiving large PGN files.

If receiving a large PGN file as a content stream from another app,
store the data in a temporary file instead of reading it all into
memory.

This also makes content containing more than one game work correctly by
letting the user select which game to open.
This commit is contained in:
Peter Osterlund 2016-12-27 14:12:45 +01:00
parent 5c6b1408f4
commit f211786b74
4 changed files with 75 additions and 12 deletions

View File

@ -21,11 +21,13 @@ package org.petero.droidfish;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -45,6 +47,9 @@ import org.petero.droidfish.activities.EditPGNLoad;
import org.petero.droidfish.activities.EditPGNSave;
import org.petero.droidfish.activities.LoadFEN;
import org.petero.droidfish.activities.LoadScid;
import org.petero.droidfish.activities.PGNFile;
import org.petero.droidfish.activities.PGNFile.GameInfo;
import org.petero.droidfish.activities.PGNFile.GameInfoResult;
import org.petero.droidfish.activities.Preferences;
import org.petero.droidfish.book.BookOptions;
import org.petero.droidfish.engine.EngineUtil;
@ -163,7 +168,6 @@ public class DroidFish extends Activity
// FIXME!!! Implement bookmark mechanism for positions in pgn files
// FIXME!!! Add support for "Chess Leipzig" font
// FIXME!!! Computer clock should stop if phone turned off (computer stops thinking if unplugged)
// FIXME!!! Add support for "no time control" and "hour-glass time control" as defined by the PGN standard
// FIXME!!! Add chess960 support
@ -174,7 +178,6 @@ public class DroidFish extends Activity
// FIXME!!! Option to display coordinates in border outside chess board.
// FIXME!!! Handle PGN non-file intents with more than one game.
// FIXME!!! Save position to fen/epd file
// FIXME!!! Selection dialog for going into variation
@ -719,19 +722,31 @@ public class DroidFish extends Activity
filename = Uri.decode(filename);
}
if ((filename == null) &&
("content".equals(scheme) ||
"file".equals(scheme))) {
("content".equals(scheme) || "file".equals(scheme))) {
ContentResolver resolver = getContentResolver();
InputStream in = resolver.openInputStream(data);
StringBuilder sb = new StringBuilder();
while (true) {
byte[] buffer = new byte[16384];
int len = in.read(buffer);
if (len <= 0)
break;
sb.append(new String(buffer, 0, len));
String sep = File.separator;
String fn = Environment.getExternalStorageDirectory() + sep +
pgnDir + sep + ".sharedfile.pgn";
try {
FileUtil.writeFile(in, fn);
} finally {
in.close();
}
PGNFile pgnFile = new PGNFile(fn);
long fileLen = FileUtil.getFileLength(fn);
Pair<GameInfoResult,ArrayList<GameInfo>> gi = pgnFile.getGameInfo(this, null, 2);
int nGames = gi.second.size();
if ((fileLen > 1024 * 1024) || (gi.first == GameInfoResult.OK && nGames > 1)) {
filename = fn;
} else {
in = new FileInputStream(fn);
try {
pgnOrFen = FileUtil.readFromStream(in);
} finally {
in.close();
}
}
pgnOrFen = sb.toString();
}
}
} catch (IOException e) {

View File

@ -20,9 +20,12 @@ package org.petero.droidfish;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
@ -60,4 +63,34 @@ public class FileUtil {
return null;
}
}
/** Read data from input stream and write to file. */
public static void writeFile(InputStream is, String outFile) throws IOException {
OutputStream os = new FileOutputStream(outFile);
try {
byte[] buffer = new byte[16384];
while (true) {
int len = is.read(buffer);
if (len <= 0)
break;
os.write(buffer, 0, len);
}
} finally {
os.close();
}
}
/** Return the length of a file, or -1 if length can not be determined. */
public static final long getFileLength(String filename) {
try {
RandomAccessFile raf = new RandomAccessFile(filename, "r");
try {
return raf.length();
} finally {
raf.close();
}
} catch (IOException ex) {
return -1;
}
}
}

View File

@ -161,6 +161,13 @@ public class PGNFile {
/** Return info about all PGN games in a file. */
public final Pair<GameInfoResult,ArrayList<GameInfo>> getGameInfo(Activity activity,
final ProgressDialog progress) {
return getGameInfo(activity, progress, -1);
}
/** Return info about all PGN games in a file. */
public final Pair<GameInfoResult,ArrayList<GameInfo>> getGameInfo(Activity activity,
final ProgressDialog progress,
int maxGames) {
ArrayList<GameInfo> gamesInFile = new ArrayList<GameInfo>();
gamesInFile.clear();
long fileLen = 0;
@ -326,6 +333,10 @@ public class PGNFile {
gi.endPos = filePos;
gi.info = hi.toString();
gamesInFile.add(gi);
if ((maxGames > 0) && gamesInFile.size() >= maxGames) {
gi = null;
break;
}
final int newPercent = (int)(filePos * 100 / fileLen);
if (newPercent > percent) {
percent = newPercent;

View File

@ -135,6 +135,10 @@ public class PGNFileTest extends TestCase {
assertEquals(80, gi.get(1).startPos);
assertEquals(137, gi.get(1).endPos);
assertEquals("2. w - b 1-0", gi.get(1).info);
res = pgnFile.getGameInfo(null, null, 1);
assertEquals(GameInfoResult.OK, res.first);
assertEquals(1, res.second.size());
}
}