Files
EhViewer/app/src/main/java/com/hippo/unifile/FileDescriptorRandomAccessFile.java
T
2022-07-18 20:47:04 +08:00

228 lines
6.2 KiB
Java

/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.unifile;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
class FileDescriptorRandomAccessFile implements UniRandomAccessFile {
public static final int FLUSH_NONE = 0;
public static final int FLUSH_FSYNC = 1;
public static final int FLUSH_FDATASYNC = 2;
private final ParcelFileDescriptor pfd;
private final FileDescriptor fd;
private final int flushAfterWrite;
private final Object closeLock = new Object();
private volatile boolean closed = false;
private FileDescriptorRandomAccessFile(ParcelFileDescriptor pfd, FileDescriptor fd, String mode) {
this.pfd = pfd;
this.fd = fd;
switch (mode) {
case "rws":
flushAfterWrite = FLUSH_FSYNC;
break;
case "rwd":
flushAfterWrite = FLUSH_FDATASYNC;
break;
default:
flushAfterWrite = FLUSH_NONE;
break;
}
}
@NonNull
static FileDescriptorRandomAccessFile create(Context context, Uri uri, String mode) throws IOException {
// Get FileDescriptor
ParcelFileDescriptor pfd;
try {
pfd = context.getContentResolver().openFileDescriptor(uri, mode);
} catch (Throwable e) {
Utils.throwIfFatal(e);
throw new IOException("Can't get ParcelFileDescriptor", e);
}
if (pfd == null) {
throw new IOException("Can't get ParcelFileDescriptor");
}
FileDescriptor fd;
try {
fd = pfd.getFileDescriptor();
} catch (SecurityException e) {
pfd.close();
throw new IOException("Can't get FileDescriptor", e);
}
if (fd == null) {
pfd.close();
throw new IOException("Can't get FileDescriptor");
}
return new FileDescriptorRandomAccessFile(pfd, fd, mode);
}
private void maybeSync() {
if (flushAfterWrite == FLUSH_FSYNC) {
try {
fd.sync();
} catch (IOException e) {
// Ignored
}
} else if (flushAfterWrite == FLUSH_FDATASYNC) {
try {
Os.fdatasync(fd);
} catch (ErrnoException e) {
// Ignored
}
}
}
@Override
public void close() throws IOException {
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
pfd.close();
try {
Os.close(fd);
} catch (ErrnoException e) {
throw new IOException("An ErrnoException occurs", e);
}
}
@Override
public long getFilePointer() throws IOException {
try {
return Os.lseek(fd, 0L, OsConstants.SEEK_CUR);
} catch (ErrnoException e) {
throw new IOException("An ErrnoException occurs", e);
}
}
@Override
public void seek(long pos) throws IOException {
if (pos < 0) {
throw new IOException("offset < 0: " + pos);
} else {
try {
Os.lseek(fd, pos, OsConstants.SEEK_SET);
} catch (ErrnoException e) {
throw new IOException("An ErrnoException occurs", e);
}
}
}
@Override
public int skipBytes(int n) throws IOException {
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);
}
@Override
public long length() throws IOException {
try {
return Os.fstat(fd).st_size;
} catch (ErrnoException e) {
throw new IOException("An ErrnoException occurs", e);
}
}
@Override
public void setLength(long newLength) throws IOException {
if (newLength < 0) {
throw new IllegalArgumentException("newLength < 0");
}
try {
Os.ftruncate(fd, newLength);
} catch (ErrnoException e) {
throw new IOException("An ErrnoException occurs", e);
}
long filePointer = getFilePointer();
if (filePointer > newLength) {
seek(newLength);
}
maybeSync();
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
try {
return Os.read(fd, b, off, len);
} catch (ErrnoException e) {
throw new IOException("An ErrnoException occurs", e);
}
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
try {
Os.write(fd, b, off, len);
} catch (ErrnoException e) {
throw new IOException("An ErrnoException occurs", e);
}
maybeSync();
}
@IntDef({FLUSH_NONE, FLUSH_FSYNC, FLUSH_FDATASYNC})
@Retention(RetentionPolicy.SOURCE)
public @interface FlushMode {
}
}