/* * JBoss, Home of Professional Open Source * * Copyright 2012 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xnio.nativeimpl; import static org.xnio.Bits.allAreSet; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.NoSuchElementException; /** * Integer-indexed hash map. * * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a> */ final class EPollMap extends AbstractCollection<EPollRegistration> { private static final int DEFAULT_INITIAL_CAPACITY = 512; private static final int MAXIMUM_CAPACITY = 1 << 30; private static final float DEFAULT_LOAD_FACTOR = 0.60f; private final float loadFactor; private final int initialCapacity; private EPollRegistration[][] table; int size; int threshold; EPollMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } /** * Construct a new instance. * * @param initialCapacity the initial capacity * @param loadFactor the load factor */ EPollMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) { throw new IllegalArgumentException("Initial capacity must be > 0"); } if (initialCapacity > MAXIMUM_CAPACITY) { initialCapacity = MAXIMUM_CAPACITY; } if (loadFactor <= 0.0 || Float.isNaN(loadFactor) || loadFactor >= 1.0) { throw new IllegalArgumentException("Load factor must be between 0.0f and 1.0f"); } int capacity = 1; while (capacity < initialCapacity) { capacity <<= 1; } this.loadFactor = loadFactor; this.initialCapacity = capacity; threshold = (int) (capacity * loadFactor); table = new EPollRegistration[capacity][]; } public EPollRegistration putIfAbsent(final EPollRegistration value) { return doPut(value, true); } public EPollRegistration removeKey(final int index) { return doRemove(index); } public boolean remove(final Object value) { return value instanceof EPollRegistration && doRemove((EPollRegistration) value); } public boolean remove(final EPollRegistration value) { return value != null && doRemove(value); } public boolean containsKey(final int index) { return doGet(index) != null; } public EPollRegistration get(final int index) { return doGet(index); } public EPollRegistration put(final EPollRegistration value) { if (value == null) { throw new IllegalArgumentException("value is null"); } return doPut(value, false); } public EPollRegistration replace(final EPollRegistration value) { if (value == null) { throw new IllegalArgumentException("value is null"); } return doReplace(value); } public boolean replace(final EPollRegistration oldValue, final EPollRegistration newValue) { if (newValue == null) { throw new IllegalArgumentException("newValue is null"); } if (oldValue.id != newValue.id) { throw new IllegalArgumentException("Can only replace with value which has the same key"); } return doReplace(oldValue, newValue); } public int getKey(final EPollRegistration argument) { return argument.id; } public boolean add(final EPollRegistration value) { if (value == null) { throw new IllegalArgumentException("value is null"); } return doPut(value, true) == null; } @SuppressWarnings("unchecked") public <T> T[] toArray(final T[] a) { final T[] target; if (a.length < size) { target = Arrays.copyOfRange(a, a.length, a.length + size); } else { target = a; } int i = 0; for (final EPollRegistration[] row : table) { if (row != null) { for (EPollRegistration item : row) { if (item != null) { target[i++] = (T) item; } } } } return target; } public Object[] toArray() { final ArrayList<Object> list = new ArrayList<Object>(size()); list.addAll(this); return list.toArray(); } public boolean contains(final Object o) { return o instanceof EPollRegistration && o == get(((EPollRegistration) o).id); } public Iterator<EPollRegistration> iterator() { return new EntryIterator(); } public int size() { return size; } private boolean doReplace(final EPollRegistration oldValue, final EPollRegistration newValue) { if (oldValue.id != newValue.id) { return false; } EPollRegistration[][] table = this.table; final int idx = oldValue.id & table.length - 1; EPollRegistration[] row = table[idx]; if (row == null) { return false; } int rowLength = row.length; for (int i = 0; i < rowLength; i++) { EPollRegistration item = row[i]; if (item != null && item == oldValue) { row[i] = newValue; return true; } } return false; } private EPollRegistration doReplace(final EPollRegistration value) { EPollRegistration[][] table = this.table; final int idx = value.id & table.length - 1; EPollRegistration[] row = table[idx]; if (row == null) { return null; } int rowLength = row.length; for (int i = 0; i < rowLength; i++) { EPollRegistration item = row[i]; if (item != null && item.id == value.id) { row[i] = value; return item; } } return null; } private boolean doRemove(final EPollRegistration value) { EPollRegistration[][] table = this.table; final int idx = value.id & table.length - 1; EPollRegistration[] row = table[idx]; if (row == null) { return false; } int rowLength = row.length; for (int i = 0; i < rowLength; i++) { EPollRegistration item = row[i]; if (item != null && item == value) { row[i] = null; size--; return true; } } return false; } private EPollRegistration doRemove(final int key) { EPollRegistration[][] table = this.table; final int idx = key & table.length - 1; EPollRegistration[] row = table[idx]; if (row == null) { return null; } int rowLength = row.length; for (int i = 0; i < rowLength; i++) { EPollRegistration item = row[i]; if (item != null && item.id == key) { row[i] = null; size--; return item; } } return null; } private EPollRegistration doPut(EPollRegistration value, boolean ifAbsent) { final int hashCode = value.id; EPollRegistration[][] table = this.table; final int idx = hashCode & table.length - 1; EPollRegistration[] row = table[idx]; if (row == null) { if (grow()) { table = this.table; } table[idx] = new EPollRegistration[3]; table[idx][0] = value; return null; } int s = -1; int rowLength = row.length; for (int i = 0; i < rowLength; i++) { EPollRegistration item = row[i]; if (item == null) { if (s == -1) { s = i; } } else if (item.id == value.id) { if (! ifAbsent) { row[i] = value; } return item; } } // Search again when grown if (grow()) { return doPut(value, ifAbsent); } if (s != -1) { row[s] = value; return null; } row = Arrays.copyOf(row, rowLength + 2); row[rowLength] = value; table[idx] = row; return null; } private boolean grow() { if (size == Integer.MAX_VALUE) { throw new IllegalStateException("Table full"); } if (size ++ < threshold || threshold == Integer.MAX_VALUE) { return false; } final EPollRegistration[][] oldTable = table; final int oldLen = oldTable.length; assert Integer.bitCount(oldLen) == 1; final EPollRegistration[][] newTable = Arrays.copyOf(oldTable, oldLen << 1); for (int i = 0; i < oldLen; i++) { EPollRegistration[] row = newTable[i]; if (row != null) { EPollRegistration[] newRow = row.clone(); newTable[i + oldLen] = newRow; final int rowLen = row.length; for (int j = 0; j < rowLen; j++) { final EPollRegistration item = row[j]; if (item != null) { if (allAreSet(item.id, oldLen)) { row[j] = null; } else { newRow[j] = null; } } } } } table = newTable; threshold = (int) (newTable.length * loadFactor); return true; } private EPollRegistration doGet(final int key) { final EPollRegistration[][] table = this.table; int idx = key & (table.length - 1); final EPollRegistration[] row = table[idx]; if (row != null) for (EPollRegistration item : row) { if (item != null && key == item.id) { return item; } } return null; } public void clear() { table = new EPollRegistration[initialCapacity][]; size = 0; threshold = (int) (initialCapacity * loadFactor); } class EntryIterator implements Iterator<EPollRegistration> { int rowIdx; int itemIdx; EPollRegistration next; public boolean hasNext() { if (next == null) { while (rowIdx < table.length) { final EPollRegistration[] row = table[rowIdx]; if (row != null) { while (itemIdx < row.length) { final EPollRegistration item = row[itemIdx++]; if (item != null) { next = item; return true; } } itemIdx = 0; } rowIdx++; } return false; } else { return true; } } public EPollRegistration next() { if (! hasNext()) { throw new NoSuchElementException(); } try { return next; } finally { next = null; } } public void remove() { table[rowIdx][itemIdx - 1] = null; size--; } } }