Cleaned up input console to use my own growing-byte buffers. Also made one that is fast (but unsynchronized)

This commit is contained in:
nathan 2014-10-17 00:10:49 +02:00
parent 24efc75f7b
commit 890d73e8aa
4 changed files with 549 additions and 172 deletions

View File

@ -34,23 +34,23 @@ public class ByteBuffer2 {
public ByteBuffer2(byte[] bytes) {
this.bytes = bytes;
clear();
position = bytes.length;
this.position = bytes.length;
}
public byte getByte() {
if (position > limit) {
if (this.position > this.limit) {
throw new BufferUnderflowException();
}
return bytes[position++];
return this.bytes[this.position++];
}
public byte getByte(int i) {
if (i > limit) {
if (i > this.limit) {
throw new BufferUnderflowException();
}
return bytes[i];
return this.bytes[i];
}
public void getBytes(byte[] buffer) {
@ -62,12 +62,12 @@ public class ByteBuffer2 {
}
public void getBytes(byte[] buffer, int offset, int length) {
if (position + length - offset > limit) {
if (this.position + length - offset > this.limit) {
throw new BufferUnderflowException();
}
System.arraycopy(bytes, position, buffer, 0, length-offset);
position += length-offset;
System.arraycopy(this.bytes, this.position, buffer, 0, length-offset);
this.position += length-offset;
}
/**
@ -76,18 +76,18 @@ public class ByteBuffer2 {
* NOT PROTECTED
*/
private final void _put(byte b) {
bytes[position++] = b;
this.bytes[this.position++] = b;
}
/** NOT PROTECTED! */
private final void checkBuffer(int threshold) {
if (bytes.length < threshold) {
if (this.bytes.length < threshold) {
byte[] t = new byte[threshold];
// grow at back of array
System.arraycopy(bytes, 0, t, 0, bytes.length);
limit = t.length;
System.arraycopy(this.bytes, 0, t, 0, this.bytes.length);
this.limit = t.length;
bytes = t;
this.bytes = t;
}
}
@ -101,16 +101,16 @@ public class ByteBuffer2 {
}
public final synchronized ByteBuffer2 putBytes(byte[] src, int offset, int length) {
checkBuffer(position + length - offset);
checkBuffer(this.position + length - offset);
System.arraycopy(src, offset, bytes, position, length);
position += length;
System.arraycopy(src, offset, this.bytes, this.position, length);
this.position += length;
return this;
}
public final synchronized ByteBuffer2 putByte(byte b) {
checkBuffer(position + 1);
checkBuffer(this.position + 1);
_put(b);
return this;
@ -122,7 +122,7 @@ public class ByteBuffer2 {
}
public final synchronized void putChar(char c) {
checkBuffer(position + 2);
checkBuffer(this.position + 2);
putBytes(BigEndian.Char_.toBytes(c));
}
@ -131,8 +131,19 @@ public class ByteBuffer2 {
return BigEndian.Char_.fromBytes(getByte(), getByte());
}
public final char getChar(int i) {
return BigEndian.Char_.fromBytes(getByte(i++), getByte(i));
}
public void getChars(int srcStart, int srcLength, char[] dest, int destStart) {
for (int i=srcStart;i<srcLength;i+=2) {
char c = BigEndian.Char_.fromBytes(getByte(), getByte());
dest[destStart++] = c;
}
}
public final synchronized ByteBuffer2 putShort(short x) {
checkBuffer(position + 2);
checkBuffer(this.position + 2);
putBytes(BigEndian.Short_.toBytes(x));
@ -143,21 +154,26 @@ public class ByteBuffer2 {
return BigEndian.Short_.fromBytes(getByte(), getByte());
}
public final short getShort(int i) {
return BigEndian.Short_.fromBytes(getByte(i++), getByte(i));
}
public final synchronized void putInt(int x) {
checkBuffer(position + 4);
checkBuffer(this.position + 4);
putBytes(BigEndian.Int_.toBytes(x));
}
public final synchronized int getInt() {
byte b3 = getByte();
byte b2 = getByte();
byte b1 = getByte();
return BigEndian.Int_.fromBytes(getByte(), b1, b2, b3);
return BigEndian.Int_.fromBytes(getByte(), getByte(), getByte(), getByte());
}
public final int getInt(int i) {
return BigEndian.Int_.fromBytes(getByte(i++), getByte(i++), getByte(i++), getByte(i++));
}
public final synchronized void putLong(long x) {
checkBuffer(position + 8);
checkBuffer(this.position + 8);
putBytes(BigEndian.Long_.toBytes(x));
}
@ -166,21 +182,25 @@ public class ByteBuffer2 {
return BigEndian.Long_.fromBytes(getByte(), getByte(), getByte(), getByte(), getByte(), getByte(), getByte(), getByte());
}
public final long getLong(int i) {
return BigEndian.Long_.fromBytes(getByte(i++), getByte(i++), getByte(i++), getByte(i++), getByte(i++), getByte(i++), getByte(i++), getByte(i++));
}
/**
* Returns the backing array of this buffer
*/
public byte[] array() {
return bytes;
return this.bytes;
}
/**
* Returns a copy of the backing array of this buffer
*/
public final synchronized byte[] arrayCopy() {
int length = bytes.length - position;
int length = this.bytes.length - this.position;
byte[] b = new byte[length];
System.arraycopy(bytes, position, b, 0, length);
System.arraycopy(this.bytes, this.position, b, 0, length);
return b;
}
@ -188,20 +208,20 @@ public class ByteBuffer2 {
* Returns this buffer's position.
*/
public int position() {
return position;
return this.position;
}
/**
* Sets this buffer's position.
*/
public final synchronized ByteBuffer2 position(int position) {
if (position > bytes.length || position < 0) {
if (position > this.bytes.length || position < 0) {
throw new IllegalArgumentException();
}
this.position = position;
if (mark > position) {
mark = -1;
if (this.mark > position) {
this.mark = -1;
}
return this;
@ -212,7 +232,7 @@ public class ByteBuffer2 {
* limit.
*/
public final synchronized int remaining() {
return limit - position;
return this.limit - this.position;
}
/**
@ -220,7 +240,7 @@ public class ByteBuffer2 {
* the limit.
*/
public final synchronized boolean hasRemaining() {
return position < limit;
return this.position < this.limit;
}
/**
@ -228,11 +248,11 @@ public class ByteBuffer2 {
*/
public final synchronized void limit(int limit) {
this.limit = limit;
if (position > limit) {
position = limit;
if (this.position > limit) {
this.position = limit;
}
if (mark > limit) {
mark = -1;
if (this.mark > limit) {
this.mark = -1;
}
}
@ -240,14 +260,14 @@ public class ByteBuffer2 {
* Returns this buffer's limit.
*/
public int limit() {
return limit;
return this.limit;
}
/**
* Returns this buffer's capacity.
*/
public int capacity() {
return bytes.length;
return this.bytes.length;
}
/**
@ -260,8 +280,8 @@ public class ByteBuffer2 {
* can be followed immediately by an invocation of another relative put method.
*/
public final synchronized void compact() {
mark = -1;
System.arraycopy(bytes, position, bytes, 0, remaining());
this.mark = -1;
System.arraycopy(this.bytes, this.position, this.bytes, 0, remaining());
position(remaining());
limit(capacity());
@ -275,9 +295,9 @@ public class ByteBuffer2 {
* discarded.
*/
public final synchronized void flip() {
limit = position;
position = 0;
mark = -1;
this.limit = this.position;
this.position = 0;
this.mark = -1;
}
/**
@ -285,9 +305,9 @@ public class ByteBuffer2 {
* the capacity, and the mark is discarded.
*/
public final synchronized void clear() {
position = 0;
limit = capacity();
mark = -1;
this.position = 0;
this.limit = capacity();
this.mark = -1;
}
/**
@ -295,15 +315,15 @@ public class ByteBuffer2 {
* discarded.
*/
public final synchronized void rewind() {
position = 0;
mark = -1;
this.position = 0;
this.mark = -1;
}
/**
* Sets this buffer's mark at its position.
*/
public final synchronized void mark() {
mark = position;
this.mark = this.position;
}
/**
@ -313,6 +333,6 @@ public class ByteBuffer2 {
* value. </p>
*/
public void reset() {
position = mark;
this.position = this.mark;
}
}

View File

@ -0,0 +1,338 @@
package dorkbox.util.bytes;
import java.nio.BufferUnderflowException;
/**
* Cleanroom implementation of a self-growing bytebuffer. NOT SYNCHRONIZED!
*/
public class ByteBuffer2Fast {
private byte[] bytes;
private int position = 0;
private int mark = -1;
private int limit = 0;
public static ByteBuffer2Fast wrap(byte[] buffer) {
return new ByteBuffer2Fast(buffer);
}
public static ByteBuffer2Fast allocate(int capacity) {
ByteBuffer2Fast byteBuffer2 = new ByteBuffer2Fast(new byte[capacity]);
byteBuffer2.clear();
return byteBuffer2;
}
public ByteBuffer2Fast() {
this(0);
}
public ByteBuffer2Fast(int size) {
this(new byte[size]);
}
public ByteBuffer2Fast(byte[] bytes) {
this.bytes = bytes;
clear();
this.position = bytes.length;
}
public byte getByte() {
if (this.position > this.limit) {
throw new BufferUnderflowException();
}
return this.bytes[this.position++];
}
public byte getByte(int i) {
if (i > this.limit) {
throw new BufferUnderflowException();
}
return this.bytes[i];
}
public void getBytes(byte[] buffer) {
getBytes(buffer, 0, buffer.length);
}
public void getBytes(byte[] buffer, int length) {
getBytes(buffer, 0, length);
}
public void getBytes(byte[] buffer, int offset, int length) {
if (this.position + length - offset > this.limit) {
throw new BufferUnderflowException();
}
System.arraycopy(this.bytes, this.position, buffer, 0, length-offset);
this.position += length-offset;
}
/**
* MUST call checkBuffer before calling this!
*
* NOT PROTECTED
*/
private final void _put(byte b) {
this.bytes[this.position++] = b;
}
/** NOT PROTECTED! */
private final void checkBuffer(int threshold) {
if (this.bytes.length < threshold) {
byte[] t = new byte[threshold];
// grow at back of array
System.arraycopy(this.bytes, 0, t, 0, this.bytes.length);
this.limit = t.length;
this.bytes = t;
}
}
public final void put(ByteBuffer2Fast buffer) {
putBytes(buffer.array(), buffer.position, buffer.limit);
buffer.position = buffer.limit;
}
public final ByteBuffer2Fast putBytes(byte[] src) {
return putBytes(src, 0, src.length);
}
public final ByteBuffer2Fast putBytes(byte[] src, int offset, int length) {
checkBuffer(this.position + length - offset);
System.arraycopy(src, offset, this.bytes, this.position, length);
this.position += length;
return this;
}
public final ByteBuffer2Fast putByte(byte b) {
checkBuffer(this.position + 1);
_put(b);
return this;
}
public final void putByte(int position, byte b) {
this.position = position;
putByte(b);
}
public final void putChar(char c) {
checkBuffer(this.position + 2);
putBytes(BigEndian.Char_.toBytes(c));
}
public final char getChar() {
return BigEndian.Char_.fromBytes(getByte(), getByte());
}
public final char getChar(int i) {
return BigEndian.Char_.fromBytes(getByte(i++), getByte(i));
}
public void getChars(int srcStart, int srcLength, char[] dest, int destStart) {
for (int i=srcStart;i<srcLength;i+=2) {
char c = BigEndian.Char_.fromBytes(getByte(i), getByte(i+1));
dest[destStart++] = c;
}
}
public final ByteBuffer2Fast putShort(short x) {
checkBuffer(this.position + 2);
putBytes(BigEndian.Short_.toBytes(x));
return this;
}
public final short getShort() {
return BigEndian.Short_.fromBytes(getByte(), getByte());
}
public final short getShort(int i) {
return BigEndian.Short_.fromBytes(getByte(i++), getByte(i));
}
public final void putInt(int x) {
checkBuffer(this.position + 4);
putBytes(BigEndian.Int_.toBytes(x));
}
public final int getInt() {
return BigEndian.Int_.fromBytes(getByte(), getByte(), getByte(), getByte());
}
public final int getInt(int i) {
return BigEndian.Int_.fromBytes(getByte(i++), getByte(i++), getByte(i++), getByte(i++));
}
public final void putLong(long x) {
checkBuffer(this.position + 8);
putBytes(BigEndian.Long_.toBytes(x));
}
public final long getLong() {
return BigEndian.Long_.fromBytes(getByte(), getByte(), getByte(), getByte(), getByte(), getByte(), getByte(), getByte());
}
public final long getLong(int i) {
return BigEndian.Long_.fromBytes(getByte(i++), getByte(i++), getByte(i++), getByte(i++), getByte(i++), getByte(i++), getByte(i++), getByte(i++));
}
/**
* Returns the backing array of this buffer
*/
public byte[] array() {
return this.bytes;
}
/**
* Returns a copy of the backing array of this buffer
*/
public final byte[] arrayCopy() {
int length = this.bytes.length - this.position;
byte[] b = new byte[length];
System.arraycopy(this.bytes, this.position, b, 0, length);
return b;
}
/**
* Returns this buffer's position.
*/
public int position() {
return this.position;
}
/**
* Sets this buffer's position.
*/
public final ByteBuffer2Fast position(int position) {
if (position > this.bytes.length || position < 0) {
throw new IllegalArgumentException();
}
this.position = position;
if (this.mark > position) {
this.mark = -1;
}
return this;
}
/**
* Returns the number of elements between the current position and the
* limit.
*/
public final int remaining() {
return this.limit - this.position;
}
/**
* Tells whether there are any elements between the current position and
* the limit.
*/
public final boolean hasRemaining() {
return this.position < this.limit;
}
/**
* Sets this buffer's limit.
*/
public final void limit(int limit) {
this.limit = limit;
if (this.position > limit) {
this.position = limit;
}
if (this.mark > limit) {
this.mark = -1;
}
}
/**
* Returns this buffer's limit.
*/
public int limit() {
return this.limit;
}
/**
* Returns this buffer's capacity.
*/
public int capacity() {
return this.bytes.length;
}
/**
* The bytes between the buffer's current position and its limit, if any, are copied to the beginning of the buffer.
* That is, the byte at index p = position() is copied to index zero, the byte at index p + 1 is copied to index one,
* and so forth until the byte at index limit() - 1 is copied to index n = limit() - 1 - p. The buffer's position is
* then set to n+1 and its limit is set to its capacity. The mark, if defined, is discarded.
*
* The buffer's position is set to the number of bytes copied, rather than to zero, so that an invocation of this method
* can be followed immediately by an invocation of another relative put method.
*/
public final void compact() {
this.mark = -1;
System.arraycopy(this.bytes, this.position, this.bytes, 0, remaining());
position(remaining());
limit(capacity());
}
/**
* Readies the buffer for reading.
*
* Flips this buffer. The limit is set to the current position and then
* the position is set to zero. If the mark is defined then it is
* discarded.
*/
public final void flip() {
this.limit = this.position;
this.position = 0;
this.mark = -1;
}
/**
* Clears this buffer. The position is set to zero, the limit is set to
* the capacity, and the mark is discarded.
*/
public final void clear() {
this.position = 0;
this.limit = capacity();
this.mark = -1;
}
/**
* Rewinds this buffer. The position is set to zero and the mark is
* discarded.
*/
public final void rewind() {
this.position = 0;
this.mark = -1;
}
/**
* Sets this buffer's mark at its position.
*/
public final void mark() {
this.mark = this.position;
}
/**
* Resets this buffer's position to the previously-marked position.
*
* <p> Invoking this method neither changes nor discards the mark's
* value. </p>
*/
public void reset() {
this.position = this.mark;
}
}

View File

@ -5,18 +5,21 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.CopyOnWriteArrayList;
import org.fusesource.jansi.Ansi;
import org.fusesource.jansi.AnsiConsole;
import org.slf4j.Logger;
import dorkbox.util.OS;
import dorkbox.util.Sys;
import dorkbox.util.bytes.ByteBuffer2Fast;
import dorkbox.util.input.posix.UnixTerminal;
import dorkbox.util.input.unsupported.UnsupportedTerminal;
import dorkbox.util.input.windows.WindowsTerminal;
@ -36,20 +39,31 @@ public class InputConsole {
{
AnsiConsole.systemInstall();
Thread consoleThread = new Thread(new Runnable() {
@Override
public void run() {
consoleProxyReader.run();
}
});
consoleThread.setDaemon(true);
consoleThread.setName("Console Input Reader");
consoleThread.start();
// has to be NOT DAEMON thread, since it must run before the app closes.
// don't forget we have to shut down the ansi console as well
// alternatively, shut everything down when the JVM closes.
Runtime.getRuntime().addShutdownHook(new Thread() {
Thread shutdownThread = new Thread() {
@Override
public void run() {
AnsiConsole.systemUninstall();
InputConsole.destroy();
}
});
}
// called by our shutdown thread
private static final void destroy() {
consoleProxyReader.destroy0();
consoleProxyReader.shutdown0();
}
};
shutdownThread.setName("Console Input Shutdown");
Runtime.getRuntime().addShutdownHook(shutdownThread);
}
/** return null if no data */
@ -94,9 +108,17 @@ public class InputConsole {
private final Object inputLockSingle = new Object();
private final Object inputLockLine = new Object();
private AtomicBoolean isRunning = new AtomicBoolean(false);
private AtomicBoolean isInShutdown = new AtomicBoolean(false);
private volatile char[] readLine = null;
private ThreadLocal<ByteBuffer2Fast> threadBufferForRead = new ThreadLocal<ByteBuffer2Fast>();
private CopyOnWriteArrayList<ByteBuffer2Fast> threadBuffersForRead = new CopyOnWriteArrayList<ByteBuffer2Fast>();
ThreadLocal<Integer> indexOfStringForReadChar = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return -1;
}
};
private volatile int readChar = -1;
private final boolean unsupported;
@ -119,7 +141,7 @@ public class InputConsole {
logger.debug("Creating terminal; type={}", type);
Terminal t;
Terminal t;
try {
if (type.equals(TerminalType.UNIX)) {
t = new UnixTerminal();
@ -182,36 +204,8 @@ public class InputConsole {
logger.debug("Created Terminal: {}", this.terminal);
}
/**
* make sure the input console reader thread is started.
*/
private void startInputConsole() {
// protected by atomic!
if (!this.isRunning.compareAndSet(false, true) || this.isInShutdown.get()) {
return;
}
Thread consoleThread = new Thread(new Runnable() {
@Override
public void run() {
consoleProxyReader.run();
}
});
consoleThread.setDaemon(true);
consoleThread.setName("Console Input Reader");
consoleThread.start();
}
private void destroy0() {
// Don't change this, because we don't want to enable reading, etc from this once it's destroyed.
// so we pretend that it's still running
// isRunning.set(false);
if (this.isInShutdown.compareAndSet(true, true)) {
return;
}
// called when the JVM is shutting down.
private void shutdown0() {
synchronized (this.inputLockSingle) {
this.inputLockSingle.notifyAll();
}
@ -224,7 +218,8 @@ public class InputConsole {
InputConsole inputConsole = InputConsole.this;
inputConsole.terminal.restore();
inputConsole.reader.close();
// this will 'hang' our shutdown, and honestly, who cares? We're shutting down anyways.
// inputConsole.reader.close(); // hangs on shutdown
} catch (IOException ignored) {
ignored.printStackTrace();
}
@ -241,7 +236,11 @@ public class InputConsole {
/** return null if no data */
private final char[] readLine0() {
startInputConsole();
if (this.threadBufferForRead.get() == null) {
ByteBuffer2Fast buffer = ByteBuffer2Fast.allocate(0);
this.threadBufferForRead.set(buffer);
this.threadBuffersForRead.add(buffer);
}
synchronized (this.inputLockLine) {
try {
@ -250,34 +249,53 @@ public class InputConsole {
return emptyLine;
}
}
return this.readLine;
ByteBuffer2Fast stringBuffer = this.threadBufferForRead.get();
int len = stringBuffer.position();
if (len == 0) {
return emptyLine;
}
char[] chars = new char[len/2]; // because 2 chars is 1 bytes
stringBuffer.getChars(0, len, chars, 0);
// dump the chars in the buffer (safer for passwords, etc)
stringBuffer.clear();
stringBuffer.putBytes(new byte[0]);
this.threadBufferForRead.set(null);
this.threadBuffersForRead.remove(stringBuffer); // TODO: use object pool!
return chars;
}
/** return null if no data */
private final char[] readLinePassword0() {
// don't bother in an IDE. it won't work.
return readLine0();
}
ThreadLocal<Integer> indexOfStringForReadChar = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return -1;
if (this.threadBufferForRead.get() == null) {
ByteBuffer2Fast buffer = ByteBuffer2Fast.allocate(0);
this.threadBufferForRead.set(buffer);
this.threadBuffersForRead.add(buffer);
}
};
// don't bother in an IDE. it won't work.
boolean echoEnabled = this.terminal.isEchoEnabled();
this.terminal.setEchoEnabled(false);
char[] readLine0 = readLine0();
this.terminal.setEchoEnabled(echoEnabled);
return readLine0;
}
/** return -1 if no data */
private final int read0() {
startInputConsole();
// if we are reading data (because we are in IDE mode), we want to return ALL
// the chars of the line!
// so, readChar is REALLY the index at which we return letters (until the whole string is returned
if (this.unsupported) {
int integer = this.indexOfStringForReadChar.get();
int readerCount = this.indexOfStringForReadChar.get();
if (integer == -1) {
if (readerCount == -1) {
// we have to wait for more data.
synchronized (this.inputLockLine) {
try {
@ -285,24 +303,23 @@ public class InputConsole {
} catch (InterruptedException e) {
return -1;
}
integer = 0;
readerCount = 0;
this.indexOfStringForReadChar.set(0);
}
}
// EACH thread will have it's own count!
char[] readLine2 = this.readLine;
if (readLine2 == null) {
return -1;
} else if (integer == readLine2.length) {
// EACH thread will have it's own count!
ByteBuffer2Fast stringBuffer = this.threadBufferForRead.get();
if (readerCount == stringBuffer.position()) {
this.indexOfStringForReadChar.set(-1);
return '\n';
} else {
this.indexOfStringForReadChar.set(integer+1);
this.indexOfStringForReadChar.set(readerCount+1);
}
char c = readLine2[integer];
char c = stringBuffer.getChar(readerCount);
return c;
}
else {
@ -339,19 +356,30 @@ public class InputConsole {
// it just waits until \n until it triggers
if (this.unsupported) {
BufferedReader reader = (BufferedReader) this.reader;
String line = null;
char[] readLine = null;
try {
while ((this.readLine = reader.readLine().toCharArray()) != null) {
while ((line = reader.readLine()) != null) {
readLine = line.toCharArray();
// notify everyone waiting for a line of text.
synchronized (this.inputLockSingle) {
if (this.readLine.length > 0) {
this.readChar = this.readLine[0];
if (readLine.length > 0) {
this.readChar = readLine[0];
} else {
this.readChar = -1;
}
this.inputLockSingle.notifyAll();
}
synchronized (this.inputLockLine) {
byte[] charToBytes = Sys.charToBytes(readLine);
for (ByteBuffer2Fast buffer : this.threadBuffersForRead) {
buffer.clear();
buffer.putBytes(charToBytes);
}
this.inputLockLine.notifyAll();
}
}
@ -360,12 +388,13 @@ public class InputConsole {
}
}
else {
// from a 'regular' console
try {
final boolean ansiEnabled = Ansi.isEnabled();
Ansi ansi = Ansi.ansi();
PrintStream out = AnsiConsole.out;
int typedChar;
StringBuilder buf = new StringBuilder();
// don't type ; in a bash shell, it quits everything
// \n is replaced by \r in unix terminal?
@ -388,65 +417,57 @@ public class InputConsole {
// clear ourself + one extra.
if (ansiEnabled) {
// size of the buffer BEFORE our backspace was typed
int length = buf.length();
int amtToOverwrite = 2; // backspace is always 2 chars (^?)
for (ByteBuffer2Fast buffer : this.threadBuffersForRead) {
// size of the buffer BEFORE our backspace was typed
int length = buffer.position();
int amtToOverwrite = 2 * 2; // backspace is always 2 chars (^?) * 2 because it's bytes
if (length > 1) {
char charAt = buf.charAt(length-1);
amtToOverwrite += getPrintableCharacters(charAt);
if (length > 1) {
char charAt = buffer.getChar(length-2);
amtToOverwrite += getPrintableCharacters(charAt);
// delete last item in our buffer
buf.setLength(--length);
// delete last item in our buffer
length -= 2;
buffer.position(length);
// now figure out where the cursor is really at.
// this is more memory friendly than buf.toString.length
for (int i=0;i<length;i++) {
charAt = buf.charAt(i);
position += getPrintableCharacters(charAt);
// now figure out where the cursor is really at.
// this is more memory friendly than buf.toString.length
for (int i=0;i<length;i+=2) {
charAt = buffer.getChar(i);
position += getPrintableCharacters(charAt);
}
position++;
}
position++;
}
char[] overwrite = new char[amtToOverwrite];
char c = ' ';
for (int i=0;i<amtToOverwrite;i++) {
overwrite[i] = c;
}
char[] overwrite = new char[amtToOverwrite];
char c = ' ';
for (int i=0;i<amtToOverwrite;i++) {
overwrite[i] = c;
}
// move back however many, over write, then go back again
AnsiConsole.out.print(ansi.cursorToColumn(position));
AnsiConsole.out.print(overwrite);
AnsiConsole.out.print(ansi.cursorToColumn(position));
AnsiConsole.out.flush();
// move back however many, over write, then go back again
out.print(ansi.cursorToColumn(position));
out.print(overwrite);
out.print(ansi.cursorToColumn(position));
out.flush();
}
}
// readline will ignore backspace
// read-line will ignore backspace
continue;
}
// ignoring \r, because \n is ALWAYS the last character in a new line sequence. (even for windows)
if (asChar == '\n') {
int length = buf.length();
synchronized (this.inputLockLine) {
if (length > 0) {
this.readLine = new char[length];
buf.getChars(0, length, this.readLine, 0);
} else {
this.readLine = emptyLine;
}
this.inputLockLine.notifyAll();
}
// dump the characters in the backing array (slightly safer for passwords when using this method)
if (length > 0) {
buf.delete(0, buf.length());
}
} else if (asChar != '\r') {
// only append if we are not a new line.
buf.append(asChar);
for (ByteBuffer2Fast buffer : this.threadBuffersForRead) {
buffer.putChar(asChar);
}
}
}
} catch (IOException ignored) {
@ -455,11 +476,11 @@ public class InputConsole {
}
/**
+ * Get the default encoding. Will first look at the LC_CTYPE environment variable, then the input.encoding
+ * system property, then the default charset according to the JVM.
+ *
+ * @return The default encoding to use when none is specified.
+ */
* Get the default encoding. Will first look at the LC_CTYPE environment variable, then the input.encoding
* system property, then the default charset according to the JVM.
*
* @return The default encoding to use when none is specified.
*/
public static String getEncoding() {
// LC_CTYPE is usually in the form en_US.UTF-8
String envEncoding = extractEncodingFromCtype(System.getenv("LC_CTYPE"));
@ -481,9 +502,8 @@ public class InputConsole {
String encodingAndModifier = ctype.substring(ctype.indexOf('.') + 1);
if (encodingAndModifier.indexOf('@') > 0) {
return encodingAndModifier.substring(0, encodingAndModifier.indexOf('@'));
} else {
return encodingAndModifier;
}
return encodingAndModifier;
}
return null;
}

View File

@ -54,7 +54,6 @@ public class WindowsTerminal extends Terminal
public void init() throws IOException {
this.originalMode = WindowsSupport.getConsoleMode();
WindowsSupport.setConsoleMode(this.originalMode & ~ConsoleMode.ENABLE_ECHO_INPUT.code);
setEchoEnabled(false);
}
/**