private static Arbitrary<Encoder> groupsEncoder(
        final List<Token> tokens,
        final MutableInteger cursor,
        final int endIdxInclusive,
        final CharGenerationMode charGenerationMode)
    {
        final List<Arbitrary<Encoder>> encoders = new ArrayList<>();

        while (cursor.get() <= endIdxInclusive)
        {
            final Token token = tokens.get(cursor.get());
            if (BEGIN_GROUP != token.signal())
            {
                break;
            }
            final int nextFieldIdx = cursor.get() + token.componentTokenCount();

            cursor.increment(); // consume BEGIN_GROUP
            cursor.increment(); // consume BEGIN_COMPOSITE
            final Token blockLengthToken = tokens.get(cursor.get());
            final int blockLength = token.encodedLength();
            final Encoder blockLengthEncoder = integerValueEncoder(blockLengthToken.encoding(), blockLength);
            cursor.increment(); // consume ENCODED
            final Token numInGroupToken = tokens.get(cursor.get());
            cursor.increment(); // consume ENCODED
            cursor.increment(); // consume END_COMPOSITE
            final int headerLength = blockLengthToken.encodedLength() + numInGroupToken.encodedLength();


            final Arbitrary<Encoder> groupElement = Combinators.combine(
                fieldsEncoder(tokens, cursor, nextFieldIdx - 1, true, charGenerationMode),
                groupsEncoder(tokens, cursor, nextFieldIdx - 1, charGenerationMode),
                varDataEncoder(tokens, cursor, nextFieldIdx - 1, charGenerationMode)
            ).as((fieldsEncoder, groupsEncoder, varDataEncoder) ->
                (builder, buffer, ignored, limit) ->
                {
                    final int offset = limit.get();
                    fieldsEncoder.encode(builder, buffer, offset, null);
                    buffer.checkLimit(offset + blockLength);
                    limit.set(offset + blockLength);
                    builder.appendLine().append("limit: ").append(offset).append(" -> ").append(limit.get());
                    groupsEncoder.encode(builder, buffer, NULL_VALUE, limit);
                    varDataEncoder.encode(builder, buffer, NULL_VALUE, limit);
                });

            final Arbitrary<Encoder> repeatingGroupEncoder = groupElement.list()
                .ofMaxSize(10)
                .map(elements -> (builder, buffer, ignored, limit) ->
                {
                    final int offset = limit.get();
                    limit.set(offset + headerLength);
                    builder.beginScope(token.name());
                    builder.appendLine().append("limit: ").append(offset).append(" -> ").append(limit.get());
                    builder.beginScope("blockLength");
                    blockLengthEncoder.encode(builder, buffer, offset, null);
                    builder.endScope();
                    builder.beginScope("numInGroup");
                    integerValueEncoder(numInGroupToken.encoding(), elements.size())
                        .encode(builder, buffer, offset + blockLengthToken.encodedLength(), null);
                    builder.endScope();
                    for (int i = 0; i < elements.size(); i++)
                    {
                        final Encoder element = elements.get(i);
                        builder.beginScope("[" + i + "]");
                        element.encode(builder, buffer, NULL_VALUE, limit);
                        builder.endScope();
                    }
                    builder.endScope();
                });

            encoders.add(repeatingGroupEncoder);

            cursor.set(nextFieldIdx);
        }

        return combineArbitraryEncoders(encoders);
    }
This combinator adds duplicates to an arbitrary list. We prefer this to JQwik's built-in functionality,
as that is inefficient and dominates test runs.
<p>
This method works by generating a list of integers, which represent, in an alternating manner,
the number of items to skip before <i>selecting</i> an item to duplicate
and the number of items to skip before <i>inserting</i> the duplicate.

@param maxDuplicates the maximum number of duplicates to add
@param arbitrary     the arbitrary list to duplicate items in
@param <T>           the type of the list
@return an arbitrary list with duplicates
sbe-tool/src/propertyTest/java/uk/co/real_logic/sbe/properties/arbitraries/SbeArbitraries.java