|
|
@@ -1,8 +1,9 @@
|
|
|
package me.ag2s.epublib.zip;
|
|
|
|
|
|
+import static me.ag2s.utils.ThrowableUtils.rethrowAsIOException;
|
|
|
+
|
|
|
import android.content.Context;
|
|
|
import android.net.Uri;
|
|
|
-import android.os.Build;
|
|
|
import android.os.ParcelFileDescriptor;
|
|
|
import android.system.ErrnoException;
|
|
|
import android.system.OsConstants;
|
|
|
@@ -22,12 +23,18 @@ import java.nio.ByteBuffer;
|
|
|
import java.nio.ByteOrder;
|
|
|
import java.nio.channels.FileChannel;
|
|
|
|
|
|
-import me.ag2s.epublib.util.AndroidCloseGuard;
|
|
|
-
|
|
|
+import me.ag2s.utils.AndroidCloseGuard;
|
|
|
|
|
|
+@SuppressWarnings("unused")
|
|
|
public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
private final ParcelFileDescriptor pfd;
|
|
|
private FileInputStream fis;
|
|
|
+ private final Object lock = new Object();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 读取基本类型的buffer
|
|
|
+ */
|
|
|
+ private final byte[] readBuffer = new byte[8];
|
|
|
private long pos = 0;
|
|
|
|
|
|
private final AndroidCloseGuard guard = AndroidCloseGuard.getInstance();
|
|
|
@@ -35,7 +42,6 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
public AndroidRandomReadableFile(@NonNull Context context, @NonNull Uri treeUri) throws FileNotFoundException {
|
|
|
pfd = context.getContentResolver().openFileDescriptor(treeUri, "r");
|
|
|
fis = new FileInputStream(pfd.getFileDescriptor());
|
|
|
- //dis = new DataInputStream(fis);
|
|
|
guard.open("close");
|
|
|
|
|
|
}
|
|
|
@@ -45,18 +51,42 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
return pfd.getFileDescriptor();
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
|
|
|
+ * object associated with this file.
|
|
|
+ *
|
|
|
+ * <p> The {@link java.nio.channels.FileChannel#position()
|
|
|
+ * position} of the returned channel will always be equal to
|
|
|
+ * this object's file-pointer offset as returned by the {@link
|
|
|
+ * #getFilePointer getFilePointer} method. Changing this object's
|
|
|
+ * file-pointer offset, whether explicitly or by reading or writing bytes,
|
|
|
+ * will change the position of the channel, and vice versa. Changing the
|
|
|
+ * file's length via this object will change the length seen via the file
|
|
|
+ * channel, and vice versa.
|
|
|
+ *
|
|
|
+ * @return the file channel associated with this file
|
|
|
+ * @spec JSR-51
|
|
|
+ * @since 1.4
|
|
|
+ */
|
|
|
public final FileChannel getChannel() {
|
|
|
- if (fis == null || pos != getPos()) {
|
|
|
- fis = new FileInputStream(pfd.getFileDescriptor());
|
|
|
+ synchronized (lock) {
|
|
|
+ if (fis == null || pos != getPos()) {
|
|
|
+ fis = new FileInputStream(pfd.getFileDescriptor());
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
return fis.getChannel();
|
|
|
}
|
|
|
|
|
|
public final FileInputStream getFileInputStream() {
|
|
|
- if (fis == null || this.pos != getPos()) {
|
|
|
- this.pos = getPos();
|
|
|
- fis = new FileInputStream(pfd.getFileDescriptor());
|
|
|
+ synchronized (lock) {
|
|
|
+ if (fis == null || this.pos != getPos()) {
|
|
|
+ this.pos = getPos();
|
|
|
+ fis = new FileInputStream(pfd.getFileDescriptor());
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
return fis;
|
|
|
}
|
|
|
|
|
|
@@ -69,8 +99,23 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
* @throws IOException if an I/O error occurs.
|
|
|
*/
|
|
|
public int read() throws IOException {
|
|
|
- byte[] b = new byte[1];
|
|
|
- return (read(b, 0, 1) != -1) ? b[0] & 0xff : -1;
|
|
|
+ return (read(readBuffer, 0, 1) != -1) ? readBuffer[0] & 0xff : -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Reads a sub array as a sequence of bytes.
|
|
|
+ *
|
|
|
+ * @param b the buffer into which the data is read.
|
|
|
+ * @param off the start offset of the data.
|
|
|
+ * @param len the number of bytes to read.
|
|
|
+ * @throws IOException If an I/O error has occurred.
|
|
|
+ */
|
|
|
+ private int readBytes(byte[] b, int off, int len) throws IOException {
|
|
|
+ try {
|
|
|
+ return android.system.Os.read(pfd.getFileDescriptor(), b, off, len);
|
|
|
+ } catch (ErrnoException e) {
|
|
|
+ throw rethrowAsIOException(e);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -86,8 +131,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
* @throws IOException if an I/O error occurs.
|
|
|
*/
|
|
|
public int read(byte[] b) throws IOException {
|
|
|
- return read(b, 0, b.length);
|
|
|
-
|
|
|
+ return readBytes(b, 0, b.length);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -109,11 +153,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
* @throws IOException if an I/O error occurs.
|
|
|
*/
|
|
|
public int read(byte[] b, int off, int len) throws IOException {
|
|
|
- try {
|
|
|
- return android.system.Os.read(pfd.getFileDescriptor(), b, off, len);
|
|
|
- } catch (Exception e) {
|
|
|
- throw new IOException(e);
|
|
|
- }
|
|
|
+ return readBytes(b, off, len);
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -126,8 +166,8 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
public void seek(long pos) throws IOException {
|
|
|
try {
|
|
|
android.system.Os.lseek(pfd.getFileDescriptor(), pos, OsConstants.SEEK_SET);
|
|
|
- } catch (Exception e) {
|
|
|
- throw new IOException(e);
|
|
|
+ } catch (ErrnoException e) {
|
|
|
+ throw rethrowAsIOException(e);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
@@ -135,21 +175,37 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
public long length() throws IOException {
|
|
|
try {
|
|
|
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_END);
|
|
|
- } catch (Exception e) {
|
|
|
- throw new IOException(e);
|
|
|
+ } catch (ErrnoException e) {
|
|
|
+ throw rethrowAsIOException(e);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the current offset in this file.
|
|
|
+ *
|
|
|
+ * @return the offset from the beginning of the file, in bytes,
|
|
|
+ * at which the next read or write occurs.
|
|
|
+ * @throws IOException if an I/O error occurs.
|
|
|
+ */
|
|
|
+ public long getFilePointer() throws IOException {
|
|
|
+ try {
|
|
|
+ return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR);
|
|
|
+ } catch (ErrnoException e) {
|
|
|
+ throw rethrowAsIOException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
public long getPos() {
|
|
|
try {
|
|
|
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR);
|
|
|
- } catch (Exception e) {
|
|
|
+ } catch (ErrnoException e) {
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/**
|
|
|
* Reads some bytes from an input
|
|
|
* stream and stores them into the buffer
|
|
|
@@ -192,13 +248,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
*/
|
|
|
@Override
|
|
|
public void readFully(byte[] b) throws IOException {
|
|
|
- try {
|
|
|
- android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length);
|
|
|
- } catch (ErrnoException e) {
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
+ readFully(b, 0, b.length);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -246,12 +296,13 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
*/
|
|
|
@Override
|
|
|
public void readFully(byte[] b, int off, int len) throws IOException {
|
|
|
- try {
|
|
|
- android.system.Os.read(pfd.getFileDescriptor(), b, off, len);
|
|
|
- //syncInputStream();
|
|
|
- } catch (ErrnoException e) {
|
|
|
- throw new IOException(e);
|
|
|
- }
|
|
|
+ int n = 0;
|
|
|
+ do {
|
|
|
+ int count = this.read(b, off + n, len - n);
|
|
|
+ if (count < 0)
|
|
|
+ throw new EOFException();
|
|
|
+ n += count;
|
|
|
+ } while (n < len);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -277,15 +328,33 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
*/
|
|
|
@Override
|
|
|
public int skipBytes(int n) throws IOException {
|
|
|
- try {
|
|
|
- byte[] b = new byte[n];
|
|
|
- return android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length);
|
|
|
- } catch (Exception e) {
|
|
|
- return -1;
|
|
|
+ long pos;
|
|
|
+ long len;
|
|
|
+ long newpos;
|
|
|
+
|
|
|
+ if (n <= 0) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ pos = getFilePointer();
|
|
|
+ len = length();
|
|
|
+ newpos = pos + n;
|
|
|
+ if (newpos > len) {
|
|
|
+ newpos = len;
|
|
|
}
|
|
|
+ seek(newpos);
|
|
|
+
|
|
|
+ /* return the actual number of bytes skipped */
|
|
|
+ return (int) (newpos - pos);
|
|
|
+// try {
|
|
|
+// byte[] b = new byte[n];
|
|
|
+// return android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length);
|
|
|
+// } catch (Exception e) {
|
|
|
+// return -1;
|
|
|
+// }
|
|
|
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/**
|
|
|
* Reads one input byte and returns
|
|
|
* {@code true} if that byte is nonzero,
|
|
|
@@ -357,7 +426,6 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
return ch;
|
|
|
}
|
|
|
|
|
|
- private final byte[] readBuffer = new byte[8];
|
|
|
|
|
|
/**
|
|
|
* Reads two input bytes and returns
|
|
|
@@ -432,7 +500,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
@Override
|
|
|
public char readChar() throws IOException {
|
|
|
readFully(readBuffer, 0, 2);
|
|
|
- return (char) ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get();
|
|
|
+ return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asCharBuffer().get();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -543,73 +611,60 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
return Double.longBitsToDouble(readLong());
|
|
|
}
|
|
|
|
|
|
- private char[] lineBuffer;
|
|
|
|
|
|
/**
|
|
|
- * See the general contract of the <code>readLine</code>
|
|
|
- * method of <code>DataInput</code>.
|
|
|
- * <p>
|
|
|
- * Bytes
|
|
|
- * for this operation are read from the contained
|
|
|
- * input stream.
|
|
|
+ * Reads the next line of text from this file. This method successively
|
|
|
+ * reads bytes from the file, starting at the current file pointer,
|
|
|
+ * until it reaches a line terminator or the end
|
|
|
+ * of the file. Each byte is converted into a character by taking the
|
|
|
+ * byte's value for the lower eight bits of the character and setting the
|
|
|
+ * high eight bits of the character to zero. This method does not,
|
|
|
+ * therefore, support the full Unicode character set.
|
|
|
*
|
|
|
- * @return the next line of text from this input stream.
|
|
|
+ * <p> A line of text is terminated by a carriage-return character
|
|
|
+ * ({@code '\u005Cr'}), a newline character ({@code '\u005Cn'}), a
|
|
|
+ * carriage-return character immediately followed by a newline character,
|
|
|
+ * or the end of the file. Line-terminating characters are discarded and
|
|
|
+ * are not included as part of the string returned.
|
|
|
+ *
|
|
|
+ * <p> This method blocks until a newline character is read, a carriage
|
|
|
+ * return and the byte following it are read (to see if it is a newline),
|
|
|
+ * the end of the file is reached, or an exception is thrown.
|
|
|
+ *
|
|
|
+ * @return the next line of text from this file, or null if end
|
|
|
+ * of file is encountered before even one byte is read.
|
|
|
* @throws IOException if an I/O error occurs.
|
|
|
- * @see java.io.BufferedReader#readLine()
|
|
|
- * @see java.io.FilterInputStream##in
|
|
|
- * @deprecated This method does not properly convert bytes to characters.
|
|
|
- * As of JDK 1.1, the preferred way to read lines of text is via the
|
|
|
- * <code>BufferedReader.readLine()</code> method. Programs that use the
|
|
|
- * <code>DataInputStream</code> class to read lines can be converted to use
|
|
|
- * the <code>BufferedReader</code> class by replacing code of the form:
|
|
|
- * <blockquote><pre>
|
|
|
- * DataInputStream d = new DataInputStream(in);
|
|
|
- * </pre></blockquote>
|
|
|
- * with:
|
|
|
- * <blockquote><pre>
|
|
|
- * BufferedReader d
|
|
|
- * = new BufferedReader(new InputStreamReader(in));
|
|
|
- * </pre></blockquote>
|
|
|
*/
|
|
|
- @Deprecated
|
|
|
+
|
|
|
@Override
|
|
|
public String readLine() throws IOException {
|
|
|
- char[] buf = lineBuffer;
|
|
|
-
|
|
|
- if (buf == null) {
|
|
|
- buf = lineBuffer = new char[128];
|
|
|
- }
|
|
|
+ StringBuilder input = new StringBuilder();
|
|
|
+ int c = -1;
|
|
|
+ boolean eol = false;
|
|
|
|
|
|
- int room = buf.length;
|
|
|
- int offset = 0;
|
|
|
- int c;
|
|
|
-
|
|
|
- loop:
|
|
|
- while (true) {
|
|
|
- switch (c = this.read()) {
|
|
|
+ while (!eol) {
|
|
|
+ switch (c = read()) {
|
|
|
case -1:
|
|
|
case '\n':
|
|
|
- break loop;
|
|
|
-
|
|
|
+ eol = true;
|
|
|
+ break;
|
|
|
case '\r':
|
|
|
- int c2 = this.read();
|
|
|
- break loop;
|
|
|
-
|
|
|
- default:
|
|
|
- if (--room < 0) {
|
|
|
- buf = new char[offset + 128];
|
|
|
- room = buf.length - offset - 1;
|
|
|
- System.arraycopy(lineBuffer, 0, buf, 0, offset);
|
|
|
- lineBuffer = buf;
|
|
|
+ eol = true;
|
|
|
+ long cur = getFilePointer();
|
|
|
+ if ((read()) != '\n') {
|
|
|
+ seek(cur);
|
|
|
}
|
|
|
- buf[offset++] = (char) c;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ input.append((char) c);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
- if ((c == -1) && (offset == 0)) {
|
|
|
+
|
|
|
+ if ((c == -1) && (input.length() == 0)) {
|
|
|
return null;
|
|
|
}
|
|
|
- return String.copyValueOf(buf, 0, offset);
|
|
|
+ return input.toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -641,9 +696,6 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
|
|
|
guard.close();
|
|
|
|
|
|
- if (Build.VERSION.SDK_INT >= 28) {
|
|
|
- //Reference.reachabilityFence(this);
|
|
|
- }
|
|
|
try {
|
|
|
if (fis != null) {
|
|
|
fis.close();
|
|
|
@@ -669,6 +721,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|
|
|
|
|
guard.warnIfOpen();
|
|
|
|
|
|
+
|
|
|
close();
|
|
|
|
|
|
} finally {
|