/*
Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
и других спецификаций для функционирования компактных приложений на языке
Java (мидлетов) в среде программного обеспечения Малик Эмулятор.
Copyright © 2016–2017, 2019–2023 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package malik.emulator.io.cloud;
import java.io.*;
import malik.emulator.io.vfs.*;
import malik.emulator.util.*;
public abstract class HandleInputStream extends InputStream
{
private static final int NO_ERROR = 0;
private static final int FILE_NOT_FOUND = 1;
private static final int FILE_NAME_NOT_SPECIFIED = 3;
private static final int BUFFER_SIZE = 1 << 9; /* должен быть целой степенью двойки */
private static final long BUFFER_POSITION_MASK = (long) (BUFFER_SIZE - 1);
private final boolean shared;
private final int openError;
private final int address;
int handle;
private long markedPosition;
private long currentPosition;
private final byte[] markedBuffer;
private final byte[] currentBuffer;
private final String fileName;
final DataDescriptor descriptor;
HandleInputStream(String fileName, int access) {
int h;
int e;
int len;
byte[] buffer;
if(fileName == null || (len = fileName.length()) <= 0)
{
h = 0;
e = FILE_NAME_NOT_SPECIFIED;
} else
{
char[] name;
fileName.getChars(0, len, name = new char[len + 1], 0);
if((h = (int) MalikSystem.syscall(Array.getFirstElementAddress(name), access, 0x0010)) == 0)
{
e = FILE_NOT_FOUND;
} else
{
e = NO_ERROR;
}
}
if(h == 0)
{
this.shared = false;
this.openError = e;
this.address = 0;
this.markedBuffer = null;
this.currentBuffer = null;
this.descriptor = null;
this.fileName = fileName;
return;
}
buffer = new byte[BUFFER_SIZE];
this.shared = false;
this.openError = NO_ERROR;
this.address = Array.getFirstElementAddress(buffer);
this.handle = h;
this.markedPosition = -1L;
this.currentPosition = -1L;
this.markedBuffer = new byte[BUFFER_SIZE];
this.currentBuffer = buffer;
this.fileName = fileName;
this.descriptor = new DataDescriptor();
}
HandleInputStream(String fileName, DataDescriptor descriptor, int handle) {
boolean shared;
byte[] buffer;
if(!(shared = descriptor != null)) descriptor = new DataDescriptor();
buffer = new byte[BUFFER_SIZE];
this.shared = shared;
this.openError = NO_ERROR;
this.address = Array.getFirstElementAddress(buffer);
this.handle = handle;
this.markedPosition = -1L;
this.currentPosition = -1L;
this.markedBuffer = new byte[BUFFER_SIZE];
this.currentBuffer = buffer;
this.fileName = fileName;
this.descriptor = descriptor;
}
public abstract long getFileSize();
public void reset() throws IOException {
int error;
checkOpenError();
error = 0;
synchronized(descriptor)
{
label0:
{
if(handle == 0)
{
error = 1;
break label0;
}
currentPosition = markedPosition;
Array.copy(markedBuffer, 0, currentBuffer, 0, BUFFER_SIZE);
}
}
if(error == 1)
{
throw new ClosedFileException("HandleInputStream.reset: файловый поток ввода закрыт.");
}
}
public void mark(int readLimit) {
if(hasOpenError()) return;
synchronized(descriptor)
{
if(handle != 0)
{
markedPosition = currentPosition;
Array.copy(currentBuffer, 0, markedBuffer, 0, BUFFER_SIZE);
}
}
}
public boolean markSupported() {
return true;
}
public int available() throws IOException {
int error;
long result;
checkOpenError();
error = 0;
synchronized(descriptor)
{
label0:
{
long filePosition;
if(handle == 0)
{
error = 1;
result = 0L;
break label0;
}
filePosition = currentPosition;
result = getFileSize() - (filePosition >= 0L ? filePosition : 0L);
}
}
if(error == 1)
{
throw new ClosedFileException("HandleInputStream.available: файловый поток ввода закрыт.");
}
return (long) Integer.MAX_VALUE < result ? Integer.MAX_VALUE : (int) result;
}
public int read() throws IOException {
int error;
int result;
checkOpenError();
error = 0;
synchronized(descriptor)
{
label0:
{
long filePosition;
if(handle == 0)
{
error = 1;
result = 0;
break label0;
}
if((filePosition = currentPosition) < 0L && !readFile(filePosition = 0L))
{
error = 2;
result = 0;
break label0;
}
if(filePosition >= getFileSize())
{
result = -1;
currentPosition = filePosition;
break label0;
}
result = currentBuffer[(int) (filePosition++ & BUFFER_POSITION_MASK)] & 0xff;
currentPosition = filePosition;
if((filePosition & BUFFER_POSITION_MASK) == 0L && !readFile(filePosition))
{
error = 2;
result = 0;
break label0;
}
}
}
switch(error)
{
case 1:
throw new ClosedFileException("HandleInputStream.read: файловый поток ввода закрыт.");
case 2:
throw new IOException("HandleInputStream.read: ошибка произошла при чтении файла.");
}
return result;
}
public int read(byte[] dst, int offset, int length) throws IOException {
int error;
int result;
if(dst == null)
{
throw new NullPointerException("HandleInputStream.read: аргумент dst равен нулевой ссылке.");
}
Array.checkBound("HandleInputStream.read", dst.length, offset, length);
checkOpenError();
error = 0;
synchronized(descriptor)
{
label0:
{
long fileSize;
long filePosition;
long fileRemainder;
byte[] bufferContent;
if(handle == 0)
{
error = 1;
result = 0;
break label0;
}
if((filePosition = currentPosition) < 0L && !readFile(filePosition = 0L))
{
error = 2;
result = 0;
break label0;
}
if(length <= 0)
{
result = 0;
break label0;
}
if(filePosition >= (fileSize = getFileSize()))
{
result = -1;
currentPosition = filePosition;
break label0;
}
bufferContent = currentBuffer;
fileRemainder = fileSize - filePosition;
result = length = (long) length <= fileRemainder ? length : (int) fileRemainder;
currentPosition = filePosition + result;
for(int readed; length > 0; offset += readed, length -= readed)
{
int bufferPosition;
int bufferRemainder = BUFFER_SIZE - (bufferPosition = (int) (filePosition & BUFFER_POSITION_MASK));
long readedAsLong = (long) (readed = bufferRemainder <= length ? bufferRemainder : length);
Array.copy(bufferContent, bufferPosition, dst, offset, readed);
if(((filePosition += readedAsLong) & BUFFER_POSITION_MASK) == 0L && !readFile(filePosition))
{
error = 2;
currentPosition = filePosition;
break label0;
}
}
}
}
switch(error)
{
case 1:
throw new ClosedFileException("HandleInputStream.read: файловый поток ввода закрыт.");
case 2:
throw new IOException("HandleInputStream.read: ошибка произошла при чтении файла.");
}
return result;
}
public long skip(long quantity) throws IOException {
int error;
long result;
if(quantity < 0L) quantity = 0L;
checkOpenError();
error = 0;
synchronized(descriptor)
{
label0:
{
long fileSize;
long filePosition;
long fileRemainder;
long newPosition;
if(handle == 0)
{
error = 1;
result = 0L;
break label0;
}
if((filePosition = currentPosition) < 0L && !readFile(filePosition = 0L))
{
error = 2;
result = 0L;
break label0;
}
if(filePosition >= (fileSize = getFileSize()))
{
result = 0L;
currentPosition = filePosition;
break label0;
}
fileRemainder = fileSize - filePosition;
result = quantity <= fileRemainder ? quantity : fileRemainder;
currentPosition = newPosition = filePosition + result;
if((filePosition & ~BUFFER_POSITION_MASK) != (newPosition &= ~BUFFER_POSITION_MASK) && !readFile(newPosition))
{
error = 2;
break label0;
}
}
}
switch(error)
{
case 1:
throw new ClosedFileException("HandleInputStream.skip: файловый поток ввода закрыт.");
case 2:
throw new IOException("HandleInputStream.skip: ошибка произошла при чтении файла.");
}
return result;
}
public String toString() {
String name = fileName;
return (handle != 0 ? "Открытый для чтения файл " : "Закрытый файл ").concat(name != null && name.length() > 0 ? name : "<имя не задано>");
}
public void checkOpenError() throws IOException {
String name = fileName;
switch(openError)
{
case FILE_NOT_FOUND:
throw new FileNotFoundException((new StringBuilder()).append("HandleInputStream.checkOpenError: файл ").append(name).append(" не найден или занят.").toString(), name);
case FILE_NAME_NOT_SPECIFIED:
throw new IOException("HandleInputStream.checkOpenError: имя файла не было задано при создании этого экземпляра HandleInputStream.");
}
}
public boolean hasOpenError() {
int e;
return (e = openError) > NO_ERROR && e <= FILE_NAME_NOT_SPECIFIED;
}
public final String getFileName() {
return fileName;
}
final void resetPosition() {
currentPosition = -1L;
}
private long seek(long delta) {
return MalikSystem.syscall(handle, MalikSystem.getLocalVariableAddress(delta), 0x0014);
}
private boolean readFile(long position) {
boolean result;
int mustRead;
long oldPosition = seek(0L);
long fileRemainder = getFileSize() - position;
DataDescriptor descriptor = this.descriptor;
mustRead = (long) BUFFER_SIZE <= fileRemainder ? BUFFER_SIZE : (int) fileRemainder;
seek(position - oldPosition);
descriptor.setDataInfo(address, BUFFER_SIZE);
result = (int) MalikSystem.syscall(handle, descriptor.getDescriptorAddress(), 0x0012) == mustRead;
if(shared) seek(oldPosition - seek(0L));
return result;
}
}