package com.lambdaworks.redis;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.lambdaworks.redis.internal.LettuceAssert;
import com.lambdaworks.redis.protocol.CommandArgs;
import com.lambdaworks.redis.protocol.CommandType;
import com.lambdaworks.redis.protocol.LettuceCharsets;
import com.lambdaworks.redis.protocol.ProtocolKeyword;
/**
* Arguments and types for the {@code BITFIELD} command.
*
* @author Mark Paluch
* @since 4.2
*/
public class BitFieldArgs {
private List<SubCommand> commands;
/**
* Creates a new {@link BitFieldArgs} instance.
*/
public BitFieldArgs() {
this(new ArrayList<>());
}
private BitFieldArgs(List<SubCommand> commands) {
LettuceAssert.notNull(commands, "Commands must not be null");
this.commands = commands;
}
public static class Builder {
/**
* Utility constructor.
*/
private Builder() {
}
/**
* Adds a new {@link Get} subcommand.
*
* @param bitFieldType the bit field type, must not be {@literal null}.
* @param offset bitfield offset
* @return a new {@link Get} subcommand for the given {@code bitFieldType} and {@code offset}.
*/
public static BitFieldArgs get(BitFieldType bitFieldType, int offset) {
return new BitFieldArgs().get(bitFieldType, offset);
}
/**
* Adds a new {@link Set} subcommand.
*
* @param bitFieldType the bit field type, must not be {@literal null}.
* @param offset bitfield offset
* @param value the value
* @return a new {@link Set} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}.
*/
public static BitFieldArgs set(BitFieldType bitFieldType, int offset, long value) {
return new BitFieldArgs().set(bitFieldType, offset, value);
}
/**
* Adds a new {@link IncrBy} subcommand.
*
* @param bitFieldType the bit field type, must not be {@literal null}.
* @param offset bitfield offset
* @param value the value
* @return a new {@link IncrBy} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}.
*/
public static BitFieldArgs incrBy(BitFieldType bitFieldType, int offset, long value) {
return new BitFieldArgs().incrBy(bitFieldType, offset, value);
}
/**
* Adds a new {@link Overflow} subcommand.
*
* @param overflowType type of overflow, must not be {@literal null}.
* @return a new {@link Overflow} subcommand for the given {@code overflowType}.
*/
public static BitFieldArgs overflow(OverflowType overflowType) {
return new BitFieldArgs().overflow(overflowType);
}
}
/**
* Creates a new signed {@link BitFieldType} for the given number of {@code bits}.
*
* Redis allows up to {@code 64} bits for unsigned integers.
*
* @param bits
* @return
*/
public static BitFieldType signed(int bits) {
return new BitFieldType(true, bits);
}
/**
* Creates a new unsigned {@link BitFieldType} for the given number of {@code bits}. Redis allows up to {@code 63} bits for
* unsigned integers.
*
* @param bits
* @return
*/
public static BitFieldType unsigned(int bits) {
return new BitFieldType(false, bits);
}
/**
* Adds a new {@link SubCommand} to the {@code BITFIELD} execution.
*
* @param subCommand
*/
private BitFieldArgs addSubCommand(SubCommand subCommand) {
LettuceAssert.notNull(subCommand, "SubCommand must not be null");
commands.add(subCommand);
return this;
}
/**
* Adds a new {@link Get} subcommand using offset {@code 0} and the field type of the previous command.
*
* @return a new {@link Get} subcommand for the given {@code bitFieldType} and {@code offset}.
* @throws IllegalStateException if no previous field type was found
*/
public BitFieldArgs get() {
return get(previousFieldType());
}
/**
* Adds a new {@link Get} subcommand using offset {@code 0}.
*
* @param bitFieldType the bit field type, must not be {@literal null}.
* @return a new {@link Get} subcommand for the given {@code bitFieldType} and {@code offset}.
*/
public BitFieldArgs get(BitFieldType bitFieldType) {
return get(bitFieldType, 0);
}
/**
* Adds a new {@link Get} subcommand.
*
* @param bitFieldType the bit field type, must not be {@literal null}.
* @param offset bitfield offset
* @return a new {@link Get} subcommand for the given {@code bitFieldType} and {@code offset}.
*/
public BitFieldArgs get(BitFieldType bitFieldType, int offset) {
return addSubCommand(new Get(bitFieldType, offset));
}
/**
* Adds a new {@link Get} subcommand using the field type of the previous command.
*
* @param offset bitfield offset
* @return a new {@link Get} subcommand for the given {@code bitFieldType} and {@code offset}.
* @throws IllegalStateException if no previous field type was found
*/
public BitFieldArgs get(int offset) {
return get(previousFieldType(), offset);
}
/**
* Adds a new {@link Set} subcommand using offset {@code 0} and the field type of the previous command.
*
* @param value the value
* @return a new {@link Set} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}.
* @throws IllegalStateException if no previous field type was found
*/
public BitFieldArgs set(long value) {
return set(previousFieldType(), value);
}
/**
* Adds a new {@link Set} subcommand using offset {@code 0}.
*
* @param bitFieldType the bit field type, must not be {@literal null}.
* @param value the value
* @return a new {@link Set} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}.
*/
public BitFieldArgs set(BitFieldType bitFieldType, long value) {
return set(bitFieldType, 0, value);
}
/**
* Adds a new {@link Set} subcommand using the field type of the previous command.
*
* @param offset bitfield offset
* @param value the value
* @return a new {@link Set} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}.
* @throws IllegalStateException if no previous field type was found
*/
public BitFieldArgs set(int offset, long value) {
return set(previousFieldType(), offset, value);
}
/**
* Adds a new {@link Set} subcommand.
*
* @param bitFieldType the bit field type, must not be {@literal null}.
* @param offset bitfield offset
* @param value the value
* @return a new {@link Set} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}.
*/
public BitFieldArgs set(BitFieldType bitFieldType, int offset, long value) {
return addSubCommand(new Set(bitFieldType, offset, value));
}
/**
* Adds a new {@link IncrBy} subcommand using offset {@code 0} and the field type of the previous command.
*
* @param value the value
* @return a new {@link IncrBy} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}.
* @throws IllegalStateException if no previous field type was found
*/
public BitFieldArgs incrBy(long value) {
return incrBy(previousFieldType(), value);
}
/**
* Adds a new {@link IncrBy} subcommand using offset {@code 0}.
*
* @param bitFieldType the bit field type, must not be {@literal null}.
* @param value the value
* @return a new {@link IncrBy} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}.
*/
public BitFieldArgs incrBy(BitFieldType bitFieldType, long value) {
return incrBy(bitFieldType, 0, value);
}
/**
* Adds a new {@link IncrBy} subcommand using the field type of the previous command.
*
* @param offset bitfield offset
* @param value the value
* @return a new {@link IncrBy} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}.
* @throws IllegalStateException if no previous field type was found
*/
public BitFieldArgs incrBy(int offset, long value) {
return incrBy(previousFieldType(), offset, value);
}
/**
* Adds a new {@link IncrBy} subcommand.
*
* @param bitFieldType the bit field type, must not be {@literal null}.
* @param offset bitfield offset
* @param value the value
* @return a new {@link IncrBy} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}.
*/
public BitFieldArgs incrBy(BitFieldType bitFieldType, int offset, long value) {
return addSubCommand(new IncrBy(bitFieldType, offset, value));
}
/**
* Adds a new {@link Overflow} subcommand.
*
* @param overflowType type of overflow, must not be {@literal null}.
* @return a new {@link Overflow} subcommand for the given {@code overflowType}.
*/
public BitFieldArgs overflow(OverflowType overflowType) {
return addSubCommand(new Overflow(overflowType));
}
private BitFieldType previousFieldType() {
List<SubCommand> list = new ArrayList<>(commands);
Collections.reverse(list);
for (SubCommand command : list) {
if (command instanceof Get) {
return ((Get) command).bitFieldType;
}
if (command instanceof Set) {
return ((Set) command).bitFieldType;
}
if (command instanceof IncrBy) {
return ((IncrBy) command).bitFieldType;
}
}
throw new IllegalStateException("No previous field type found");
}
/**
* Representation for the {@code SET} subcommand for {@code BITFIELD}.
*/
private static class Set extends SubCommand {
private final BitFieldType bitFieldType;
private final long offset;
private final long value;
private Set(BitFieldType bitFieldType, int offset, long value) {
LettuceAssert.notNull(bitFieldType, "BitFieldType must not be null");
LettuceAssert.isTrue(offset > -1, "Offset must be greater or equal to 0");
this.offset = offset;
this.bitFieldType = bitFieldType;
this.value = value;
}
@Override
<K, V> void build(CommandArgs<K, V> args) {
args.add(CommandType.SET).add(bitFieldType.asString()).add(offset).add(value);
}
}
/**
* Representation for the {@code GET} subcommand for {@code BITFIELD}.
*/
private static class Get extends SubCommand {
private final BitFieldType bitFieldType;
private final long offset;
private Get(BitFieldType bitFieldType, int offset) {
LettuceAssert.notNull(bitFieldType, "BitFieldType must not be null");
LettuceAssert.isTrue(offset > -1, "Offset must be greater or equal to 0");
this.offset = offset;
this.bitFieldType = bitFieldType;
}
@Override
<K, V> void build(CommandArgs<K, V> args) {
args.add(CommandType.GET).add(bitFieldType.asString()).add(offset);
}
}
/**
* Representation for the {@code INCRBY} subcommand for {@code BITFIELD}.
*/
private static class IncrBy extends SubCommand {
private final BitFieldType bitFieldType;
private final long offset;
private final long value;
private IncrBy(BitFieldType bitFieldType, int offset, long value) {
LettuceAssert.notNull(bitFieldType, "BitFieldType must not be null");
LettuceAssert.isTrue(offset > -1, "Offset must be greater or equal to 0");
this.offset = offset;
this.bitFieldType = bitFieldType;
this.value = value;
}
@Override
<K, V> void build(CommandArgs<K, V> args) {
args.add(CommandType.INCRBY).add(bitFieldType.asString()).add(offset).add(value);
}
}
/**
* Representation for the {@code INCRBY} subcommand for {@code BITFIELD}.
*/
private static class Overflow extends SubCommand {
private final OverflowType overflowType;
private Overflow(OverflowType overflowType) {
LettuceAssert.notNull(overflowType, "OverflowType must not be null");
this.overflowType = overflowType;
}
@Override
<K, V> void build(CommandArgs<K, V> args) {
args.add("OVERFLOW").add(overflowType);
}
}
/**
* Base class for bitfield subcommands.
*/
private abstract static class SubCommand {
abstract <K, V> void build(CommandArgs<K, V> args);
}
<K, V> void build(CommandArgs<K, V> args) {
for (SubCommand command : commands) {
command.build(args);
}
}
/**
* Represents the overflow types for the {@code OVERFLOW} subcommand argument.
*/
public enum OverflowType implements ProtocolKeyword {
WRAP, SAT, FAIL;
public final byte[] bytes;
private OverflowType() {
bytes = name().getBytes(LettuceCharsets.ASCII);
}
@Override
public byte[] getBytes() {
return bytes;
}
}
/**
* Represents a bit field type with details about signed/unsigned and the number of bits.
*/
public static class BitFieldType {
private final boolean signed;
private final int bits;
private BitFieldType(boolean signed, int bits) {
LettuceAssert.isTrue(bits > 0, "Bits must be greater 0");
if (signed) {
LettuceAssert.isTrue(bits < 65, "Signed integers support only up to 64 bits");
} else {
LettuceAssert.isTrue(bits < 64, "Unsigned integers support only up to 63 bits");
}
this.signed = signed;
this.bits = bits;
}
/**
*
* @return {@literal true} if the bitfield type is signed.
*/
public boolean isSigned() {
return signed;
}
/**
*
* @return number of bits.
*/
public int getBits() {
return bits;
}
private String asString() {
return (signed ? "i" : "u") + bits;
}
}
}