private int processElement(char chunk[], int offset, int length) throws GuacamoleException {

        int charsParsed = 0;

        // Do not exceed maximum number of elements
        if (elementCount == INSTRUCTION_MAX_ELEMENTS && state != State.COMPLETE) {
            state = State.ERROR;
            throw new GuacamoleServerException("Instruction contains too many elements.");
        }

        // Parse element length
        if (state == State.PARSING_LENGTH) {

            int parsedLength = elementLength;
            while (charsParsed < length) {

                // Pull next character
                char c = chunk[offset + charsParsed++];

                // If digit, add to length
                if (c >= '0' && c <= '9')
                    parsedLength = parsedLength*10 + c - '0';

                // If period, switch to parsing content
                else if (c == '.') {
                    state = State.PARSING_CONTENT;
                    break;
                }

                // If not digit, parse error
                else {
                    state = State.ERROR;
                    throw new GuacamoleServerException("Non-numeric character in element length.");
                }

            }

            // If too long, parse error
            if (parsedLength > INSTRUCTION_MAX_LENGTH) {
                state = State.ERROR;
                throw new GuacamoleServerException("Instruction exceeds maximum length.");
            }

            // Save length
            elementCodepoints = elementLength = parsedLength;

        } // end parse length

        // Parse element content, if available
        while (state == State.PARSING_CONTENT && charsParsed + elementLength + 1 <= length) {

            // Read element (which may not match element length if surrogate
            // characters are present)
            String element = new String(chunk, offset + charsParsed, elementLength);

            // Verify element contains the number of whole Unicode characters
            // expected, scheduling a future read if we don't yet have enough
            // characters
            int codepoints = element.codePointCount(0, element.length());
            if (codepoints < elementCodepoints) {
                elementLength += elementCodepoints - codepoints;
                continue;
            }

            // If the current element ends with a character involving both
            // a high and low surrogate, elementLength points to the low
            // surrogate and NOT the element terminator. We must correct the
            // length and reevaluate.
            else if (Character.isSurrogatePair(chunk[offset + charsParsed + elementLength - 1],
                    chunk[offset + charsParsed + elementLength])) {
                elementLength++;
                continue;
            }

            charsParsed += elementLength;
            elementLength = 0;

            // Add element to currently parsed elements
            elements[elementCount++] = element;

            // Read terminator char following element
            char terminator = chunk[offset + charsParsed++];
            switch (terminator) {

                // If semicolon, store end-of-instruction
                case ';':
                    state = State.COMPLETE;
                    break;

                // If comma, move on to next element
                case ',':
                    state = State.PARSING_LENGTH;
                    break;

                // Otherwise, parse error
                default:
                    state = State.ERROR;
                    throw new GuacamoleServerException("Element terminator of instruction was not ';' nor ','");

            }

        } // end parse content

        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.
guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleParser.java