private static Arbitrary<Encoder> messageValueEncoder(
        final Ir ir,
        final short messageId,
        final CharGenerationMode charGenerationMode)
    {
        final List<Token> tokens = ir.getMessage(messageId);
        final MutableInteger cursor = new MutableInteger(1);

        final Token token = tokens.get(0);
        if (BEGIN_MESSAGE != token.signal())
        {
            throw new IllegalArgumentException("Expected BEGIN_MESSAGE token");
        }

        final Arbitrary<Encoder> fieldsEncoder = fieldsEncoder(
            tokens, cursor, tokens.size() - 1, true, charGenerationMode);
        final Arbitrary<Encoder> groupsEncoder = groupsEncoder(
            tokens, cursor, tokens.size() - 1, charGenerationMode);
        final Arbitrary<Encoder> varDataEncoder = varDataEncoder(
            tokens, cursor, tokens.size() - 1, charGenerationMode);
        return Combinators.combine(fieldsEncoder, groupsEncoder, varDataEncoder)
            .as((fields, groups, varData) -> (builder, buffer, offset, limit) ->
            {
                final int blockLength = token.encodedLength();
                buffer.putShort(0, (short)blockLength, ir.byteOrder());
                buffer.putShort(2, messageId, ir.byteOrder());
                buffer.putShort(4, (short)ir.id(), ir.byteOrder());
                buffer.putShort(6, (short)ir.version(), ir.byteOrder());
                final int headerLength = 8;
                fields.encode(builder, buffer, offset + headerLength, null);
                final int oldLimit = limit.get();
                buffer.checkLimit(offset + headerLength + blockLength);
                limit.set(offset + headerLength + blockLength);
                builder.appendLine().append("limit: ").append(oldLimit).append(" -> ").append(limit.get());
                groups.encode(builder, buffer, NULL_VALUE, limit);
                varData.encode(builder, buffer, NULL_VALUE, limit);
            });
    }
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