/*
Реализация среды исполнения языка программирования
Объектно-ориентированный продвинутый векторный транслятор
Copyright © 2021, 2024, 2026 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package avt.io;
import avt.io.charset.*;
import avt.io.extension.*;
public final class CharInputStream(Object, Closeable, CharReader, Extendable)
{
public static CharInputStream create(ByteReader reader, String charsetName) throws UnsupportedCharsetNameException {
CharDecoder decoder = Charset.get(charsetName).newDecoder();
return new CharInputStream(reader, decoder);
}
private CharDecoder fldDecoder;
private final ByteReader fldReader;
private final CharInputStreamMarkExtension fldMarkExtension;
private final CharInputStreamStreamedExtension fldStreamedExtension;
public (ByteReader reader): this(reader, Charset.getDefault().newDecoder()) { }
public (ByteReader reader, CharDecoder decoder) {
LimitedSizeExtension ext0 = null;
MarkExtension ext1 = null;
if(reader != null)
{
ext0 = (LimitedSizeExtension) reader.getExtension(LimitedSizeExtension.class);
ext1 = (MarkExtension) reader.getExtension(MarkExtension.class);
}
fldDecoder = decoder != null ? decoder : Charset.getDefault().newDecoder();
fldReader = reader;
fldMarkExtension = ext1 == null ? null : new CharInputStreamMarkExtension(ext1);
fldStreamedExtension = new CharInputStreamStreamedExtension(ext0);
}
public void close() throws IOException { fldReader.close(); }
public int read() throws IOException {
CharBuffer stream = readChars(1);
return stream.length < 1 ? -1 : stream[0];
}
public int read(char[] dst, int offset, int length) throws IOException {
if(dst == null)
{
throw new NullPointerException(String.format(avt.lang.package.getResourceString("null-pointer.argument"), new Object[] { "dst" }));
}
Array.checkBounds(dst, offset, length);
if(length <= 0) return 0;
CharBuffer stream = readChars(length);
int result = Int.min(stream.length, length);
if(result <= 0) return -1;
stream.getChars(0, result, dst, offset);
return result;
}
public long skip(long charsQuantity) throws IOException {
final int BUFFER_CAPACITY = 224;
if(charsQuantity <= 0) return 0;
long result = 0;
for(char[] buffer = new char[BUFFER_CAPACITY]; charsQuantity > 0; )
{
long skipped = (long) read(buffer, 0, (int) Long.min(charsQuantity, BUFFER_CAPACITY));
if(skipped < 0) break;
charsQuantity -= skipped;
result += skipped;
}
return result;
}
public Extension[] getExtensions() {
Extension ext1 = fldMarkExtension;
Extension ext0 = fldStreamedExtension;
return ext1 == null ? new Extension[] { ext0 } : new Extension[] { ext0, ext1 };
}
public Extension getExtension(Class type) {
Extension ext = fldStreamedExtension;
if(type == null || type.isAssignableFrom(ext.getClass())) return ext;
return (ext = fldMarkExtension) != null && type.isAssignableFrom(ext.getClass()) ? ext : null;
}
public ByteReader reader { read = fldReader }
public CharDecoder decoder { read = fldDecoder, write = setDecoder }
private void setDecoder(CharDecoder newDecoder) { fldDecoder = newDecoder != null ? newDecoder : Charset.getDefault().newDecoder(); }
private CharBuffer readChars(int length) throws CharDecodingException, IOException {
ByteReader src = fldReader;
CharDecoder decoder = fldDecoder;
CharInputStreamStreamedExtension ext = fldStreamedExtension;
CharBuffer dst = ext.fldCharsBuffer;
dst.discard(ext.fldCharsReaded);
ext.fldCharsReaded = Int.MAX_VALUE;
for(int readed = -1; readed != (readed = dst.length) && readed < length; )
{
int remained = length - readed;
decoder.decodeToWriter(dst, src, remained >= Int.MAX_VALUE - 6 ? Int.MAX_VALUE : remained + 6);
}
ext.fldCharsReaded = length;
return dst;
}
}
class CharInputStreamMarkExtension(Object, Extension, MarkExtension)
{
private final MarkExtension fldMarkExtension;
public (MarkExtension ext) { fldMarkExtension = ext; }
public void mark() throws IOException { fldMarkExtension.mark(); }
public void reset() throws IOException { fldMarkExtension.reset(); }
}
class CharInputStreamStreamedExtension(Object, Extension, StreamedExtension)
{
int fldCharsReaded;
final CharBuffer fldCharsBuffer;
private final LimitedSizeExtension fldLimitedSizeExtension;
public (LimitedSizeExtension ext) {
fldCharsBuffer = new CharBuffer();
fldLimitedSizeExtension = ext;
}
public boolean ready() throws IOException {
LimitedSizeExtension ext;
return fldCharsBuffer.length > fldCharsReaded || (ext = fldLimitedSizeExtension) != null && ext.available() > 0;
}
}