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

bchapuis pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-baremaps.git


The following commit(s) were added to refs/heads/main by this push:
     new 00ec04d7 Bugfix when key is out of Boundary for MemoryAlignedDataMap 
(#880)
00ec04d7 is described below

commit 00ec04d78cb33a3986d23da6567094964b7061fd
Author: yagagagaga <[email protected]>
AuthorDate: Tue Jun 18 16:00:04 2024 +0800

    Bugfix when key is out of Boundary for MemoryAlignedDataMap (#880)
---
 .../data/collection/MemoryAlignedDataMap.java      |  17 ++
 .../data/collection/MemoryAlignedDataMapTest.java  | 182 +++++++++++++++++++++
 2 files changed, 199 insertions(+)

diff --git 
a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataMap.java
 
b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataMap.java
index ca0380ba..0bec5dcf 100644
--- 
a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataMap.java
+++ 
b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataMap.java
@@ -24,6 +24,7 @@ import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import org.apache.baremaps.data.memory.Memory;
 import org.apache.baremaps.data.type.FixedSizeDataType;
 
@@ -45,6 +46,8 @@ public class MemoryAlignedDataMap<E> implements DataMap<Long, 
E> {
 
   private final long segmentMask;
 
+  private final long upperBoundary;
+
   /**
    * Constructs a {@link MemoryAlignedDataMap}.
    *
@@ -66,11 +69,24 @@ public class MemoryAlignedDataMap<E> implements 
DataMap<Long, E> {
     this.valueShift = (int) (Math.log(dataType.size()) / Math.log(2));
     this.segmentShift = memory.segmentShift();
     this.segmentMask = memory.segmentMask();
+    this.upperBoundary = segmentShift > 32 ? Long.MAX_VALUE >> valueShift
+        : Long.MAX_VALUE >> (32 - segmentShift + valueShift);
+  }
+
+  private void checkBoundary(Long key) {
+    Objects.requireNonNull(key, "Key couldn't be null");
+    if (key < 0 || key > upperBoundary) {
+      String msg =
+          String.format("Key should between 0 and %d, but your key is %d", 
upperBoundary, key);
+      throw new IndexOutOfBoundsException(msg);
+    }
   }
 
   /** {@inheritDoc} */
   @Override
   public E put(Long key, E value) {
+    checkBoundary(key);
+    Objects.requireNonNull(value, "Value couldn't be null");
     long position = key << valueShift;
     int segmentIndex = (int) (position >>> segmentShift);
     int segmentOffset = (int) (position & segmentMask);
@@ -83,6 +99,7 @@ public class MemoryAlignedDataMap<E> implements DataMap<Long, 
E> {
   /** {@inheritDoc} */
   @Override
   public E get(Object key) {
+    checkBoundary((long) key);
     long position = (long) key << valueShift;
     int segmentIndex = (int) (position >>> segmentShift);
     int segmentOffset = (int) (position & segmentMask);
diff --git 
a/baremaps-data/src/test/java/org/apache/baremaps/data/collection/MemoryAlignedDataMapTest.java
 
b/baremaps-data/src/test/java/org/apache/baremaps/data/collection/MemoryAlignedDataMapTest.java
new file mode 100644
index 00000000..241cc4c2
--- /dev/null
+++ 
b/baremaps-data/src/test/java/org/apache/baremaps/data/collection/MemoryAlignedDataMapTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.baremaps.data.collection;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import org.apache.baremaps.data.memory.OnHeapMemory;
+import org.apache.baremaps.data.type.IntegerDataType;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class MemoryAlignedDataMapTest {
+
+  private MemoryAlignedDataMap<Integer> map;
+
+  @BeforeEach
+  void setUp() {
+    map = new MemoryAlignedDataMap<>(new IntegerDataType(), new 
OnHeapMemory(1024));
+  }
+
+  @AfterEach
+  void tearDown() {
+    map = null;
+  }
+
+  @Test
+  void put() {
+    map.put(1L, 1);
+    map.put(1023L, 2);
+    map.put(1024L, 3);
+    map.put(1025L, 4);
+    map.put(0L, 5);
+    assertThrows(IndexOutOfBoundsException.class, () -> map.put(549755813888L, 
1));
+    assertThrows(IndexOutOfBoundsException.class, () -> 
map.put(Long.MAX_VALUE, 1));
+    assertThrows(IndexOutOfBoundsException.class, () -> map.put(Long.MIN_VALUE 
>> 2, 1));
+    assertThrows(IndexOutOfBoundsException.class, () -> 
map.put(Long.MIN_VALUE, 1));
+    assertThrows(IndexOutOfBoundsException.class, () -> 
map.put(0xc000000000000000L, 1));
+    assertThrows(IndexOutOfBoundsException.class, () -> map.put(-1L, 1));
+    assertThrows(IndexOutOfBoundsException.class, () -> map.put(-2L, 1));
+    assertThrows(IndexOutOfBoundsException.class, () -> map.put(-1L << 60, 1));
+    assertThrows(IndexOutOfBoundsException.class, () -> map.put(-1L << 59, 1));
+
+    assertThrows(NullPointerException.class, () -> map.put(null, 1));
+    assertThrows(NullPointerException.class, () -> map.put(1L, null));
+  }
+
+  @Test
+  void get() {
+    map.put(1L, 1);
+    map.put(2L, 2);
+    assertEquals(1, map.get(1L));
+    assertEquals(2, map.get(2L));
+    assertEquals(0, map.get(3L));
+    assertThrows(IndexOutOfBoundsException.class, () -> map.get(-1L));
+    assertThrows(IndexOutOfBoundsException.class, () -> 
map.get(549755813888L));
+
+    assertThrows(NullPointerException.class, () -> map.get(null));
+  }
+
+  @Test
+  void containsKey() {
+    assertFalse(map.containsKey(1));
+    assertFalse(map.containsKey(1L));
+    map.put(1L, 1);
+    assertFalse(map.containsKey(1));
+    assertTrue(map.containsKey(0L));
+    assertTrue(map.containsKey(1L));
+    assertTrue(map.containsKey(5L));
+    assertFalse(map.containsKey(256L));
+
+    map.put(500L, 1);
+    assertTrue(map.containsKey(257L));
+    assertTrue(map.containsKey(258L));
+    assertTrue(map.containsKey(511L));
+
+    assertFalse(map.containsKey(null));
+  }
+
+  @Test
+  void containsValue() {
+    assertFalse(map.containsValue(0));
+    assertFalse(map.containsValue(1));
+    map.put(1L, 1);
+    assertTrue(map.containsValue(0));
+    assertTrue(map.containsValue(1));
+    assertFalse(map.containsValue(2));
+    assertFalse(map.containsValue(255));
+    map.put(256L, 1);
+    assertFalse(map.containsValue(256));
+
+    assertFalse(map.containsValue(null));
+  }
+
+  @Test
+  void size() {
+    assertEquals(0, map.size());
+    map.put(0L, 1);
+    map.put(1L, 1);
+    assertEquals(256, map.size());
+    map.put(256L, 1);
+    assertEquals(512, map.size());
+    map.put(600L, 6);
+    assertEquals(768, map.size());
+  }
+
+  @Test
+  void clear() {
+    assertThrows(UnsupportedOperationException.class, () -> map.clear());
+  }
+
+  @Test
+  void keyIterator() {
+    Iterator<Long> itr1 = map.keyIterator();
+    assertFalse(itr1.hasNext());
+    assertThrows(NoSuchElementException.class, itr1::next);
+
+    map.put(1L, 1);
+    Iterator<Long> itr2 = map.keyIterator();
+    assertTrue(itr2.hasNext());
+    for (long i = 0; i < 256; i++) {
+      assertEquals(i, itr2.next());
+    }
+  }
+
+  @Test
+  void valueIterator() {
+    Iterator<Integer> itr1 = map.valueIterator();
+    assertFalse(itr1.hasNext());
+    assertThrows(NoSuchElementException.class, itr1::next);
+
+    map.put(1L, 2);
+    Iterator<Integer> itr2 = map.valueIterator();
+    assertTrue(itr2.hasNext());
+    for (int i = 0; i < 256; i++) {
+      if (i == 1) {
+        assertEquals(2, itr2.next());
+      } else {
+        assertEquals(0, itr2.next());
+      }
+    }
+  }
+
+  @Test
+  void entryIterator() {
+    Iterator<Map.Entry<Long, Integer>> itr1 = map.entryIterator();
+    assertFalse(itr1.hasNext());
+    assertThrows(NoSuchElementException.class, itr1::next);
+
+    map.put(1L, 3);
+    Iterator<Map.Entry<Long, Integer>> itr2 = map.entryIterator();
+    assertTrue(itr2.hasNext());
+    for (long i = 0; i < 256; i++) {
+      if (i == 1) {
+        assertEquals(Map.entry(i, 3), itr2.next());
+      } else {
+        assertEquals(Map.entry(i, 0), itr2.next());
+      }
+    }
+  }
+}

Reply via email to