private static Arbitrary<Encoder> fieldsEncoder(
        final List<Token> tokens,
        final MutableInteger cursor,
        final int endIdxInclusive,
        final boolean expectFields,
        final CharGenerationMode charGenerationMode)
    {
        final List<Arbitrary<Encoder>> encoders = new ArrayList<>();
        while (cursor.get() <= endIdxInclusive)
        {
            final Token memberToken = tokens.get(cursor.get());
            final int nextFieldIdx = cursor.get() + memberToken.componentTokenCount();

            Token typeToken = memberToken;
            int endFieldTokenCount = 0;

            if (BEGIN_FIELD == memberToken.signal())
            {
                cursor.increment();
                typeToken = tokens.get(cursor.get());
                endFieldTokenCount = 1;
            }
            else if (expectFields)
            {
                break;
            }

            final int offset = typeToken.offset();

            if (!memberToken.isConstantEncoding())
            {
                Arbitrary<Encoder> fieldEncoder = null;
                switch (typeToken.signal())
                {
                    case BEGIN_COMPOSITE:
                        cursor.increment();
                        final int endCompositeTokenCount = 1;
                        final int lastMemberIdx = nextFieldIdx - endCompositeTokenCount - endFieldTokenCount - 1;
                        final Arbitrary<Encoder> encoder = fieldsEncoder(
                            tokens, cursor, lastMemberIdx, false, charGenerationMode);
                        fieldEncoder = encoder.map(e ->
                            (builder, buffer, bufferOffset, limit) ->
                                e.encode(builder, buffer, bufferOffset + offset, limit));
                        break;

                    case BEGIN_ENUM:
                        final int endEnumTokenCount = 1;
                        final int lastValidValueIdx = nextFieldIdx - endFieldTokenCount - endEnumTokenCount - 1;
                        fieldEncoder = enumEncoder(offset, tokens, typeToken, cursor, lastValidValueIdx);
                        break;

                    case BEGIN_SET:
                        final int endSetTokenCount = 1;
                        final int lastChoiceIdx = nextFieldIdx - endFieldTokenCount - endSetTokenCount - 1;
                        fieldEncoder = bitSetEncoder(offset, tokens, cursor, lastChoiceIdx);
                        break;

                    case ENCODING:
                        fieldEncoder = encodedTypeEncoder(offset, typeToken, charGenerationMode);
                        break;

                    default:
                        break;
                }

                if (fieldEncoder != null)
                {
                    encoders.add(fieldEncoder.map(encoder -> (builder, buffer, off, limit) ->
                    {
                        builder.beginScope(memberToken.name());
                        encoder.encode(builder, buffer, off, limit);
                        builder.endScope();
                    }));
                }
            }

            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