This is an automated email from the ASF dual-hosted git repository.

ringles pushed a commit to branch 
GEODE-9892-Create-Infrastructure-for-Redis-Lists
in repository https://gitbox.apache.org/repos/asf/geode.git

commit 90b39edc4638e267929f3d1f750eae8d56e074e8
Author: Ray Ingles <ring...@vmware.com>
AuthorDate: Tue Jan 4 14:26:48 2022 -0500

    Initial (broken) commit
---
 .../geode/redis/internal/data/NullRedisList.java   |  76 ++++++
 .../geode/redis/internal/data/RedisList.java       | 297 +++++++++++++++++++++
 .../serialization/DataSerializableFixedID.java     |   2 +-
 3 files changed, 374 insertions(+), 1 deletion(-)

diff --git 
a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/data/NullRedisList.java
 
b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/data/NullRedisList.java
new file mode 100644
index 0000000..1e1f1cf
--- /dev/null
+++ 
b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/data/NullRedisList.java
@@ -0,0 +1,76 @@
+/*
+ * 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.data;
+
+import static java.util.Collections.emptyList;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.geode.cache.Region;
+
+class NullRedisList extends RedisList {
+
+  NullRedisList() {
+    super(new HashSet<>());
+  }
+
+  @Override
+  public boolean isNull() {
+    return true;
+  }
+
+  @Override
+  public Collection<byte[]> spop(Region<RedisKey, RedisData> region, RedisKey 
key, int popCount) {
+    return emptyList();
+  }
+
+  @Override
+  public Collection<byte[]> srandmember(int count) {
+    return emptyList();
+  }
+
+  @Override
+  public boolean sismember(byte[] member) {
+    return false;
+  }
+
+  @Override
+  public int scard() {
+    return 0;
+  }
+
+  @Override
+  public long sadd(List<byte[]> membersToAdd, Region<RedisKey, RedisData> 
region, RedisKey key) {
+    region.create(key, new RedisSet(membersToAdd));
+    return membersToAdd.size();
+  }
+
+  @Override
+  public long srem(List<byte[]> membersToRemove, Region<RedisKey, RedisData> 
region, RedisKey key) {
+    return 0;
+  }
+
+  @Override
+  public Set<byte[]> smembers() {
+    // some callers want to be able to modify the set returned
+    return Collections.emptySet();
+  }
+}
diff --git 
a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/data/RedisList.java
 
b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/data/RedisList.java
new file mode 100644
index 0000000..6a3bfbd
--- /dev/null
+++ 
b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/data/RedisList.java
@@ -0,0 +1,297 @@
+/*
+ * 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.data;
+
+import static java.util.Collections.emptyList;
+import static org.apache.geode.internal.JvmSizeUtils.memoryOverhead;
+import static org.apache.geode.redis.internal.data.RedisDataType.REDIS_SET;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+
+import it.unimi.dsi.fastutil.bytes.ByteArrays;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+
+import org.apache.geode.DataSerializer;
+import org.apache.geode.annotations.VisibleForTesting;
+import org.apache.geode.cache.Region;
+import org.apache.geode.internal.serialization.DeserializationContext;
+import org.apache.geode.internal.serialization.KnownVersion;
+import org.apache.geode.internal.serialization.SerializationContext;
+import org.apache.geode.redis.internal.commands.executor.GlobPattern;
+import 
org.apache.geode.redis.internal.data.collections.SizeableObjectOpenCustomHashSet;
+import org.apache.geode.redis.internal.data.delta.AddByteArrays;
+import org.apache.geode.redis.internal.data.delta.RemoveByteArrays;
+import org.apache.geode.redis.internal.data.delta.ReplaceByteArrays;
+import org.apache.geode.redis.internal.services.RegionProvider;
+
+public class RedisList extends AbstractRedisData {
+  protected static final int REDIS_LIST_OVERHEAD = 
memoryOverhead(RedisList.class);
+
+  private List elements;
+
+  public RedisList(Collection<byte[]> elements) {
+    this.elements = new ArrayList(elements.size());
+    for (byte[] member : elements) {
+      appendElement(member);
+    }
+  }
+
+  public RedisList(List members) {
+    this.elements = members;
+  }
+
+  public RedisList(int expectedSize) {
+    elements = new ArrayList(expectedSize);
+  }
+
+  /**
+   * For deserialization only.
+   */
+  public RedisList() {}
+
+  /**
+   * @param elementsToAdd members to add to this set; NOTE this list may by
+   *        modified by this call
+   * @param region the region this instance is stored in
+   * @param key the name of the set to add to
+   * @return the number of members actually added
+   */
+  public long lpush(List<byte[]> elementsToAdd, Region<RedisKey, RedisData> 
region, RedisKey key) {
+    for (byte[] element: elementsToAdd) {
+      prependElement(element);
+    }
+    int elementsAdded = elementsToAdd.size();
+    if (elementsAdded != 0) {
+      storeChanges(region, key, new AddByteArrays(elementsToAdd));
+    }
+    return elements.size();
+  }
+
+  /**
+   * @param region the region this instance is stored in
+   * @param key the name of the set to add to
+   * @param popCount the number of elements to remove and return
+   * @return the element(s) actually popped
+   */
+  public Collection<byte[]> lpop(Region<RedisKey, RedisData> region, RedisKey 
key, int popCount) {
+    int originalSize = elements.size();
+    if (originalSize == 0) {
+      return emptyList();
+    }
+
+    if (popCount >= originalSize) {
+      region.remove(key, this);
+      return this.elements;
+    }
+
+    List<byte[]> popped = new ArrayList<>();
+    while (popped.size() < popCount) {
+      popped.add((byte[]) elements.remove(0));
+    }
+    if (!popped.isEmpty()) {
+      storeChanges(region, key, new RemoveByteArrays(popped));
+    }
+    return popped;
+  }
+
+  public int llen() {
+    return elements.size();
+  }
+
+  @Override
+  public void applyAddByteArrayDelta(byte[] bytes) {
+    appendElement(bytes);
+  }
+
+  @Override
+  public void applyRemoveByteArrayDelta(byte[] bytes) {
+    membersRemove(bytes);
+  }
+
+  @Override
+  public void applyReplaceByteArraysDelta(MemberSet members) {
+    persistNoDelta();
+    this.elements = members;
+  }
+
+  /**
+   * Since GII (getInitialImage) can come in and call toData while other 
threads
+   * are modifying this object, the striped executor will not protect toData.
+   * So any methods that modify "members" needs to be thread safe with toData.
+   */
+
+  @Override
+  public synchronized void toData(DataOutput out, SerializationContext 
context) throws IOException {
+    super.toData(out, context);
+    DataSerializer.writePrimitiveInt(elements.size(), out);
+    for (byte[] member : elements) {
+      DataSerializer.writeByteArray(member, out);
+    }
+  }
+
+  @Override
+  public void fromData(DataInput in, DeserializationContext context)
+      throws IOException, ClassNotFoundException {
+    super.fromData(in, context);
+    int size = DataSerializer.readPrimitiveInt(in);
+    elements = new MemberSet(size);
+    for (int i = 0; i < size; ++i) {
+      elements.add(DataSerializer.readByteArray(in));
+    }
+  }
+
+  @Override
+  public int getDSFID() {
+    return REDIS_LIST_ID;
+  }
+
+  @VisibleForTesting
+  synchronized boolean appendElement(byte[] elementToAdd) {
+    return elements.add(elementToAdd);
+  }
+
+  @VisibleForTesting
+  synchronized boolean prependElement(byte[] elementToAdd) {
+    elements.add(0, elementToAdd);
+    return true;
+  }
+
+  @VisibleForTesting
+  synchronized boolean membersRemove(byte[] memberToRemove) {
+    return elements.remove(memberToRemove);
+  }
+
+  public long sadd(List<byte[]> membersToAdd, Region<RedisKey, RedisData> 
region, RedisKey key) {
+    membersToAdd.removeIf(memberToAdd -> !appendElement(memberToAdd));
+    int membersAdded = membersToAdd.size();
+    if (membersAdded != 0) {
+      storeChanges(region, key, new AddByteArrays(membersToAdd));
+    }
+    return membersAdded;
+  }
+
+  /**
+   * @param membersToRemove members to remove from this set; NOTE this list 
may by
+   *        modified by this call
+   * @param region the region this instance is stored in
+   * @param key the name of the set to remove from
+   * @return the number of members actually removed
+   */
+  public long srem(List<byte[]> membersToRemove, Region<RedisKey, RedisData> 
region, RedisKey key) {
+    membersToRemove.removeIf(memberToRemove -> !membersRemove(memberToRemove));
+    int membersRemoved = membersToRemove.size();
+    if (membersRemoved != 0) {
+      storeChanges(region, key, new RemoveByteArrays(membersToRemove));
+    }
+    return membersRemoved;
+  }
+
+  /**
+   * The returned set is NOT a copy and will be changed
+   * by future changes to this instance.
+   *
+   * @return a set containing all the members in this set
+   */
+  public Set<byte[]> smembers() {
+    return Collections.unmodifiableSet(elements);
+  }
+
+  @Override
+  public RedisDataType getType() {
+    return REDIS_SET;
+  }
+
+  @Override
+  protected boolean removeFromRegion() {
+    return elements.isEmpty();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof RedisList)) {
+      return false;
+    }
+    if (!super.equals(o)) {
+      return false;
+    }
+    RedisList redisSet = (RedisList) o;
+
+    if (redisSet.elements.size() != elements.size()) {
+      return false;
+    }
+    for (byte[] member : elements) {
+      if (!redisSet.elements.contains(member)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(super.hashCode(), elements);
+  }
+
+  @Override
+  public String toString() {
+    return "RedisSet{" + super.toString() + ", " + "size=" + elements.size() + 
'}';
+  }
+
+  @Override
+  public KnownVersion[] getSerializationVersions() {
+    return null;
+  }
+
+  @Override
+  public int getSizeInBytes() {
+    return REDIS_LIST_OVERHEAD + elements.getSizeInBytes();
+  }
+
+  public static class MemberSet extends 
SizeableObjectOpenCustomHashSet<byte[]> {
+    public MemberSet() {
+      super(ByteArrays.HASH_STRATEGY);
+    }
+
+    public MemberSet(int size) {
+      super(size, ByteArrays.HASH_STRATEGY);
+    }
+
+    public MemberSet(Collection<byte[]> initialElements) {
+      super(initialElements, ByteArrays.HASH_STRATEGY);
+    }
+
+    @Override
+    protected int sizeElement(byte[] element) {
+      return memoryOverhead(element);
+    }
+  }
+
+}
diff --git 
a/geode-serialization/src/main/java/org/apache/geode/internal/serialization/DataSerializableFixedID.java
 
b/geode-serialization/src/main/java/org/apache/geode/internal/serialization/DataSerializableFixedID.java
index 73fbcad..2e29996 100644
--- 
a/geode-serialization/src/main/java/org/apache/geode/internal/serialization/DataSerializableFixedID.java
+++ 
b/geode-serialization/src/main/java/org/apache/geode/internal/serialization/DataSerializableFixedID.java
@@ -690,7 +690,7 @@ public interface DataSerializableFixedID extends 
SerializationVersions, BasicSer
   short REDIS_STRING_ID = 2187;
   short REDIS_HASH_ID = 2188;
   short REDIS_NULL_DATA_ID = 2189;
-  // 2190 unused
+  short REDIS_LIST_ID = 2190;
   short REDIS_MEMBER_INFO_ID = 2191;
   short REDIS_SORTED_SET_ID = 2192;
   // NOTE, codes > 65535 will take 4 bytes to serialize

Reply via email to