Repository: netty/netty
File: codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttDecoder.java (Java)

Code before:
        if (!isValidClientId(mqttVersion, maxClientIdLength, decodedClientIdValue)) {
            throw new MqttIdentifierRejectedException("invalid clientIdentifier: " + decodedClientIdValue);
        }
        int numberOfBytesConsumed = decodedClientId.numberOfBytesConsumed;

        Result<String> decodedWillTopic = null;
        byte[] decodedWillMessage = null;

        final MqttProperties willProperties;
        if (mqttConnectVariableHeader.isWillFlag()) {
            if (mqttVersion == MqttVersion.MQTT_5) {
                final Result<MqttProperties> propertiesResult = decodeProperties(buffer);
                willProperties = propertiesResult.value;
                numberOfBytesConsumed += propertiesResult.numberOfBytesConsumed;
            } else {
                willProperties = MqttProperties.NO_PROPERTIES;
            }
            decodedWillTopic = decodeString(buffer, 0, 32767);
            numberOfBytesConsumed += decodedWillTopic.numberOfBytesConsumed;
            decodedWillMessage = decodeByteArray(buffer);
            numberOfBytesConsumed += decodedWillMessage.length + 2;
        } else {
            willProperties = MqttProperties.NO_PROPERTIES;
        }
        Result<String> decodedUserName = null;
        byte[] decodedPassword = null;
        if (mqttConnectVariableHeader.hasUserName()) {
            decodedUserName = decodeString(buffer);
            numberOfBytesConsumed += decodedUserName.numberOfBytesConsumed;
        }
        if (mqttConnectVariableHeader.hasPassword()) {
            decodedPassword = decodeByteArray(buffer);
            numberOfBytesConsumed += decodedPassword.length + 2;
        }

        validateNoBytesRemain(numberOfBytesConsumed);
        return new MqttConnectPayload(
                        decodedClientId.value,
                        willProperties,
                        decodedWillTopic != null ? decodedWillTopic.value : null,
                        decodedWillMessage,
                        decodedUserName != null ? decodedUserName.value : null,
                        decodedPassword);
    }

    private MqttSubscribePayload decodeSubscribePayload(
            ByteBuf buffer) {
        final List<MqttTopicSubscription> subscribeTopics = new ArrayList<MqttTopicSubscription>();
        int numberOfBytesConsumed = 0;
        while (numberOfBytesConsumed < bytesRemainingInVariablePart) {
            final Result<String> decodedTopicName = decodeString(buffer);

Review comment (suggestion):
@normanmaurer would be nice to have `toString(int length, Charset charset)`, similar to `readCharSequence`

```
    @Override
    public CharSequence readCharSequence(int length, Charset charset) {
        CharSequence sequence = getCharSequence(readerIndex, length, charset);
        readerIndex += length;
        return sequence;
    }
```

that uses `readerIndex` as `index` and increments readerIndex. So we can avoid 2 polymorphic method calls.

Code after:
        if (mqttConnectVariableHeader.hasUserName()) {
            decodedUserName = decodeStringAndDecreaseBytesRemaining(buffer);
        }
        if (mqttConnectVariableHeader.hasPassword()) {
            decodedPassword = decodeByteArray(buffer);
            numberOfBytesConsumed += decodedPassword.length + 2;
        }

        validateNoBytesRemain(numberOfBytesConsumed);
        return new MqttConnectPayload(
                        decodedClientId,
                        willProperties,
                        decodedWillTopic,
                        decodedWillMessage,
                        decodedUserName,
                        decodedPassword);
    }

    private MqttSubscribePayload decodeSubscribePayload(
            ByteBuf buffer) {
        final List<MqttTopicSubscription> subscribeTopics = new ArrayList<MqttTopicSubscription>();
        int numberOfBytesConsumed = 0;
        while (numberOfBytesConsumed < bytesRemainingInVariablePart) {
            int topicNameSize = decodeMsbLsb(buffer);
            String decodedTopicName = buffer.toString(buffer.readerIndex(), topicNameSize, CharsetUtil.UTF_8);
            buffer.skipBytes(topicNameSize);
            numberOfBytesConsumed += 2 + topicNameSize;

            //See 3.8.3.1 Subscription Options of MQTT 5.0 specification for optionByte details
            final short optionByte = buffer.readUnsignedByte();

            MqttQoS qos = MqttQoS.valueOf(optionByte & 0x03);
            boolean noLocal = ((optionByte & 0x04) >> 2) == 1;
            boolean retainAsPublished = ((optionByte & 0x08) >> 3) == 1;
            RetainedHandlingPolicy retainHandling = RetainedHandlingPolicy.valueOf((optionByte & 0x30) >> 4);

            final MqttSubscriptionOption subscriptionOption = new MqttSubscriptionOption(qos,
                    noLocal,
                    retainAsPublished,
                    retainHandling);

            numberOfBytesConsumed++;
            subscribeTopics.add(new MqttTopicSubscription(decodedTopicName, subscriptionOption));
        }
        validateNoBytesRemain(numberOfBytesConsumed);
        return new MqttSubscribePayload(subscribeTopics);
    }

    private MqttSubAckPayload decodeSubackPayload(
            ByteBuf buffer) {
        int bytesRemainingInVariablePart = this.bytesRemainingInVariablePart;