http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRangeByScoreExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRangeByScoreExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRangeByScoreExecutor.java new file mode 100644 index 0000000..8d0ecb2 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRangeByScoreExecutor.java @@ -0,0 +1,214 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.query.FunctionDomainException; +import org.apache.geode.cache.query.NameResolutionException; +import org.apache.geode.cache.query.Query; +import org.apache.geode.cache.query.QueryInvocationTargetException; +import org.apache.geode.cache.query.SelectResults; +import org.apache.geode.cache.query.Struct; +import org.apache.geode.cache.query.TypeMismatchException; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.DoubleWrapper; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.Extendable; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; +import org.apache.geode.redis.internal.executor.SortedSetQuery; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +public class ZRangeByScoreExecutor extends SortedSetExecutor implements Extendable { + + private final String ERROR_NOT_NUMERIC = "The number provided is not numeric"; + + private final String ERROR_LIMIT = "The offset and count cannot be negative"; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + if (commandElems.size() < 4) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), getArgsError())); + return; + } + + boolean withScores = false; + byte[] elem4Array = null; + int offset = 0; + int limit = -1; + if (commandElems.size() >= 5) { + elem4Array = commandElems.get(4); + String elem4 = Coder.bytesToString(elem4Array); + int limitIndex = 4; + if (elem4.equalsIgnoreCase("WITHSCORES")) { + withScores = true; + limitIndex++; + } + + if (commandElems.size() >= limitIndex + 2) { + String limitString = Coder.bytesToString(commandElems.get(limitIndex)); + if (limitString.equalsIgnoreCase("LIMIT")) { + try { + byte[] offsetArray = commandElems.get(limitIndex + 1); + byte[] limitArray = commandElems.get(limitIndex + 2); + offset = Coder.bytesToInt(offsetArray); + limit = Coder.bytesToInt(limitArray); + } catch (NumberFormatException e) { + command.setResponse( + Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_NUMERIC)); + return; + } + } + + if (offset < 0 || limit < 0) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_LIMIT)); + return; + } + + if (limitIndex == 4 && commandElems.size() >= 8) { + byte[] lastElemArray = commandElems.get(7); + String lastString = Coder.bytesToString(lastElemArray); + if (lastString.equalsIgnoreCase("WITHSCORES")) { + withScores = true; + } + } + } + + } + + ByteArrayWrapper key = command.getKey(); + + checkDataType(key, RedisDataType.REDIS_SORTEDSET, context); + Region<ByteArrayWrapper, DoubleWrapper> keyRegion = getRegion(context, key); + + if (keyRegion == null) { + command.setResponse(Coder.getEmptyArrayResponse(context.getByteBufAllocator())); + return; + } + + int startIndex = isReverse() ? 3 : 2; + int stopIndex = isReverse() ? 2 : 3; + boolean startInclusive = true; + boolean stopInclusive = true; + double start; + double stop; + + byte[] startArray = commandElems.get(startIndex); + byte[] stopArray = commandElems.get(stopIndex); + String startString = Coder.bytesToString(startArray); + String stopString = Coder.bytesToString(stopArray); + + if (startArray[0] == Coder.OPEN_BRACE_ID) { + startString = startString.substring(1); + startInclusive = false; + } + if (stopArray[0] == Coder.OPEN_BRACE_ID) { + stopString = stopString.substring(1); + stopInclusive = false; + } + + try { + start = Coder.stringToDouble(startString); + stop = Coder.stringToDouble(stopString); + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_NUMERIC)); + return; + } + + Collection<?> list; + try { + list = getKeys(key, keyRegion, context, start, stop, startInclusive, stopInclusive, offset, + limit); + } catch (Exception e) { + throw new RuntimeException(e); + } + + if (list == null) + command.setResponse(Coder.getEmptyArrayResponse(context.getByteBufAllocator())); + else + command.setResponse(Coder.zRangeResponse(context.getByteBufAllocator(), list, withScores)); + } + + private Collection<?> getKeys(ByteArrayWrapper key, + Region<ByteArrayWrapper, DoubleWrapper> keyRegion, ExecutionHandlerContext context, + double start, double stop, boolean startInclusive, boolean stopInclusive, int offset, + int limit) throws FunctionDomainException, TypeMismatchException, NameResolutionException, + QueryInvocationTargetException { + if (start == Double.POSITIVE_INFINITY || stop == Double.NEGATIVE_INFINITY || start > stop + || (start == stop && (!startInclusive || !stopInclusive))) + return null; + if (start == Double.NEGATIVE_INFINITY && stop == Double.POSITIVE_INFINITY) + return new HashSet(keyRegion.entrySet()); + + Query query; + Object[] params; + if (isReverse()) { + if (startInclusive) { + if (stopInclusive) { + query = getQuery(key, SortedSetQuery.ZREVRBSSTISI, context); + } else { + query = getQuery(key, SortedSetQuery.ZREVRBSSTI, context); + } + } else { + if (stopInclusive) { + query = getQuery(key, SortedSetQuery.ZREVRBSSI, context); + } else { + query = getQuery(key, SortedSetQuery.ZREVRBS, context); + } + } + params = new Object[] {start, stop, INFINITY_LIMIT}; + } else { + if (startInclusive) { + if (stopInclusive) { + query = getQuery(key, SortedSetQuery.ZRBSSTISI, context); + } else { + query = getQuery(key, SortedSetQuery.ZRBSSTI, context); + } + } else { + if (stopInclusive) { + query = getQuery(key, SortedSetQuery.ZRBSSI, context); + } else { + query = getQuery(key, SortedSetQuery.ZRBS, context); + } + } + params = new Object[] {start, stop, INFINITY_LIMIT}; + } + if (limit > 0) + params[params.length - 1] = (limit + offset); + + SelectResults<?> results = (SelectResults<?>) query.execute(params); + if (offset < results.size()) + return (Collection<Struct>) results.asList().subList(offset, results.size()); + else + return null; + } + + protected boolean isReverse() { + return false; + } + + @Override + public String getArgsError() { + return ArityDef.ZRANGEBYSCORE; + } + +}
http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRangeExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRangeExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRangeExecutor.java new file mode 100644 index 0000000..d316faa --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRangeExecutor.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.query.Query; +import org.apache.geode.cache.query.SelectResults; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.DoubleWrapper; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.Extendable; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; +import org.apache.geode.redis.internal.executor.SortedSetQuery; + +import java.util.List; + +public class ZRangeExecutor extends SortedSetExecutor implements Extendable { + + private final String ERROR_NOT_NUMERIC = "The index provided is not numeric"; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + if (commandElems.size() < 4) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), getArgsError())); + return; + } + + boolean withScores = false; + + if (commandElems.size() >= 5) { + byte[] fifthElem = commandElems.get(4); + withScores = Coder.bytesToString(fifthElem).equalsIgnoreCase("WITHSCORES"); + + } + + ByteArrayWrapper key = command.getKey(); + + checkDataType(key, RedisDataType.REDIS_SORTEDSET, context); + Region<ByteArrayWrapper, DoubleWrapper> keyRegion = getRegion(context, key); + + if (keyRegion == null) { + command.setResponse(Coder.getEmptyArrayResponse(context.getByteBufAllocator())); + return; + } + + + int start; + int stop; + int sSetSize = keyRegion.size(); + + try { + byte[] startArray = commandElems.get(2); + byte[] stopArray = commandElems.get(3); + start = Coder.bytesToInt(startArray); + stop = Coder.bytesToInt(stopArray); + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_NUMERIC)); + return; + } + + start = getBoundedStartIndex(start, sSetSize); + stop = getBoundedEndIndex(stop, sSetSize); + + if (start > stop || start == sSetSize) { + command.setResponse(Coder.getEmptyArrayResponse(context.getByteBufAllocator())); + return; + } + if (stop == sSetSize) + stop--; + List<?> list; + try { + list = getRange(context, key, start, stop); + } catch (Exception e) { + throw new RuntimeException(e); + } + + command.setResponse(Coder.zRangeResponse(context.getByteBufAllocator(), list, withScores)); + } + + private List<?> getRange(ExecutionHandlerContext context, ByteArrayWrapper key, int start, + int stop) throws Exception { + Query query; + + if (isReverse()) + query = getQuery(key, SortedSetQuery.ZRANGE, context); + else + query = getQuery(key, SortedSetQuery.ZREVRANGE, context); + + Object[] params = {stop + 1}; + + SelectResults<?> results = (SelectResults<?>) query.execute(params); + + List<?> list = results.asList(); + + return list.subList(start, stop + 1); + + } + + protected boolean isReverse() { + return false; + } + + @Override + public String getArgsError() { + return ArityDef.ZRANGE; + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRankExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRankExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRankExecutor.java new file mode 100644 index 0000000..d3b6bee --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRankExecutor.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.query.Query; +import org.apache.geode.cache.query.SelectResults; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.DoubleWrapper; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.Extendable; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; +import org.apache.geode.redis.internal.executor.SortedSetQuery; + +import java.util.List; + +public class ZRankExecutor extends SortedSetExecutor implements Extendable { + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + if (commandElems.size() < 3) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), getArgsError())); + return; + } + + ByteArrayWrapper key = command.getKey(); + + checkDataType(key, RedisDataType.REDIS_SORTEDSET, context); + Region<ByteArrayWrapper, DoubleWrapper> keyRegion = getRegion(context, key); + + if (keyRegion == null) { + command.setResponse(Coder.getNilResponse(context.getByteBufAllocator())); + return; + } + + ByteArrayWrapper member = new ByteArrayWrapper(commandElems.get(2)); + + DoubleWrapper value = keyRegion.get(member); + + if (value == null) { + command.setResponse(Coder.getNilResponse(context.getByteBufAllocator())); + return; + } + + int rank; + try { + rank = getRange(context, key, member, value); + } catch (Exception e) { + throw new RuntimeException(e); + } + + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), rank)); + } + + private int getRange(ExecutionHandlerContext context, ByteArrayWrapper key, + ByteArrayWrapper member, DoubleWrapper valueWrapper) throws Exception { + Query query; + if (isReverse()) + query = getQuery(key, SortedSetQuery.ZREVRANK, context); + else + query = getQuery(key, SortedSetQuery.ZRANK, context); + + Object[] params = {valueWrapper.score, valueWrapper.score, member}; + + SelectResults<?> results = (SelectResults<?>) query.execute(params); + + return (Integer) results.asList().get(0); + + } + + protected boolean isReverse() { + return false; + } + + @Override + public String getArgsError() { + return ArityDef.ZRANK; + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemExecutor.java new file mode 100644 index 0000000..93e0c32 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemExecutor.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.DoubleWrapper; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; + +import java.util.List; + +public class ZRemExecutor extends SortedSetExecutor { + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + if (commandElems.size() < 3) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ZREM)); + return; + } + + ByteArrayWrapper key = command.getKey(); + + checkDataType(key, RedisDataType.REDIS_SORTEDSET, context); + Region<ByteArrayWrapper, DoubleWrapper> keyRegion = getRegion(context, key); + + if (keyRegion == null) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), 0)); + return; + } + + int numDeletedMembers = 0; + + for (int i = 2; i < commandElems.size(); i++) { + byte[] memberArray = commandElems.get(i); + ByteArrayWrapper member = new ByteArrayWrapper(memberArray); + Object oldVal = keyRegion.remove(member); + if (oldVal != null) + numDeletedMembers++; + } + if (keyRegion.isEmpty()) + context.getRegionProvider().removeKey(key); + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), numDeletedMembers)); + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByLexExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByLexExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByLexExecutor.java new file mode 100644 index 0000000..73f380b --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByLexExecutor.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.query.Query; +import org.apache.geode.cache.query.SelectResults; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.DoubleWrapper; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; +import org.apache.geode.redis.internal.executor.SortedSetQuery; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class ZRemRangeByLexExecutor extends SortedSetExecutor { + + private final int ERROR_NOT_EXISTS = 0; + + private final String ERROR_ILLEGAL_SYNTAX = + "The min and max strings must either start with a (, [ or be - or +"; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + if (commandElems.size() < 4) { + command.setResponse( + Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ZREMRANGEBYLEX)); + return; + } + + ByteArrayWrapper key = command.getKey(); + + checkDataType(key, RedisDataType.REDIS_SORTEDSET, context); + Region<ByteArrayWrapper, DoubleWrapper> keyRegion = getRegion(context, key); + + if (keyRegion == null) { + command + .setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), ERROR_NOT_EXISTS)); + return; + } + + boolean minInclusive = false; + boolean maxInclusive = false; + + byte[] minArray = commandElems.get(2); + byte[] maxArray = commandElems.get(3); + String startString = Coder.bytesToString(minArray); + String stopString = Coder.bytesToString(maxArray); + + if (minArray[0] == Coder.OPEN_BRACE_ID) { + startString = startString.substring(1); + minInclusive = false; + } else if (minArray[0] == Coder.OPEN_BRACKET_ID) { + startString = startString.substring(1); + minInclusive = true; + } else if (minArray[0] != Coder.HYPHEN_ID) { + command + .setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_ILLEGAL_SYNTAX)); + return; + } + + if (maxArray[0] == Coder.OPEN_BRACE_ID) { + stopString = stopString.substring(1); + maxInclusive = false; + } else if (maxArray[0] == Coder.OPEN_BRACKET_ID) { + stopString = stopString.substring(1); + maxInclusive = true; + } else if (maxArray[0] != Coder.PLUS_ID) { + command + .setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_ILLEGAL_SYNTAX)); + return; + } + + Collection<ByteArrayWrapper> removeList; + try { + removeList = getRange(key, keyRegion, context, Coder.stringToByteArrayWrapper(startString), + Coder.stringToByteArrayWrapper(stopString), minInclusive, maxInclusive); + } catch (Exception e) { + throw new RuntimeException(e); + } + + int numRemoved = 0; + + for (ByteArrayWrapper entry : removeList) { + Object oldVal = keyRegion.remove(entry); + if (oldVal != null) + numRemoved++; + } + + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), numRemoved)); + } + + private Collection<ByteArrayWrapper> getRange(ByteArrayWrapper key, + Region<ByteArrayWrapper, DoubleWrapper> keyRegion, ExecutionHandlerContext context, + ByteArrayWrapper start, ByteArrayWrapper stop, boolean startInclusive, boolean stopInclusive) + throws Exception { + if (start.equals("-") && stop.equals("+")) + return new ArrayList<ByteArrayWrapper>(keyRegion.keySet()); + else if (start.equals("+") || stop.equals("-")) + return null; + + Query query; + Object[] params; + if (start.equals("-")) { + if (stopInclusive) { + query = getQuery(key, SortedSetQuery.ZRANGEBYLEXNINFI, context); + } else { + query = getQuery(key, SortedSetQuery.ZRANGEBYLEXNINF, context); + } + params = new Object[] {stop, INFINITY_LIMIT}; + } else if (stop.equals("+")) { + if (startInclusive) { + query = getQuery(key, SortedSetQuery.ZRANGEBYLEXPINFI, context); + } else { + query = getQuery(key, SortedSetQuery.ZRANGEBYLEXPINF, context); + } + params = new Object[] {start, INFINITY_LIMIT}; + } else { + if (startInclusive) { + if (stopInclusive) { + query = getQuery(key, SortedSetQuery.ZRANGEBYLEXSTISI, context); + } else { + query = getQuery(key, SortedSetQuery.ZRANGEBYLEXSTI, context); + } + } else { + if (stopInclusive) { + query = getQuery(key, SortedSetQuery.ZRANGEBYLEXSI, context); + } else { + query = getQuery(key, SortedSetQuery.ZRANGEBYLEX, context); + } + } + params = new Object[] {start, stop, INFINITY_LIMIT}; + } + + @SuppressWarnings("unchecked") + SelectResults<ByteArrayWrapper> results = + (SelectResults<ByteArrayWrapper>) query.execute(params); + + return results.asList(); + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByRankExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByRankExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByRankExecutor.java new file mode 100644 index 0000000..abd2a96 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByRankExecutor.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.Region.Entry; +import org.apache.geode.cache.query.Query; +import org.apache.geode.cache.query.SelectResults; +import org.apache.geode.cache.query.Struct; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.DoubleWrapper; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; +import org.apache.geode.redis.internal.executor.SortedSetQuery; + +import java.util.List; + +public class ZRemRangeByRankExecutor extends SortedSetExecutor { + + private final int NONE_REMOVED = 0; + + private final String ERROR_NOT_NUMERIC = "The index provided is not numeric"; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + if (commandElems.size() < 4) { + command.setResponse( + Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ZREMRANGEBYRANK)); + return; + } + + ByteArrayWrapper key = command.getKey(); + + checkDataType(key, RedisDataType.REDIS_SORTEDSET, context); + Region<ByteArrayWrapper, DoubleWrapper> keyRegion = getRegion(context, key); + + if (keyRegion == null) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), NONE_REMOVED)); + return; + } + + int startRank; + int stopRank; + + try { + startRank = Coder.bytesToInt(commandElems.get(2)); + stopRank = Coder.bytesToInt(commandElems.get(3)); + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_NUMERIC)); + return; + } + + int sSetSize = keyRegion.size(); + + startRank = getBoundedStartIndex(startRank, sSetSize); + stopRank = getBoundedEndIndex(stopRank, sSetSize); + if (stopRank > sSetSize - 1) + stopRank = sSetSize - 1; + + if (startRank > stopRank) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), 0)); + return; + } + + int numRemoved = 0; + List<?> removeList = null; + try { + if (startRank == 0 && stopRank == sSetSize - 1) { + numRemoved = keyRegion.size(); + context.getRegionProvider().removeKey(key); + } else { + removeList = getRemoveKeys(context, key, startRank, stopRank); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + if (removeList != null) { + for (Object entry : removeList) { + ByteArrayWrapper removeKey; + if (entry instanceof Entry) + removeKey = (ByteArrayWrapper) ((Entry<?, ?>) entry).getKey(); + else + removeKey = (ByteArrayWrapper) ((Struct) entry).getFieldValues()[0]; + Object oldVal = keyRegion.remove(removeKey); + if (oldVal != null) + numRemoved++; + } + if (keyRegion.isEmpty()) + context.getRegionProvider().removeKey(key); + } + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), numRemoved)); + } + + private List<?> getRemoveKeys(ExecutionHandlerContext context, ByteArrayWrapper key, + int startRank, int stopRank) throws Exception { + Query query = getQuery(key, SortedSetQuery.ZREMRANGEBYRANK, context); + Object[] params = {stopRank + 1}; + + SelectResults<?> results = (SelectResults<?>) query.execute(params); + + return results.asList().subList(startRank, stopRank + 1); + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByScoreExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByScoreExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByScoreExecutor.java new file mode 100644 index 0000000..514748d --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRemRangeByScoreExecutor.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.query.Query; +import org.apache.geode.cache.query.SelectResults; +import org.apache.geode.cache.query.Struct; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.DoubleWrapper; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; +import org.apache.geode.redis.internal.executor.SortedSetQuery; + +import java.util.Collection; +import java.util.List; +import java.util.Map.Entry; + +public class ZRemRangeByScoreExecutor extends SortedSetExecutor { + + private final String ERROR_NOT_NUMERIC = "The number provided is not numeric"; + + private final int NOT_EXISTS = 0; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + if (commandElems.size() < 4) { + command.setResponse( + Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ZREMRANGEBYSCORE)); + return; + } + + ByteArrayWrapper key = command.getKey(); + + checkDataType(key, RedisDataType.REDIS_SORTEDSET, context); + Region<ByteArrayWrapper, DoubleWrapper> keyRegion = getRegion(context, key); + + if (keyRegion == null) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), NOT_EXISTS)); + return; + } + + boolean startInclusive = true; + boolean stopInclusive = true; + double start; + double stop; + + byte[] startArray = commandElems.get(2); + byte[] stopArray = commandElems.get(3); + String startString = Coder.bytesToString(startArray); + String stopString = Coder.bytesToString(stopArray); + if (startArray[0] == Coder.OPEN_BRACE_ID) { + startString = startString.substring(1); + startInclusive = false; + } + if (stopArray[0] == Coder.OPEN_BRACE_ID) { + stopString = stopString.substring(1); + stopInclusive = false; + } + + try { + start = Coder.stringToDouble(startString); + stop = Coder.stringToDouble(stopString); + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_NUMERIC)); + return; + } + + int numRemoved = 0; + + Collection<?> removeList = null; + try { + if (start == Double.NEGATIVE_INFINITY && stop == Double.POSITIVE_INFINITY && startInclusive + && stopInclusive) { + numRemoved = keyRegion.size(); + context.getRegionProvider().removeKey(key); + } else { + removeList = getKeys(context, key, keyRegion, start, stop, startInclusive, stopInclusive); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + if (removeList != null) { + for (Object entry : removeList) { + ByteArrayWrapper remove = null; + if (entry instanceof Entry) + remove = (ByteArrayWrapper) ((Entry<?, ?>) entry).getKey(); + else if (entry instanceof Struct) + remove = (ByteArrayWrapper) ((Struct) entry).getFieldValues()[0]; + Object oldVal = keyRegion.remove(remove); + if (oldVal != null) + numRemoved++; + if (keyRegion.isEmpty()) + context.getRegionProvider().removeKey(key); + } + } + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), numRemoved)); + } + + private Collection<?> getKeys(ExecutionHandlerContext context, ByteArrayWrapper key, + Region<ByteArrayWrapper, DoubleWrapper> keyRegion, double start, double stop, + boolean startInclusive, boolean stopInclusive) throws Exception { + if (start == Double.POSITIVE_INFINITY || stop == Double.NEGATIVE_INFINITY || (start > stop)) + return null; + + Query query; + Object[] params; + if (startInclusive) { + if (stopInclusive) { + query = getQuery(key, SortedSetQuery.ZRBSSTISI, context); + } else { + query = getQuery(key, SortedSetQuery.ZRBSSTI, context); + } + } else { + if (stopInclusive) { + query = getQuery(key, SortedSetQuery.ZRBSSI, context); + } else { + query = getQuery(key, SortedSetQuery.ZRBS, context); + } + } + params = new Object[] {start, stop, INFINITY_LIMIT}; + + SelectResults<?> results = (SelectResults<?>) query.execute(params); + + return (Collection<?>) results.asList(); + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRangeByScoreExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRangeByScoreExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRangeByScoreExecutor.java new file mode 100644 index 0000000..652e784 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRangeByScoreExecutor.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.redis.internal.RedisConstants.ArityDef; + + +public class ZRevRangeByScoreExecutor extends ZRangeByScoreExecutor { + + @Override + protected boolean isReverse() { + return true; + } + + @Override + public String getArgsError() { + return ArityDef.ZREVRANGEBYSCORE; + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRangeExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRangeExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRangeExecutor.java new file mode 100644 index 0000000..8154570 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRangeExecutor.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.redis.internal.RedisConstants.ArityDef; + + +public class ZRevRangeExecutor extends ZRangeExecutor { + + @Override + protected boolean isReverse() { + return true; + } + + @Override + public String getArgsError() { + return ArityDef.ZREVRANGE; + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRankExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRankExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRankExecutor.java new file mode 100644 index 0000000..80ebfa4 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZRevRankExecutor.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.redis.internal.RedisConstants.ArityDef; + +public class ZRevRankExecutor extends ZRankExecutor { + + @Override + protected boolean isReverse() { + return true; + } + + @Override + public String getArgsError() { + return ArityDef.ZREVRANK; + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScanExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScanExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScanExecutor.java new file mode 100644 index 0000000..1d50f17 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScanExecutor.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.DoubleWrapper; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; +import org.apache.geode.redis.internal.executor.AbstractScanExecutor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class ZScanExecutor extends AbstractScanExecutor { + + @SuppressWarnings("unchecked") + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + if (commandElems.size() < 3) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ZSCAN)); + return; + } + + ByteArrayWrapper key = command.getKey(); + Region<ByteArrayWrapper, DoubleWrapper> keyRegion = + (Region<ByteArrayWrapper, DoubleWrapper>) context.getRegionProvider().getRegion(key); + checkDataType(key, RedisDataType.REDIS_SORTEDSET, context); + if (keyRegion == null) { + command.setResponse( + Coder.getScanResponse(context.getByteBufAllocator(), new ArrayList<String>())); + return; + } + byte[] cAr = commandElems.get(2); + // String cursorString = ResponseToByteEncoder.bytesToString(cAr); + + int cursor = 0; + Pattern matchPattern = null; + String globMatchPattern = null; + int count = DEFUALT_COUNT; + try { + cursor = Coder.bytesToInt(cAr); + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_CURSOR)); + return; + } + if (cursor < 0) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_CURSOR)); + return; + } + + if (commandElems.size() > 4) { + try { + byte[] bytes = commandElems.get(3); + if (Coder.bytesToString(bytes).equalsIgnoreCase("MATCH")) { + bytes = commandElems.get(4); + globMatchPattern = Coder.bytesToString(bytes); + } else if (Coder.bytesToString(bytes).equalsIgnoreCase("COUNT")) { + bytes = commandElems.get(4); + count = Coder.bytesToInt(bytes); + } + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_COUNT)); + return; + } + } + + if (commandElems.size() > 6) { + try { + byte[] bytes = commandElems.get(5); + if (Coder.bytesToString(bytes).equalsIgnoreCase("COUNT")) { + bytes = commandElems.get(6); + count = Coder.bytesToInt(bytes); + } + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_COUNT)); + return; + } + } + + if (count < 0) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_COUNT)); + return; + } + + try { + matchPattern = convertGlobToRegex(globMatchPattern); + } catch (PatternSyntaxException e) { + command.setResponse( + Coder.getErrorResponse(context.getByteBufAllocator(), RedisConstants.ERROR_ILLEGAL_GLOB)); + return; + } + + List<ByteArrayWrapper> returnList = + (List<ByteArrayWrapper>) getIteration(new HashSet(keyRegion.entrySet()), matchPattern, + count, cursor); + + command.setResponse(Coder.getScanResponse(context.getByteBufAllocator(), returnList)); + } + + @SuppressWarnings("unchecked") + @Override + protected List<?> getIteration(Collection<?> list, Pattern matchPattern, int count, int cursor) { + List<Object> returnList = new ArrayList<Object>(); + int size = list.size(); + int beforeCursor = 0; + int numElements = 0; + int i = -1; + for (Entry<ByteArrayWrapper, DoubleWrapper> entry : (Collection<Entry<ByteArrayWrapper, DoubleWrapper>>) list) { + ByteArrayWrapper keyWrapper = entry.getKey(); + String key = keyWrapper.toString(); + + DoubleWrapper value = entry.getValue(); + i++; + if (beforeCursor < cursor) { + beforeCursor++; + continue; + } else if (numElements < count) { + if (matchPattern != null) { + if (matchPattern.matcher(key).matches()) { + returnList.add(keyWrapper); + returnList.add(value.toString()); + numElements++; + } + } else { + returnList.add(keyWrapper); + returnList.add(value.toString()); + numElements++; + } + } else + break; + } + + if (i == size - 1) + returnList.add(0, String.valueOf(0)); + else + returnList.add(0, String.valueOf(i)); + return returnList; + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScoreExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScoreExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScoreExecutor.java new file mode 100644 index 0000000..dbdaa0d --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScoreExecutor.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.sortedset; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.DoubleWrapper; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; + +import java.util.List; + +public class ZScoreExecutor extends SortedSetExecutor { + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + if (commandElems.size() < 3) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ZSCORE)); + return; + } + + ByteArrayWrapper key = command.getKey(); + ByteArrayWrapper member = new ByteArrayWrapper(commandElems.get(2)); + + checkDataType(key, RedisDataType.REDIS_SORTEDSET, context); + Region<ByteArrayWrapper, DoubleWrapper> keyRegion = getRegion(context, key); + + if (keyRegion == null) { + command.setResponse(Coder.getNilResponse(context.getByteBufAllocator())); + return; + } + DoubleWrapper score = keyRegion.get(member); + if (score == null) { + command.setResponse(Coder.getNilResponse(context.getByteBufAllocator())); + return; + } + command + .setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), score.toString())); + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/AppendExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/AppendExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/AppendExecutor.java new file mode 100644 index 0000000..7bd12d6 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/AppendExecutor.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.string; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; + +import java.util.List; + +public class AppendExecutor extends StringExecutor { + + private final int VALUE_INDEX = 2; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();; + + if (commandElems.size() < 3) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.APPEND)); + return; + } + + ByteArrayWrapper key = command.getKey(); + checkAndSetDataType(key, context); + ByteArrayWrapper string = r.get(key); + + byte[] stringByteArray = commandElems.get(VALUE_INDEX); + if (string == null) { + r.put(key, new ByteArrayWrapper(stringByteArray)); + command.setResponse( + Coder.getIntegerResponse(context.getByteBufAllocator(), stringByteArray.length)); + } else { + byte[] newValue = concatArrays(string.toBytes(), stringByteArray); + string.setBytes(newValue); + r.put(key, string); + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), newValue.length)); + } + + } + + private byte[] concatArrays(byte[] o, byte[] n) { + int oLen = o.length; + int nLen = n.length; + byte[] combined = new byte[oLen + nLen]; + System.arraycopy(o, 0, combined, 0, oLen); + System.arraycopy(n, 0, combined, oLen, nLen); + return combined; + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitCountExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitCountExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitCountExecutor.java new file mode 100644 index 0000000..7461eea --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitCountExecutor.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.string; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; + +import java.util.List; + +public class BitCountExecutor extends StringExecutor { + + private final String ERROR_NOT_INT = "The indexes provided must be numeric values"; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion(); + + if (commandElems.size() != 2 && commandElems.size() != 4) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.BITCOUNT)); + return; + } + + ByteArrayWrapper key = command.getKey(); + checkAndSetDataType(key, context); + ByteArrayWrapper wrapper = r.get(key); + if (wrapper == null) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), 0)); + return; + } + byte[] value = wrapper.toBytes(); + + long startL = 0; + long endL = value.length - 1; + + if (commandElems.size() == 4) { + try { + startL = Coder.bytesToLong(commandElems.get(2)); + endL = Coder.bytesToLong(commandElems.get(3)); + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_INT)); + return; + } + } + if (startL > Integer.MAX_VALUE || endL > Integer.MAX_VALUE) { + command.setResponse( + Coder.getErrorResponse(context.getByteBufAllocator(), RedisConstants.ERROR_OUT_OF_RANGE)); + return; + } + + int start = (int) startL; + int end = (int) endL; + if (start < 0) + start += value.length; + if (end < 0) + end += value.length; + + if (start < 0) + start = 0; + if (end < 0) + end = 0; + + if (end > value.length - 1) + end = value.length - 1; + + if (end < start || start >= value.length) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), 0)); + return; + } + + long setBits = 0; + for (int j = start; j <= end; j++) + setBits += Integer.bitCount(0xFF & value[j]); // 0xFF keeps same bit sequence as the byte as + // opposed to keeping the same value + + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), setBits)); + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitOpExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitOpExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitOpExecutor.java new file mode 100644 index 0000000..d32a996 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitOpExecutor.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.string; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; + +import java.util.List; + +public class BitOpExecutor extends StringExecutor { + + private static final String ERROR_NO_SUCH_OP = "Please specify a legal operation"; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion(); + + if (commandElems.size() < 4) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.BITOP)); + return; + } + + String operation = command.getStringKey().toUpperCase(); + ByteArrayWrapper destKey = new ByteArrayWrapper(commandElems.get(2)); + checkDataType(destKey, context); + + byte[][] values = new byte[commandElems.size() - 3][]; + int maxLength = 0; + for (int i = 3; i < commandElems.size(); i++) { + ByteArrayWrapper key = new ByteArrayWrapper(commandElems.get(i)); + checkDataType(key, context); + ByteArrayWrapper value = r.get(key); + if (value == null) { + values[i - 3] = null; + continue; + } + + byte[] val = value.toBytes(); + values[i - 3] = val; + if (val.length > maxLength) { + maxLength = val.length; + byte[] tmp = values[0]; + values[0] = val; + values[i - 3] = tmp; + } + if (i == 3 && operation.equalsIgnoreCase("NOT")) + break; + } + + + if (operation.equals("AND")) + and(context, r, destKey, values, maxLength); + else if (operation.equals("OR")) + or(context, r, destKey, values, maxLength); + else if (operation.equals("XOR")) + xor(context, r, destKey, values, maxLength); + else if (operation.equals("NOT")) + not(context, r, destKey, values, maxLength); + else { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NO_SUCH_OP)); + return; + } + + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), maxLength)); + } + + private void and(ExecutionHandlerContext context, Region<ByteArrayWrapper, ByteArrayWrapper> r, + ByteArrayWrapper destKey, byte[][] values, int max) { + byte[] dest = new byte[max]; + outer: for (int i = 0; i < max; i++) { + byte b = values[0][i]; + for (int j = 1; j < values.length; j++) { + if (values[j] == null) { + break outer; + } else if (i < values[j].length) + b &= values[j][i]; + else + b &= 0; + } + dest[i] = b; + } + checkAndSetDataType(destKey, context); + r.put(destKey, new ByteArrayWrapper(dest)); + } + + private void or(ExecutionHandlerContext context, Region<ByteArrayWrapper, ByteArrayWrapper> r, + ByteArrayWrapper destKey, byte[][] values, int max) { + byte[] dest = new byte[max]; + for (int i = 0; i < max; i++) { + byte b = values[0][i]; + for (int j = 1; j < values.length; j++) { + byte[] cA = values[j]; + if (cA != null && i < cA.length) + b |= cA[i]; + else + b |= 0; + } + dest[i] = b; + } + checkAndSetDataType(destKey, context); + r.put(destKey, new ByteArrayWrapper(dest)); + } + + private void xor(ExecutionHandlerContext context, Region<ByteArrayWrapper, ByteArrayWrapper> r, + ByteArrayWrapper destKey, byte[][] values, int max) { + byte[] dest = new byte[max]; + for (int i = 0; i < max; i++) { + byte b = values[0][i]; + for (int j = 1; j < values.length; j++) { + byte[] cA = values[j]; + if (cA != null && i < cA.length) + b ^= cA[i]; + else + b ^= 0; + } + dest[i] = b; + } + checkAndSetDataType(destKey, context); + r.put(destKey, new ByteArrayWrapper(dest)); + } + + private void not(ExecutionHandlerContext context, Region<ByteArrayWrapper, ByteArrayWrapper> r, + ByteArrayWrapper destKey, byte[][] values, int max) { + byte[] dest = new byte[max]; + byte[] cA = values[0]; + for (int i = 0; i < max; i++) { + if (cA == null) + dest[i] = ~0; + else + dest[i] = (byte) (~cA[i] & 0xFF); + } + checkAndSetDataType(destKey, context); + r.put(destKey, new ByteArrayWrapper(dest)); + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitPosExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitPosExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitPosExecutor.java new file mode 100644 index 0000000..4ea633a --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/BitPosExecutor.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.string; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; + +import java.util.List; + +public class BitPosExecutor extends StringExecutor { + + private final String ERROR_NOT_INT = "The numbers provided must be numeric values"; + + private final String ERROR_BIT = "The bit must either be a 0 or 1"; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion(); + + if (commandElems.size() < 3) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.BITPOS)); + return; + } + + ByteArrayWrapper key = command.getKey(); + checkAndSetDataType(key, context); + ByteArrayWrapper string = r.get(key); + + int bit; + int bitPosition = -1; + boolean endSet = false; + + try { + byte[] bitAr = commandElems.get(2); + bit = Coder.bytesToInt(bitAr); + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_INT)); + return; + } + + if (bit != 0 && bit != 1) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_BIT)); + return; + } + + if (string == null || string.length() == 0) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), -bit)); // Redis + // returns + // 0 when + // key + // does + // not + // exists + // for + // this + // command + return; + } + byte[] bytes = string.toBytes(); + int start = 0; + int end = bytes.length - 1; + if (commandElems.size() > 3) { + try { + byte[] startAr = commandElems.get(3); + start = Coder.bytesToInt(startAr); + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_INT)); + return; + } + } + + + if (commandElems.size() > 4) { + try { + byte[] endAr = commandElems.get(4); + end = Coder.bytesToInt(endAr); + endSet = true; + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_INT)); + return; + } + } + + if (start < 0) + start += bytes.length; + if (end < 0) + end += bytes.length; + + if (start < 0) + start = 0; + if (end < 0) + end = 0; + + if (start > bytes.length) + start = bytes.length - 1; + if (end > bytes.length) + end = bytes.length - 1; + + if (end < start) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), -1)); + return; + } + + outerLoop: for (int i = start; i <= end; i++) { + int cBit; + byte cByte = bytes[i]; + for (int j = 0; j < 8; j++) { + cBit = (cByte & (0x80 >> j)) >> (7 - j); + if (cBit == bit) { + bitPosition = 8 * i + j; + break outerLoop; + } + } + } + + if (bit == 0 && bitPosition == -1 && !endSet) + bitPosition = bytes.length * 8; + + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), bitPosition)); + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/DecrByExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/DecrByExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/DecrByExecutor.java new file mode 100644 index 0000000..d632bc0 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/DecrByExecutor.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.string; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; + +import java.util.List; + +public class DecrByExecutor extends StringExecutor { + + private final String ERROR_VALUE_NOT_USABLE = + "The value at this key cannot be decremented numerically"; + + private final String ERROR_DECREMENT_NOT_USABLE = + "The decrementation on this key must be numeric"; + + private final String ERROR_OVERFLOW = "This decrementation cannot be performed due to overflow"; + + private final int DECREMENT_INDEX = 2; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion(); + + if (commandElems.size() < 3) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.DECRBY)); + return; + } + ByteArrayWrapper key = command.getKey(); + checkAndSetDataType(key, context); + ByteArrayWrapper valueWrapper = r.get(key); + + /* + * Try increment + */ + + byte[] decrArray = commandElems.get(DECREMENT_INDEX); + String decrString = Coder.bytesToString(decrArray); + Long decrement; + + try { + decrement = Long.parseLong(decrString); + } catch (NumberFormatException e) { + command.setResponse( + Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_DECREMENT_NOT_USABLE)); + return; + } + + /* + * Value does not exist + */ + + if (valueWrapper == null) { + String negativeDecrString = + decrString.charAt(0) == Coder.HYPHEN_ID ? decrString.substring(1) : "-" + decrString; + r.put(key, new ByteArrayWrapper(Coder.stringToBytes(negativeDecrString))); + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), -decrement)); + return; + } + + /* + * Value exists + */ + + String stringValue = Coder.bytesToString(valueWrapper.toBytes()); + + Long value; + try { + value = Long.parseLong(stringValue); + } catch (NumberFormatException e) { + command.setResponse( + Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_VALUE_NOT_USABLE)); + return; + } + + /* + * Check for overflow Negative decrement is used because the decrement is stored as a positive + * long + */ + if (value <= 0 && -decrement < (Long.MIN_VALUE - value)) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_OVERFLOW)); + return; + } + + value -= decrement; + + stringValue = "" + value; + r.put(key, new ByteArrayWrapper(Coder.stringToBytes(stringValue))); + + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), value)); + + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/DecrExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/DecrExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/DecrExecutor.java new file mode 100644 index 0000000..8c73616 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/DecrExecutor.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.string; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; +import org.apache.geode.redis.internal.RegionProvider; + +import java.util.List; + +public class DecrExecutor extends StringExecutor { + + private final String ERROR_VALUE_NOT_USABLE = + "The value at this key cannot be decremented numerically"; + + private final String ERROR_OVERFLOW = "This decrementation cannot be performed due to overflow"; + + private final byte[] INIT_VALUE_BYTES = Coder.stringToBytes("-1"); + + private final int INIT_VALUE_INT = -1; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + RegionProvider rC = context.getRegionProvider(); + Region<ByteArrayWrapper, ByteArrayWrapper> r = rC.getStringsRegion();; + + if (commandElems.size() < 2) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.DECR)); + return; + } + + ByteArrayWrapper key = command.getKey(); + checkAndSetDataType(key, context); + ByteArrayWrapper valueWrapper = r.get(key); + + /* + * Value does not exist + */ + + if (valueWrapper == null) { + byte[] newValue = INIT_VALUE_BYTES; + r.put(key, new ByteArrayWrapper(newValue)); + rC.metaPut(key, RedisDataType.REDIS_STRING); + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), INIT_VALUE_INT)); + return; + } + + /* + * Value exists + */ + + String stringValue = valueWrapper.toString(); + Long value; + try { + value = Long.parseLong(stringValue); + } catch (NumberFormatException e) { + command.setResponse( + Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_VALUE_NOT_USABLE)); + return; + } + + if (value == Long.MIN_VALUE) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_OVERFLOW)); + return; + } + + value--; + + stringValue = "" + value; + + r.put(key, new ByteArrayWrapper(Coder.stringToBytes(stringValue))); + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), value)); + + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetBitExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetBitExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetBitExecutor.java new file mode 100644 index 0000000..1220be1 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetBitExecutor.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.string; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; + +import java.util.List; + +public class GetBitExecutor extends StringExecutor { + + private final String ERROR_NOT_INT = "The offset provided must be numeric"; + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + List<byte[]> commandElems = command.getProcessedCommand(); + + Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion(); + + if (commandElems.size() < 3) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.GETBIT)); + return; + } + + ByteArrayWrapper key = command.getKey(); + checkAndSetDataType(key, context); + ByteArrayWrapper wrapper = r.get(key); + if (wrapper == null) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), 0)); + return; + } + + int bit = 0; + byte[] bytes = wrapper.toBytes(); + int offset; + try { + byte[] offAr = commandElems.get(2); + offset = Coder.bytesToInt(offAr); + } catch (NumberFormatException e) { + command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_INT)); + return; + } + if (offset < 0) + offset += bytes.length * 8; + + if (offset < 0 || offset > bytes.length * 8) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), 0)); + return; + } + + int byteIndex = offset / 8; + offset %= 8; + + if (byteIndex >= bytes.length) { + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), 0)); + return; + } + + bit = (bytes[byteIndex] & (0x80 >> offset)) >> (7 - offset); + + command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), bit)); + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetExecutor.java ---------------------------------------------------------------------- diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetExecutor.java new file mode 100644 index 0000000..31aac75 --- /dev/null +++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetExecutor.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.internal.executor.string; + +import org.apache.geode.cache.Region; +import org.apache.geode.redis.internal.ByteArrayWrapper; +import org.apache.geode.redis.internal.Coder; +import org.apache.geode.redis.internal.Command; +import org.apache.geode.redis.internal.ExecutionHandlerContext; +import org.apache.geode.redis.internal.RedisConstants.ArityDef; +import org.apache.geode.redis.internal.RedisDataType; + +public class GetExecutor extends StringExecutor { + + @Override + public void executeCommand(Command command, ExecutionHandlerContext context) { + Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion(); + + if (command.getProcessedCommand().size() < 2) { + command + .setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.GETEXECUTOR)); + return; + } + + ByteArrayWrapper key = command.getKey(); + checkDataType(key, RedisDataType.REDIS_STRING, context); + ByteArrayWrapper wrapper = r.get(key); + + if (wrapper == null) { + command.setResponse(Coder.getNilResponse(context.getByteBufAllocator())); + return; + } else { + command.setResponse( + Coder.getBulkStringResponse(context.getByteBufAllocator(), wrapper.toBytes())); + } + + } + +}
