mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 21:27:40 +00:00
GUACAMOLE-2036: Merge changes to reuse buffers received by parser when converting instructions back to character arrays.
This commit is contained in:
@@ -24,7 +24,6 @@ import java.io.IOException;
|
|||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.util.Arrays;
|
|
||||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
import org.apache.guacamole.GuacamoleServerException;
|
import org.apache.guacamole.GuacamoleServerException;
|
||||||
@@ -92,7 +91,7 @@ public class ReaderGuacamoleReader implements GuacamoleReader {
|
|||||||
if (instruction == null)
|
if (instruction == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return instruction.toString().toCharArray();
|
return instruction.toCharArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -41,18 +41,31 @@ public class GuacamoleInstruction {
|
|||||||
private final List<String> args;
|
private final List<String> args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The cached result of converting this GuacamoleInstruction to the format
|
* The cached String result of converting this GuacamoleInstruction to the
|
||||||
* used by the Guacamole protocol.
|
* format used by the Guacamole protocol.
|
||||||
|
*
|
||||||
|
* @see #toString()
|
||||||
*/
|
*/
|
||||||
private String protocolForm = null;
|
private String rawString = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new GuacamoleInstruction having the given Operation and
|
* The cached char[] result of converting this GuacamoleInstruction to the
|
||||||
* list of arguments values.
|
* format used by the Guacamole protocol.
|
||||||
*
|
*
|
||||||
* @param opcode The opcode of the instruction to create.
|
* @see #toCharArray()
|
||||||
* @param args The list of argument values to provide in the new
|
*/
|
||||||
* instruction if any.
|
private char[] rawChars = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new GuacamoleInstruction having the given opcode and list of
|
||||||
|
* argument values.
|
||||||
|
*
|
||||||
|
* @param opcode
|
||||||
|
* The opcode of the instruction to create.
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* The list of argument values to provide in the new instruction, if
|
||||||
|
* any.
|
||||||
*/
|
*/
|
||||||
public GuacamoleInstruction(String opcode, String... args) {
|
public GuacamoleInstruction(String opcode, String... args) {
|
||||||
this.opcode = opcode;
|
this.opcode = opcode;
|
||||||
@@ -60,22 +73,59 @@ public class GuacamoleInstruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new GuacamoleInstruction having the given Operation and
|
* Creates a new GuacamoleInstruction having the given opcode and list of
|
||||||
* list of arguments values. The list given will be used to back the
|
* argument values. The list given will be used to back the internal list of
|
||||||
* internal list of arguments and the list returned by getArgs().
|
* arguments and the list returned by {@link #getArgs()}.
|
||||||
|
* <p>
|
||||||
|
* The provided argument list may not be modified in any way after being
|
||||||
|
* provided to this constructor. Doing otherwise will result in undefined
|
||||||
|
* behavior.
|
||||||
*
|
*
|
||||||
* @param opcode The opcode of the instruction to create.
|
* @param opcode
|
||||||
* @param args The list of argument values to provide in the new
|
* The opcode of the instruction to create.
|
||||||
* instruction if any.
|
*
|
||||||
|
* @param args
|
||||||
|
* The list of argument values to provide in the new instruction, if
|
||||||
|
* any.
|
||||||
*/
|
*/
|
||||||
public GuacamoleInstruction(String opcode, List<String> args) {
|
public GuacamoleInstruction(String opcode, List<String> args) {
|
||||||
this.opcode = opcode;
|
this.opcode = opcode;
|
||||||
this.args = Collections.unmodifiableList(args);
|
this.args = Collections.unmodifiableList(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new GuacamoleInstruction having the given opcode, list of
|
||||||
|
* argument values, and underlying protocol representation. The list given
|
||||||
|
* will be used to back the internal list of arguments and the list
|
||||||
|
* returned by {@link #getArgs()}. The provided protocol representation
|
||||||
|
* will be used to back the internal protocol representation and values
|
||||||
|
* returned by {@link #toCharArray()} and {@link #toString()}.
|
||||||
|
* <p>
|
||||||
|
* Neither the provided argument list nor the provided protocol
|
||||||
|
* representation may be modified in any way after being provided to this
|
||||||
|
* constructor. Doing otherwise will result in undefined behavior.
|
||||||
|
*
|
||||||
|
* @param opcode
|
||||||
|
* The opcode of the instruction to create.
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* The list of argument values to provide in the new instruction, if
|
||||||
|
* any.
|
||||||
|
*
|
||||||
|
* @param raw
|
||||||
|
* The underlying representation of this instruction as would be sent
|
||||||
|
* over the network via the Guacamole protocol.
|
||||||
|
*/
|
||||||
|
public GuacamoleInstruction(String opcode, List<String> args, char[] raw) {
|
||||||
|
this(opcode, args);
|
||||||
|
this.rawChars = raw;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the opcode associated with this GuacamoleInstruction.
|
* Returns the opcode associated with this GuacamoleInstruction.
|
||||||
* @return The opcode associated with this GuacamoleInstruction.
|
*
|
||||||
|
* @return
|
||||||
|
* The opcode associated with this GuacamoleInstruction.
|
||||||
*/
|
*/
|
||||||
public String getOpcode() {
|
public String getOpcode() {
|
||||||
return opcode;
|
return opcode;
|
||||||
@@ -86,7 +136,8 @@ public class GuacamoleInstruction {
|
|||||||
* GuacamoleInstruction. Note that the List returned is immutable.
|
* GuacamoleInstruction. Note that the List returned is immutable.
|
||||||
* Attempts to modify the list will result in exceptions.
|
* Attempts to modify the list will result in exceptions.
|
||||||
*
|
*
|
||||||
* @return A List of all argument values specified for this
|
* @return
|
||||||
|
* An unmodifiable List of all argument values specified for this
|
||||||
* GuacamoleInstruction.
|
* GuacamoleInstruction.
|
||||||
*/
|
*/
|
||||||
public List<String> getArgs() {
|
public List<String> getArgs() {
|
||||||
@@ -122,7 +173,15 @@ public class GuacamoleInstruction {
|
|||||||
|
|
||||||
// Avoid rebuilding Guacamole protocol form of instruction if already
|
// Avoid rebuilding Guacamole protocol form of instruction if already
|
||||||
// known
|
// known
|
||||||
if (protocolForm == null) {
|
if (rawString == null) {
|
||||||
|
|
||||||
|
// Prefer to construct String from existing char array, rather than
|
||||||
|
// reconstruct protocol from scratch
|
||||||
|
if (rawChars != null)
|
||||||
|
rawString = new String(rawChars);
|
||||||
|
|
||||||
|
// Reconstruct protocol details only if truly necessary
|
||||||
|
else {
|
||||||
|
|
||||||
StringBuilder buff = new StringBuilder();
|
StringBuilder buff = new StringBuilder();
|
||||||
|
|
||||||
@@ -139,11 +198,33 @@ public class GuacamoleInstruction {
|
|||||||
buff.append(';');
|
buff.append(';');
|
||||||
|
|
||||||
// Cache result for future calls
|
// Cache result for future calls
|
||||||
protocolForm = buff.toString();
|
rawString = buff.toString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return protocolForm;
|
}
|
||||||
|
|
||||||
|
return rawString;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this GuacamoleInstruction in the form it would be sent over the
|
||||||
|
* Guacamole protocol. The returned char[] MUST NOT be modified. If the
|
||||||
|
* returned char[] is modified, the results of doing so are undefined.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* This GuacamoleInstruction in the form it would be sent over the
|
||||||
|
* Guacamole protocol. The returned char[] MUST NOT be modified.
|
||||||
|
*/
|
||||||
|
public char[] toCharArray() {
|
||||||
|
|
||||||
|
// Avoid rebuilding Guacamole protocol form of instruction if already
|
||||||
|
// known
|
||||||
|
if (rawChars == null)
|
||||||
|
rawChars = toString().toCharArray();
|
||||||
|
|
||||||
|
return rawChars;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -108,6 +108,18 @@ public class GuacamoleParser implements Iterator<GuacamoleInstruction> {
|
|||||||
*/
|
*/
|
||||||
private final String elements[] = new String[INSTRUCTION_MAX_ELEMENTS];
|
private final String elements[] = new String[INSTRUCTION_MAX_ELEMENTS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A copy of the raw protocol data that has been parsed for the current
|
||||||
|
* instruction. This value is maintained by {@link #append(char[], int, int)}.
|
||||||
|
*/
|
||||||
|
private final char rawInstruction[] = new char[INSTRUCTION_MAX_LENGTH];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset within {@link #rawInstruction} that new data should be
|
||||||
|
* appended. This value is maintained by {@link #append(char[], int, int)}.
|
||||||
|
*/
|
||||||
|
private int rawInstructionOffset = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends data from the given buffer to the current instruction.
|
* Appends data from the given buffer to the current instruction.
|
||||||
*
|
*
|
||||||
@@ -130,6 +142,71 @@ public class GuacamoleParser implements Iterator<GuacamoleInstruction> {
|
|||||||
*/
|
*/
|
||||||
public int append(char chunk[], int offset, int length) throws GuacamoleException {
|
public int append(char chunk[], int offset, int length) throws GuacamoleException {
|
||||||
|
|
||||||
|
int originalOffset = offset;
|
||||||
|
int originalLength = length;
|
||||||
|
|
||||||
|
// Process as much of the received chunk as possible
|
||||||
|
while (length > 0) {
|
||||||
|
|
||||||
|
int appended = processElement(chunk, offset, length);
|
||||||
|
if (appended == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
length -= appended;
|
||||||
|
offset += appended;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the raw copy of the received instruction with whatever data
|
||||||
|
// has now been processed
|
||||||
|
int charsParsed = originalLength - length;
|
||||||
|
if (charsParsed > 0) {
|
||||||
|
|
||||||
|
System.arraycopy(chunk, originalOffset, rawInstruction, rawInstructionOffset, charsParsed);
|
||||||
|
rawInstructionOffset += charsParsed;
|
||||||
|
|
||||||
|
// If the instruction is now complete, we're good to store the
|
||||||
|
// parsed instruction for future retrieval via next()
|
||||||
|
if (state == State.COMPLETE) {
|
||||||
|
parsedInstruction = new GuacamoleInstruction(elements[0], Arrays.asList(elements).subList(1, elementCount),
|
||||||
|
Arrays.copyOf(rawInstruction, rawInstructionOffset));
|
||||||
|
rawInstructionOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return charsParsed;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes additional data from the given buffer, potentially adding
|
||||||
|
* another element to the current instruction being parsed. This function
|
||||||
|
* will need to be invoked multiple times per instruction until all data
|
||||||
|
* for that instruction is ready.
|
||||||
|
* <p>
|
||||||
|
* This function DOES NOT update {@link #parsedInstruction}. The caller
|
||||||
|
* ({@link #append(char[], int, int)}) must update this as necessary when
|
||||||
|
* the parser {@link #state} indicates the instruction is complete.
|
||||||
|
*
|
||||||
|
* @param chunk
|
||||||
|
* The buffer containing the data to append.
|
||||||
|
*
|
||||||
|
* @param offset
|
||||||
|
* The offset within the buffer where the data begins.
|
||||||
|
*
|
||||||
|
* @param length
|
||||||
|
* The length of the data to append.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The number of characters appended, or 0 if complete instructions
|
||||||
|
* have already been parsed and must be read via next() before more
|
||||||
|
* data can be appended.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If an error occurs while parsing the new data.
|
||||||
|
*/
|
||||||
|
private int processElement(char chunk[], int offset, int length) throws GuacamoleException {
|
||||||
|
|
||||||
int charsParsed = 0;
|
int charsParsed = 0;
|
||||||
|
|
||||||
// Do not exceed maximum number of elements
|
// Do not exceed maximum number of elements
|
||||||
@@ -215,8 +292,6 @@ public class GuacamoleParser implements Iterator<GuacamoleInstruction> {
|
|||||||
// If semicolon, store end-of-instruction
|
// If semicolon, store end-of-instruction
|
||||||
case ';':
|
case ';':
|
||||||
state = State.COMPLETE;
|
state = State.COMPLETE;
|
||||||
parsedInstruction = new GuacamoleInstruction(elements[0],
|
|
||||||
Arrays.asList(elements).subList(1, elementCount));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// If comma, move on to next element
|
// If comma, move on to next element
|
||||||
|
Reference in New Issue
Block a user