diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index 3fabae061..3378d956b 100755 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -35,6 +35,7 @@ import org.redisson.core.Node; import org.redisson.core.NodesGroup; import org.redisson.core.RAtomicLong; import org.redisson.core.RBatch; +import org.redisson.core.RBitSet; import org.redisson.core.RBlockingQueue; import org.redisson.core.RBucket; import org.redisson.core.RCountDownLatch; @@ -394,6 +395,11 @@ public class Redisson implements RedissonClient { return new RedissonCountDownLatch(commandExecutor, name, id); } + @Override + public RBitSet getBitSet(String name) { + return new RedissonBitSet(commandExecutor, name); + } + /** * Returns keys operations. * Each of Redis/Redisson object associated with own key diff --git a/src/main/java/org/redisson/RedissonBitSet.java b/src/main/java/org/redisson/RedissonBitSet.java new file mode 100644 index 000000000..60e9bfb01 --- /dev/null +++ b/src/main/java/org/redisson/RedissonBitSet.java @@ -0,0 +1,137 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.redisson; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.List; + +import org.redisson.client.codec.ByteArrayCodec; +import org.redisson.client.protocol.RedisCommands; +import org.redisson.core.RBitSet; + +import io.netty.util.concurrent.Future; + +public class RedissonBitSet extends RedissonExpirable implements RBitSet { + + protected RedissonBitSet(CommandExecutor connectionManager, String name) { + super(connectionManager, name); + } + + public boolean get(int bitIndex) { + return get(getAsync(bitIndex)); + } + + public Future getAsync(int bitIndex) { + return commandExecutor.readAsync(getName(), codec, RedisCommands.GETBIT, getName(), bitIndex); + } + + public void set(int bitIndex) { + set(bitIndex, true); + } + + public void set(int fromIndex, int toIndex) { + CommandBatchExecutorService executorService = new CommandBatchExecutorService(commandExecutor.getConnectionManager()); + for (int i = fromIndex; i < toIndex; i++) { + executorService.writeAsync(getName(), codec, RedisCommands.SETBIT, getName(), i, 1); + } + executorService.execute(); + } + + public void set(int bitIndex, boolean value) { + get(setAsync(bitIndex, value)); + } + + public Future setAsync(int bitIndex, boolean value) { + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SETBIT, getName(), bitIndex, value ? 1 : 0); + } + + public byte[] toByteArray() { + return commandExecutor.read(getName(), ByteArrayCodec.INSTANCE, RedisCommands.GET, getName()); + } + + public int cardinality() { + return commandExecutor.read(getName(), codec, RedisCommands.BITCOUNT, getName()); + } + + public int size() { + int r = commandExecutor.read(getName(), codec, RedisCommands.STRLEN, getName()); + return r * 8; + } + + public void clear(int bitIndex) { + set(bitIndex, false); + } + + public void clear() { + delete(); + } + + public void or(String... bitSetNames) { + op("OR", bitSetNames); + } + + public void and(String... bitSetNames) { + op("AND", bitSetNames); + } + + public void xor(String... bitSetNames) { + op("XOR", bitSetNames); + } + + private void op(String op, String... bitSetNames) { + List params = new ArrayList(bitSetNames.length + 3); + params.add(op); + params.add(getName()); + params.add(getName()); + params.addAll(Arrays.asList(bitSetNames)); + commandExecutor.write(getName(), codec, RedisCommands.BITOP, params.toArray()); + } + + public BitSet asBitSet() { + return fromByteArrayReverse(toByteArray()); + } + + //Copied from: https://github.com/xetorthio/jedis/issues/301 + private static BitSet fromByteArrayReverse(byte[] bytes) { + BitSet bits = new BitSet(); + for (int i = 0; i < bytes.length * 8; i++) { + if ((bytes[i / 8] & (1 << (7 - (i % 8)))) != 0) { + bits.set(i); + } + } + return bits; + } + + //Copied from: https://github.com/xetorthio/jedis/issues/301 + private static byte[] toByteArrayReverse(BitSet bits) { + byte[] bytes = new byte[bits.length() / 8 + 1]; + for (int i = 0; i < bits.length(); i++) { + if (bits.get(i)) { + final int value = bytes[i / 8] | (1 << (7 - (i % 8))); + bytes[i / 8] = (byte) value; + } + } + return bytes; + } + + @Override + public String toString() { + return asBitSet().toString(); + } + +} diff --git a/src/main/java/org/redisson/RedissonClient.java b/src/main/java/org/redisson/RedissonClient.java index d614143ae..ac33d9480 100755 --- a/src/main/java/org/redisson/RedissonClient.java +++ b/src/main/java/org/redisson/RedissonClient.java @@ -24,6 +24,7 @@ import org.redisson.core.Node; import org.redisson.core.NodesGroup; import org.redisson.core.RAtomicLong; import org.redisson.core.RBatch; +import org.redisson.core.RBitSet; import org.redisson.core.RBlockingQueue; import org.redisson.core.RBucket; import org.redisson.core.RCountDownLatch; @@ -210,6 +211,8 @@ public interface RedissonClient { */ RCountDownLatch getCountDownLatch(String name); + RBitSet getBitSet(String name); + /** * Returns script operations object * diff --git a/src/main/java/org/redisson/client/codec/ByteArrayCodec.java b/src/main/java/org/redisson/client/codec/ByteArrayCodec.java new file mode 100644 index 000000000..db7568306 --- /dev/null +++ b/src/main/java/org/redisson/client/codec/ByteArrayCodec.java @@ -0,0 +1,76 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.redisson.client.codec; + +import java.io.IOException; + +import org.redisson.client.handler.State; +import org.redisson.client.protocol.Decoder; +import org.redisson.client.protocol.Encoder; + +import io.netty.buffer.ByteBuf; + +public class ByteArrayCodec implements Codec { + + public static final ByteArrayCodec INSTANCE = new ByteArrayCodec(); + + private final Encoder encoder = new Encoder() { + @Override + public byte[] encode(Object in) throws IOException { + return (byte[]) in; + } + }; + + private final Decoder decoder = new Decoder() { + @Override + public Object decode(ByteBuf buf, State state) { + byte[] result = new byte[buf.readableBytes()]; + buf.readBytes(result); + return result; + } + }; + + @Override + public Decoder getValueDecoder() { + return decoder; + } + + @Override + public Encoder getValueEncoder() { + return encoder; + } + + @Override + public Decoder getMapValueDecoder() { + return getValueDecoder(); + } + + @Override + public Encoder getMapValueEncoder() { + return getValueEncoder(); + } + + @Override + public Decoder getMapKeyDecoder() { + return getValueDecoder(); + } + + @Override + public Encoder getMapKeyEncoder() { + return getValueEncoder(); + } + +} diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index 103f1bf24..fa4064646 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -50,6 +50,12 @@ import org.redisson.client.protocol.pubsub.PubSubStatusDecoder; public interface RedisCommands { + RedisStrictCommand GETBIT = new RedisStrictCommand("GETBIT", new BooleanReplayConvertor()); + RedisStrictCommand STRLEN = new RedisStrictCommand("STRLEN", new IntegerReplayConvertor()); + RedisStrictCommand BITCOUNT = new RedisStrictCommand("BITCOUNT", new IntegerReplayConvertor()); + RedisStrictCommand SETBIT = new RedisStrictCommand("SETBIT", new VoidReplayConvertor()); + RedisStrictCommand BITOP = new RedisStrictCommand("BITOP", new VoidReplayConvertor()); + RedisStrictCommand ASKING = new RedisStrictCommand("ASKING", new VoidReplayConvertor()); RedisStrictCommand READONLY = new RedisStrictCommand("READONLY", new VoidReplayConvertor()); diff --git a/src/main/java/org/redisson/core/RBitSet.java b/src/main/java/org/redisson/core/RBitSet.java new file mode 100644 index 000000000..9388c03d6 --- /dev/null +++ b/src/main/java/org/redisson/core/RBitSet.java @@ -0,0 +1,60 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.redisson.core; + +import java.util.BitSet; + +import io.netty.util.concurrent.Future; + +/** + * Distributed alternative to the {@link java.util.concurrent.atomic.AtomicLong} + * + * @author Nikita Koksharov + * + */ +public interface RBitSet extends RExpirable { + + void set(int fromIndex, int toIndex); + + int size(); + + boolean get(int bitIndex); + + Future getAsync(int bitIndex); + + void set(int bitIndex); + + void set(int bitIndex, boolean value); + + Future setAsync(int bitIndex, boolean value); + + byte[] toByteArray(); + + int cardinality(); + + void clear(int bitIndex); + + void clear(); + + BitSet asBitSet(); + + void or(String... bitSetNames); + + void and(String... bitSetNames); + + void xor(String... bitSetNames); + +}