diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java index 71a574f6cc..c437282f62 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java @@ -387,8 +387,7 @@ public class DefaultHttp2Connection implements Http2Connection { @Override public Http2Stream forEachChild(Http2StreamVisitor visitor) throws Http2Exception { - for (IntObjectHashMap.Entry entry : children.entries()) { - Http2Stream stream = entry.value(); + for (DefaultStream stream : children.values()) { if (!visitor.visit(stream)) { return stream; } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java index 15568344ac..035fc949e8 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java @@ -484,7 +484,7 @@ public class DefaultHttp2FrameReader implements Http2FrameReader, Http2FrameSize char id = (char) payload.readUnsignedShort(); long value = payload.readUnsignedInt(); try { - settings.put(id, value); + settings.put(id, Long.valueOf(value)); } catch (IllegalArgumentException e) { switch(id) { case SETTINGS_MAX_FRAME_SIZE: diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java index 036537aa2d..20fc26baa2 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java @@ -213,7 +213,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize int payloadLength = SETTING_ENTRY_LENGTH * settings.size(); ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + settings.size() * SETTING_ENTRY_LENGTH); writeFrameHeaderInternal(buf, payloadLength, SETTINGS, new Http2Flags(), 0); - for (CharObjectMap.Entry entry : settings.entries()) { + for (CharObjectMap.PrimitiveEntry entry : settings.entries()) { writeUnsignedShort(entry.key(), buf); writeUnsignedInt(entry.value(), buf); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java index 0cdb65b941..dc13efb5ec 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java @@ -14,18 +14,6 @@ */ package io.netty.handler.codec.http2; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.base64.Base64; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpClientUpgradeHandler; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.util.collection.CharObjectHashMap; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - import static io.netty.handler.codec.base64.Base64Dialect.URL_SAFE; import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME; import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_SETTINGS_HEADER; @@ -36,6 +24,18 @@ import static io.netty.util.CharsetUtil.UTF_8; import static io.netty.util.ReferenceCountUtil.release; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.base64.Base64; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpClientUpgradeHandler; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.util.collection.CharObjectMap; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + /** * Client-side cleartext upgrade codec from HTTP to HTTP/2. */ @@ -105,7 +105,7 @@ public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.Upgrade // Serialize the payload of the SETTINGS frame. int payloadLength = SETTING_ENTRY_LENGTH * settings.size(); buf = ctx.alloc().buffer(payloadLength); - for (CharObjectHashMap.Entry entry : settings.entries()) { + for (CharObjectMap.PrimitiveEntry entry : settings.entries()) { writeUnsignedShort(entry.key(), buf); writeUnsignedInt(entry.value(), buf); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Settings.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Settings.java index 5aaca0555f..da719e4bca 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Settings.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Settings.java @@ -46,6 +46,8 @@ public final class Http2Settings extends CharObjectHashMap { * the standard settings will not cause the map capacity to change. */ private static final int DEFAULT_CAPACITY = (int) (NUM_STANDARD_SETTINGS / DEFAULT_LOAD_FACTOR) + 1; + private static final Long FALSE = 0L; + private static final Long TRUE = 1L; public Http2Settings() { this(DEFAULT_CAPACITY); @@ -84,7 +86,7 @@ public final class Http2Settings extends CharObjectHashMap { * @throws IllegalArgumentException if verification of the setting fails. */ public Http2Settings headerTableSize(int value) { - put(SETTINGS_HEADER_TABLE_SIZE, (long) value); + put(SETTINGS_HEADER_TABLE_SIZE, Long.valueOf(value)); return this; } @@ -96,14 +98,14 @@ public final class Http2Settings extends CharObjectHashMap { if (value == null) { return null; } - return value != 0L; + return TRUE.equals(value); } /** * Sets the {@code SETTINGS_ENABLE_PUSH} value. */ public Http2Settings pushEnabled(boolean enabled) { - put(SETTINGS_ENABLE_PUSH, enabled ? 1L : 0L); + put(SETTINGS_ENABLE_PUSH, enabled ? TRUE : FALSE); return this; } @@ -120,7 +122,7 @@ public final class Http2Settings extends CharObjectHashMap { * @throws IllegalArgumentException if verification of the setting fails. */ public Http2Settings maxConcurrentStreams(long value) { - put(SETTINGS_MAX_CONCURRENT_STREAMS, value); + put(SETTINGS_MAX_CONCURRENT_STREAMS, Long.valueOf(value)); return this; } @@ -137,7 +139,7 @@ public final class Http2Settings extends CharObjectHashMap { * @throws IllegalArgumentException if verification of the setting fails. */ public Http2Settings initialWindowSize(int value) { - put(SETTINGS_INITIAL_WINDOW_SIZE, (long) value); + put(SETTINGS_INITIAL_WINDOW_SIZE, Long.valueOf(value)); return this; } @@ -154,7 +156,7 @@ public final class Http2Settings extends CharObjectHashMap { * @throws IllegalArgumentException if verification of the setting fails. */ public Http2Settings maxFrameSize(int value) { - put(SETTINGS_MAX_FRAME_SIZE, (long) value); + put(SETTINGS_MAX_FRAME_SIZE, Long.valueOf(value)); return this; } @@ -183,7 +185,7 @@ public final class Http2Settings extends CharObjectHashMap { value = Integer.MAX_VALUE; } - put(SETTINGS_MAX_HEADER_LIST_SIZE, (long) value); + put(SETTINGS_MAX_HEADER_LIST_SIZE, Long.valueOf(value)); return this; } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowControllerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowControllerTest.java index 327c7c820c..963c43f9e8 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowControllerTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowControllerTest.java @@ -46,6 +46,7 @@ import io.netty.util.collection.IntObjectMap; import io.netty.util.concurrent.EventExecutor; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -896,10 +897,10 @@ public class DefaultHttp2RemoteFlowControllerTest { // Send a bunch of data on each stream. final IntObjectMap streamSizes = new IntObjectHashMap(4); - streamSizes.put(STREAM_A, 400); - streamSizes.put(STREAM_B, 500); - streamSizes.put(STREAM_C, 600); - streamSizes.put(STREAM_D, 700); + streamSizes.put(STREAM_A, (Integer) 400); + streamSizes.put(STREAM_B, (Integer) 500); + streamSizes.put(STREAM_C, (Integer) 600); + streamSizes.put(STREAM_D, (Integer) 700); FakeFlowControlled dataA = new FakeFlowControlled(streamSizes.get(STREAM_A)); FakeFlowControlled dataB = new FakeFlowControlled(streamSizes.get(STREAM_B)); @@ -922,11 +923,11 @@ public class DefaultHttp2RemoteFlowControllerTest { streamableBytesForTree(stream0)); assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_A, STREAM_C, STREAM_D)), streamableBytesForTree(streamA)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_B)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_B)), streamableBytesForTree(streamB)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_C)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_C)), streamableBytesForTree(streamC)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_D)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_D)), streamableBytesForTree(streamD)); } @@ -966,10 +967,10 @@ public class DefaultHttp2RemoteFlowControllerTest { // Send a bunch of data on each stream. final IntObjectMap streamSizes = new IntObjectHashMap(4); - streamSizes.put(STREAM_A, 400); - streamSizes.put(STREAM_B, 500); - streamSizes.put(STREAM_C, 600); - streamSizes.put(STREAM_D, 700); + streamSizes.put(STREAM_A, (Integer) 400); + streamSizes.put(STREAM_B, (Integer) 500); + streamSizes.put(STREAM_C, (Integer) 600); + streamSizes.put(STREAM_D, (Integer) 700); FakeFlowControlled dataA = new FakeFlowControlled(streamSizes.get(STREAM_A)); FakeFlowControlled dataB = new FakeFlowControlled(streamSizes.get(STREAM_B)); @@ -996,9 +997,9 @@ public class DefaultHttp2RemoteFlowControllerTest { streamableBytesForTree(streamA)); assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_B, STREAM_C, STREAM_D)), streamableBytesForTree(streamB)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_C)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_C)), streamableBytesForTree(streamC)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_D)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_D)), streamableBytesForTree(streamD)); } @@ -1041,11 +1042,11 @@ public class DefaultHttp2RemoteFlowControllerTest { // Send a bunch of data on each stream. final IntObjectMap streamSizes = new IntObjectHashMap(4); - streamSizes.put(STREAM_A, 400); - streamSizes.put(STREAM_B, 500); - streamSizes.put(STREAM_C, 600); - streamSizes.put(STREAM_D, 700); - streamSizes.put(STREAM_E, 900); + streamSizes.put(STREAM_A, (Integer) 400); + streamSizes.put(STREAM_B, (Integer) 500); + streamSizes.put(STREAM_C, (Integer) 600); + streamSizes.put(STREAM_D, (Integer) 700); + streamSizes.put(STREAM_E, (Integer) 900); FakeFlowControlled dataA = new FakeFlowControlled(streamSizes.get(STREAM_A)); FakeFlowControlled dataB = new FakeFlowControlled(streamSizes.get(STREAM_B)); @@ -1072,11 +1073,11 @@ public class DefaultHttp2RemoteFlowControllerTest { assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_A, STREAM_E, STREAM_C, STREAM_D)), streamableBytesForTree(streamA)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_B)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_B)), streamableBytesForTree(streamB)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_C)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_C)), streamableBytesForTree(streamC)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_D)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_D)), streamableBytesForTree(streamD)); assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_E, STREAM_C, STREAM_D)), streamableBytesForTree(streamE)); @@ -1106,10 +1107,10 @@ public class DefaultHttp2RemoteFlowControllerTest { // Send a bunch of data on each stream. final IntObjectMap streamSizes = new IntObjectHashMap(4); - streamSizes.put(STREAM_A, 400); - streamSizes.put(STREAM_B, 500); - streamSizes.put(STREAM_C, 600); - streamSizes.put(STREAM_D, 700); + streamSizes.put(STREAM_A, (Integer) 400); + streamSizes.put(STREAM_B, (Integer) 500); + streamSizes.put(STREAM_C, (Integer) 600); + streamSizes.put(STREAM_D, (Integer) 700); FakeFlowControlled dataA = new FakeFlowControlled(streamSizes.get(STREAM_A)); FakeFlowControlled dataB = new FakeFlowControlled(streamSizes.get(STREAM_B)); @@ -1133,11 +1134,11 @@ public class DefaultHttp2RemoteFlowControllerTest { streamableBytesForTree(stream0)); assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_C, STREAM_D)), streamableBytesForTree(streamA)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_B)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_B)), streamableBytesForTree(streamB)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_C)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_C)), streamableBytesForTree(streamC)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_D)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_D)), streamableBytesForTree(streamD)); } @@ -1174,10 +1175,10 @@ public class DefaultHttp2RemoteFlowControllerTest { // Send a bunch of data on each stream. final IntObjectMap streamSizes = new IntObjectHashMap(4); - streamSizes.put(STREAM_A, 400); - streamSizes.put(STREAM_B, 500); - streamSizes.put(STREAM_C, 600); - streamSizes.put(STREAM_D, 700); + streamSizes.put(STREAM_A, (Integer) 400); + streamSizes.put(STREAM_B, (Integer) 500); + streamSizes.put(STREAM_C, (Integer) 600); + streamSizes.put(STREAM_D, (Integer) 700); FakeFlowControlled dataA = new FakeFlowControlled(streamSizes.get(STREAM_A)); FakeFlowControlled dataB = new FakeFlowControlled(streamSizes.get(STREAM_B)); @@ -1201,10 +1202,10 @@ public class DefaultHttp2RemoteFlowControllerTest { streamableBytesForTree(stream0)); assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_A, STREAM_D)), streamableBytesForTree(streamA)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_B)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_B)), streamableBytesForTree(streamB)); assertEquals(0, streamableBytesForTree(streamC)); - assertEquals(calculateStreamSizeSum(streamSizes, Arrays.asList(STREAM_D)), + assertEquals(calculateStreamSizeSum(streamSizes, Collections.singletonList(STREAM_D)), streamableBytesForTree(streamD)); } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2SettingsTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2SettingsTest.java index aa9f1aba9b..cb33ca136e 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2SettingsTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2SettingsTest.java @@ -65,14 +65,14 @@ public class Http2SettingsTest { @Test public void nonStandardSettingsShouldBeSet() { char key = 0; - settings.put(key, 123L); + settings.put(key, (Long) 123L); assertEquals(123L, (long) settings.get(key)); } @Test public void settingsShouldSupportUnsignedShort() { char key = (char) (Short.MAX_VALUE + 1); - settings.put(key, 123L); + settings.put(key, (Long) 123L); assertEquals(123L, (long) settings.get(key)); } diff --git a/common/src/main/java/io/netty/util/collection/PrimitiveCollections.java b/common/src/main/java/io/netty/util/collection/PrimitiveCollections.java index 9903070883..4cf1f53623 100644 --- a/common/src/main/java/io/netty/util/collection/PrimitiveCollections.java +++ b/common/src/main/java/io/netty/util/collection/PrimitiveCollections.java @@ -14,12 +14,12 @@ */ package io.netty.util.collection; -import io.netty.util.internal.EmptyArrays; - import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; /** * Utility methods for primitive collections. @@ -61,11 +61,6 @@ public final class PrimitiveCollections { throw new UnsupportedOperationException("put"); } - @Override - public void putAll(IntObjectMap sourceMap) { - throw new UnsupportedOperationException("putAll"); - } - @Override public Object remove(int key) { return null; @@ -81,11 +76,21 @@ public final class PrimitiveCollections { return true; } + @Override + public boolean containsKey(Object key) { + return false; + } + @Override public void clear() { // Do nothing. } + @Override + public Set keySet() { + return Collections.emptySet(); + } + @Override public boolean containsKey(int key) { return false; @@ -97,24 +102,39 @@ public final class PrimitiveCollections { } @Override - public Iterable> entries() { + public Iterable> entries() { return Collections.emptySet(); } @Override - public int[] keys() { - return EmptyArrays.EMPTY_INTS; + public Object get(Object key) { + return null; } @Override - public Object[] values(Class clazz) { - return EmptyArrays.EMPTY_OBJECTS; + public Object put(Integer key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Object remove(Object key) { + return null; + } + + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException(); } @Override public Collection values() { return Collections.emptyList(); } + + @Override + public Set> entrySet() { + return Collections.emptySet(); + } } /** @@ -122,9 +142,12 @@ public final class PrimitiveCollections { * * @param the value type stored in the map. */ - private static final class UnmodifiableIntObjectMap implements IntObjectMap, - Iterable> { - final IntObjectMap map; + private static final class UnmodifiableIntObjectMap implements IntObjectMap { + private final IntObjectMap map; + private Set keySet; + private Set> entrySet; + private Collection values; + private Iterable> entries; UnmodifiableIntObjectMap(IntObjectMap map) { this.map = map; @@ -140,11 +163,6 @@ public final class PrimitiveCollections { throw new UnsupportedOperationException("put"); } - @Override - public void putAll(IntObjectMap sourceMap) { - throw new UnsupportedOperationException("putAll"); - } - @Override public V remove(int key) { throw new UnsupportedOperationException("remove"); @@ -171,42 +189,80 @@ public final class PrimitiveCollections { } @Override - public boolean containsValue(V value) { + public boolean containsValue(Object value) { return map.containsValue(value); } @Override - public Iterable> entries() { - return this; + public boolean containsKey(Object key) { + return map.containsKey(key); } @Override - public Iterator> iterator() { - return new IteratorImpl(map.entries().iterator()); + public V get(Object key) { + return map.get(key); } @Override - public int[] keys() { - return map.keys(); + public V put(Integer key, V value) { + throw new UnsupportedOperationException("put"); } @Override - public V[] values(Class clazz) { - return map.values(clazz); + public V remove(Object key) { + throw new UnsupportedOperationException("remove"); + } + + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException("putAll"); + } + + @Override + public Iterable> entries() { + if (entries == null) { + entries = new Iterable>() { + @Override + public Iterator> iterator() { + return new IteratorImpl(map.entries().iterator()); + } + }; + } + + return entries; + } + + @Override + public Set keySet() { + if (keySet == null) { + keySet = Collections.unmodifiableSet(map.keySet()); + } + return keySet; + } + + @Override + public Set> entrySet() { + if (entrySet == null) { + entrySet = Collections.unmodifiableSet(map.entrySet()); + } + return entrySet; } @Override public Collection values() { - return map.values(); + if (values == null) { + values = Collections.unmodifiableCollection(map.values()); + } + return values; } /** * Unmodifiable wrapper for an iterator. */ - private class IteratorImpl implements Iterator> { - final Iterator> iter; + private class IteratorImpl implements Iterator> { + final Iterator> iter; - IteratorImpl(Iterator> iter) { + IteratorImpl(Iterator> iter) { this.iter = iter; } @@ -216,7 +272,7 @@ public final class PrimitiveCollections { } @Override - public Entry next() { + public PrimitiveEntry next() { if (!hasNext()) { throw new NoSuchElementException(); } @@ -232,10 +288,10 @@ public final class PrimitiveCollections { /** * Unmodifiable wrapper for an entry. */ - private class EntryImpl implements Entry { - final Entry entry; + private class EntryImpl implements PrimitiveEntry { + final PrimitiveEntry entry; - EntryImpl(Entry entry) { + EntryImpl(PrimitiveEntry entry) { this.entry = entry; } diff --git a/common/src/main/script/codegen.groovy b/common/src/main/script/codegen.groovy index 0fd512a4f5..4ae4636e3d 100644 --- a/common/src/main/script/codegen.groovy +++ b/common/src/main/script/codegen.groovy @@ -9,17 +9,19 @@ templateDirs.eachWithIndex { templateDir, i -> void convertSources(String templateDir, String outputDir) { String[] keyPrimitives = ["byte", "char", "short", "int", "long"] - String[] keyObjects = ["Byte", "Character", "Short", "Integer", "Long"]; + String[] keyObjects = ["Byte", "Character", "Short", "Integer", "Long"] + String[] keyNumberMethod = ["byteValue", "charValue", "shortValue", "intValue", "longValue"] keyPrimitives.eachWithIndex { keyPrimitive, i -> - convertTemplates templateDir, outputDir, keyPrimitive, keyObjects[i] + convertTemplates templateDir, outputDir, keyPrimitive, keyObjects[i], keyNumberMethod[i] } } void convertTemplates(String templateDir, String outputDir, String keyPrimitive, - String keyObject) { + String keyObject, + String keyNumberMethod) { def keyName = keyPrimitive.capitalize() def replaceFrom = "(^.*)K([^.]+)\\.template\$" def replaceTo = "\\1" + keyName + "\\2.java" @@ -32,6 +34,7 @@ void convertTemplates(String templateDir, filter(token: "K", value: keyName) filter(token: "k", value: keyPrimitive) filter(token: "O", value: keyObject) + filter(token: "KEY_NUMBER_METHOD", value: keyNumberMethod) filter(token: "HASH_CODE", value: hashCodeFn) } regexpmapper(from: replaceFrom, to: replaceTo) diff --git a/common/src/main/templates/io/netty/util/collection/KObjectHashMap.template b/common/src/main/templates/io/netty/util/collection/KObjectHashMap.template index fa0a420372..78f76b3297 100644 --- a/common/src/main/templates/io/netty/util/collection/KObjectHashMap.template +++ b/common/src/main/templates/io/netty/util/collection/KObjectHashMap.template @@ -17,12 +17,14 @@ package io.netty.util.collection; import static io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo; -import java.lang.reflect.Array; import java.util.AbstractCollection; +import java.util.AbstractSet; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; +import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; /** * A hash map implementation of {@link @K@ObjectMap} that uses open addressing for keys. @@ -32,7 +34,7 @@ import java.util.NoSuchElementException; * * @param The value type stored in the map. */ -public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectMap.Entry> { +public class @K@ObjectHashMap implements @K@ObjectMap { /** Default initial capacity. Used if not specified in the constructor */ public static final int DEFAULT_CAPACITY = 8; @@ -57,6 +59,15 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM private int size; private int mask; + private final Set<@O@> keySet = new KeySet(); + private final Set> entrySet = new EntrySet(); + private final Iterable> entries = new Iterable>() { + @Override + public Iterator> iterator() { + return new PrimitiveIterator(); + } + }; + public @K@ObjectHashMap() { this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); } @@ -139,9 +150,10 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM } @Override - public void putAll(@K@ObjectMap sourceMap) { - if (sourceMap instanceof IntObjectHashMap) { + public void putAll(Map sourceMap) { + if (sourceMap instanceof @K@ObjectHashMap) { // Optimization - iterate through the arrays. + @SuppressWarnings("unchecked") @K@ObjectHashMap source = (@K@ObjectHashMap) sourceMap; for (int i = 0; i < source.values.length; ++i) { V sourceValue = source.values[i]; @@ -153,8 +165,8 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM } // Otherwise, just add each entry. - for (Entry entry : sourceMap.entries()) { - put(entry.key(), entry.value()); + for (Entry entry : sourceMap.entrySet()) { + put(entry.getKey(), entry.getValue()); } } @@ -193,8 +205,9 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM } @Override - public boolean containsValue(V value) { - V v1 = toInternal(value); + public boolean containsValue(Object value) { + @SuppressWarnings("unchecked") + V v1 = toInternal((V) value); for (V v2 : values) { // The map supports null values; this will be matched as NULL_VALUE.equals(NULL_VALUE). if (v2 != null && v2.equals(v1)) { @@ -205,38 +218,8 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM } @Override - public Iterable> entries() { - return this; - } - - @Override - public Iterator> iterator() { - return new IteratorImpl(); - } - - @Override - public @k@[] keys() { - @k@[] outKeys = new @k@[size()]; - int targetIx = 0; - for (int i = 0; i < values.length; ++i) { - if (values[i] != null) { - outKeys[targetIx++] = keys[i]; - } - } - return outKeys; - } - - @Override - public V[] values(Class clazz) { - @SuppressWarnings("unchecked") - V[] outValues = (V[]) Array.newInstance(clazz, size()); - int targetIx = 0; - for (V value : values) { - if (value != null) { - outValues[targetIx++] = value; - } - } - return outValues; + public Iterable> entries() { + return entries; } @Override @@ -245,7 +228,8 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM @Override public Iterator iterator() { return new Iterator() { - final Iterator> iter = @K@ObjectHashMap.this.iterator(); + final PrimitiveIterator iter = new PrimitiveIterator(); + @Override public boolean hasNext() { return iter.hasNext(); @@ -319,6 +303,40 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM return true; } + @Override + public boolean containsKey(Object key) { + return containsKey(objectToKey(key)); + } + + @Override + public V get(Object key) { + return get(objectToKey(key)); + } + + @Override + public V put(@O@ key, V value) { + return put(objectToKey(key), value); + } + + @Override + public V remove(Object key) { + return remove(objectToKey(key)); + } + + @Override + public Set<@O@> keySet() { + return keySet; + } + + @Override + public Set> entrySet() { + return entrySet; + } + + private @k@ objectToKey(Object key) { + return (@k@) ((@O@) key).@KEY_NUMBER_METHOD@(); + } + /** * Locates the index for the given key. This method probes using double hashing. * @@ -447,7 +465,7 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM for (;;) { if (values[index] == null) { keys[index] = oldKey; - values[index] = toInternal(oldVal); + values[index] = oldVal; break; } @@ -458,10 +476,115 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM } } + @Override + public String toString() { + if (isEmpty()) { + return "{}"; + } + StringBuilder sb = new StringBuilder(4 * size); + sb.append('{'); + boolean first = true; + for (int i = 0; i < values.length; ++i) { + V value = values[i]; + if (value != null) { + if (!first) { + sb.append(", "); + } + sb.append(keyToString(keys[i])).append('=').append(value == this ? "(this Map)" : + toExternal(value)); + first = false; + } + } + return sb.append('}').toString(); + } + /** - * Iterator for traversing the entries in this map. + * Helper method called by {@link #toString()} in order to convert a single map key into a string. + * This is protected to allow subclasses to override the appearance of a given key. */ - private final class IteratorImpl implements Iterator>, Entry { + protected String keyToString(@k@ key) { + return @O@.toString(key); + } + + /** + * Set implementation for iterating over the entries of the map. + */ + private final class EntrySet extends AbstractSet> { + @Override + public Iterator> iterator() { + return new MapIterator(); + } + + @Override + public int size() { + return @K@ObjectHashMap.this.size(); + } + } + + /** + * Set implementation for iterating over the keys. + */ + private final class KeySet extends AbstractSet<@O@> { + @Override + public int size() { + return @K@ObjectHashMap.this.size(); + } + + @Override + public boolean contains(Object o) { + return @K@ObjectHashMap.this.containsKey(o); + } + + @Override + public boolean remove(Object o) { + return @K@ObjectHashMap.this.remove(o) != null; + } + + @Override + public boolean retainAll(Collection retainedKeys) { + boolean changed = false; + for(Iterator> iter = entries().iterator(); iter.hasNext(); ) { + PrimitiveEntry entry = iter.next(); + if (!retainedKeys.contains(entry.key())) { + changed = true; + iter.remove(); + } + } + return changed; + } + + @Override + public void clear() { + @K@ObjectHashMap.this.clear(); + } + + @Override + public Iterator<@O@> iterator() { + return new Iterator<@O@>() { + private final Iterator> iter = entrySet.iterator(); + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public @O@ next() { + return iter.next().getKey(); + } + + @Override + public void remove() { + iter.remove(); + } + }; + } + } + + /** + * Iterator over primitive entries. Entry key/values are overwritten by each call to {@link #next()}. + */ + private final class PrimitiveIterator implements Iterator>, PrimitiveEntry { private int prevIndex = -1; private int nextIndex = -1; private int entryIndex = -1; @@ -483,7 +606,7 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM } @Override - public Entry next() { + public PrimitiveEntry next() { if (!hasNext()) { throw new NoSuchElementException(); } @@ -524,26 +647,68 @@ public class @K@ObjectHashMap implements @K@ObjectMap, Iterable<@K@ObjectM } } - @Override - public String toString() { - if (size == 0) { - return "{}"; + /** + * Iterator used by the {@link Map} interface. + */ + private final class MapIterator implements Iterator> { + private final PrimitiveIterator iter = new PrimitiveIterator(); + + @Override + public boolean hasNext() { + return iter.hasNext(); } - StringBuilder sb = new StringBuilder(4 * size); - for (int i = 0; i < values.length; ++i) { - V value = values[i]; - if (value != null) { - sb.append(sb.length() == 0 ? "{" : ", "); - sb.append(keyToString(keys[i])).append('=').append(value == this ? "(this Map)" : value); + + @Override + public Entry<@O@, V> next() { + if (!hasNext()) { + throw new NoSuchElementException(); } + + iter.next(); + + return new MapEntry(iter.entryIndex); + } + + @Override + public void remove() { + iter.remove(); } - return sb.append('}').toString(); } /** - * Helper method called by {@link #toString()} in order to convert a single map key into a string. + * A single entry in the map. */ - protected String keyToString(@k@ key) { - return @O@.toString(key); + final class MapEntry implements Entry<@O@, V> { + private final int entryIndex; + + MapEntry(int entryIndex) { + this.entryIndex = entryIndex; + } + + @Override + public @O@ getKey() { + verifyExists(); + return keys[entryIndex]; + } + + @Override + public V getValue() { + verifyExists(); + return toExternal(values[entryIndex]); + } + + @Override + public V setValue(V value) { + verifyExists(); + V prevValue = toExternal(values[entryIndex]); + values[entryIndex] = toInternal(value); + return prevValue; + } + + private void verifyExists() { + if (values[entryIndex] == null) { + throw new IllegalStateException("The map entry has been removed"); + } + } } } diff --git a/common/src/main/templates/io/netty/util/collection/KObjectMap.template b/common/src/main/templates/io/netty/util/collection/KObjectMap.template index 3d79c81fac..19466daf8e 100644 --- a/common/src/main/templates/io/netty/util/collection/KObjectMap.template +++ b/common/src/main/templates/io/netty/util/collection/KObjectMap.template @@ -14,21 +14,21 @@ */ package io.netty.util.collection; -import java.util.Collection; +import java.util.Map; /** * Interface for a primitive map that uses {@code @k@}s as keys. * * @param the value type stored in the map. */ -public interface @K@ObjectMap { +public interface @K@ObjectMap extends Map<@O@, V> { /** - * An Entry in the map. + * A primitive entry in the map, provided by the iterator from {@link #entries()} * * @param the value type stored in the map. */ - interface Entry { + interface PrimitiveEntry { /** * Gets the key for this entry. */ @@ -62,11 +62,6 @@ public interface @K@ObjectMap { */ V put(@k@ key, V value); - /** - * Puts all of the entries from the given map into this map. - */ - void putAll(@K@ObjectMap sourceMap); - /** * Removes the entry with the specified key. * @@ -76,48 +71,14 @@ public interface @K@ObjectMap { V remove(@k@ key); /** - * Returns the number of entries contained in this map. + * Gets an iterable to traverse over the primitive entries contained in this map. As an optimization, + * the {@link PrimitiveEntry}s returned by the {@link Iterator} may change as the {@link Iterator} + * progresses. The caller should not rely on {@link PrimitiveEntry} key/value stability. */ - int size(); - - /** - * Indicates whether or not this map is empty (i.e {@link #size()} == {@code 0]). - - */ - boolean isEmpty(); - - /** - * Clears all entries from this map. - */ - void clear(); + Iterable> entries(); /** * Indicates whether or not this map contains a value for the specified key. */ boolean containsKey(@k@ key); - - /** - * Indicates whether or not the map contains the specified value. - */ - boolean containsValue(V value); - - /** - * Gets an iterable collection of the entries contained in this map. - */ - Iterable> entries(); - - /** - * Gets the keys contained in this map. - */ - @k@[] keys(); - - /** - * Gets the values contained in this map. - */ - V[] values(Class clazz); - - /** - * Gets the values contatins in this map as a {@link Collection}. - */ - Collection values(); } diff --git a/common/src/test/templates/io.netty.util.collection/KObjectHashMapTest.template b/common/src/test/templates/io.netty.util.collection/KObjectHashMapTest.template index 5969e0a1b7..ed87c489f5 100644 --- a/common/src/test/templates/io.netty.util.collection/KObjectHashMapTest.template +++ b/common/src/test/templates/io.netty.util.collection/KObjectHashMapTest.template @@ -18,9 +18,10 @@ import org.junit.Before; import org.junit.Test; import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; import java.util.Random; import java.util.Set; @@ -87,6 +88,17 @@ public class @K@ObjectHashMapTest { assertEquals(v, map.get(key)); } + @Test + public void putNewMappingShouldSucceed_mapApi() { + Value v = new Value("v"); + @O@ key = (@O@)(@k@) 1; + assertNull(map.put(key, v)); + assertEquals(1, map.size()); + assertTrue(map.containsKey(key)); + assertTrue(map.containsValue(v)); + assertEquals(v, map.get(key)); + } + @Test public void putShouldReplaceValue() { Value v1 = new Value("v1"); @@ -103,6 +115,22 @@ public class @K@ObjectHashMapTest { assertEquals(v2, map.get(key)); } + @Test + public void putShouldReplaceValue_mapApi() { + Value v1 = new Value("v1"); + @O@ key = (@O@)(@k@) 1; + assertNull(map.put(key, v1)); + + // Replace the value. + Value v2 = new Value("v2"); + assertSame(v1, map.put(key, v2)); + + assertEquals(1, map.size()); + assertTrue(map.containsKey(key)); + assertTrue(map.containsValue(v2)); + assertEquals(v2, map.get(key)); + } + @Test public void putShouldGrowMap() { for (@k@ key = 0; key < (@k@) 255; ++key) { @@ -115,6 +143,19 @@ public class @K@ObjectHashMapTest { } } + @Test + public void putShouldGrowMap_mapApi() { + for (@k@ key = 0; key < (@k@) 255; ++key) { + @O@ okey = (@O@) key; + Value v = new Value(@O@.toString(key)); + assertNull(map.put(okey, v)); + assertEquals(key + 1, map.size()); + assertTrue(map.containsKey(okey)); + assertTrue(map.containsValue(v)); + assertEquals(v, map.get(okey)); + } + } + @Test public void negativeKeyShouldSucceed() { Value v = new Value("v"); @@ -123,12 +164,26 @@ public class @K@ObjectHashMapTest { assertEquals(v, map.get((@k@) -3)); } + @Test + public void negativeKeyShouldSucceed_mapApi() { + Value v = new Value("v"); + map.put((@O@)(@k@) -3, v); + assertEquals(1, map.size()); + assertEquals(v, map.get((@O@)(@k@) -3)); + } + @Test public void removeMissingValueShouldReturnNull() { assertNull(map.remove((@k@) 1)); assertEquals(0, map.size()); } + @Test + public void removeMissingValueShouldReturnNull_mapApi() { + assertNull(map.remove((@O@)(@k@) 1)); + assertEquals(0, map.size()); + } + @Test public void removeShouldReturnPreviousValue() { Value v = new Value("v"); @@ -137,6 +192,14 @@ public class @K@ObjectHashMapTest { assertSame(v, map.remove(key)); } + @Test + public void removeShouldReturnPreviousValue_mapApi() { + Value v = new Value("v"); + @O@ key = (@O@)(@k@) 1; + map.put(key, v); + assertSame(v, map.remove(key)); + } + /** * This test is a bit internal-centric. We're just forcing a rehash to occur based on no longer * having any FREE slots available. We do this by adding and then removing several keys up to @@ -160,24 +223,84 @@ public class @K@ObjectHashMapTest { assertSame(v, map.get(key)); } + @Test + public void noFreeSlotsShouldRehash_mapApi() { + for (@k@ i = 0; i < 10; ++i) { + map.put(i, new Value(@O@.toString(i))); + // Now mark it as REMOVED so that size won't cause the rehash. + map.remove((@O@) i); + assertEquals(0, map.size()); + } + + // Now add an entry to force the rehash since no FREE slots are available in the map. + Value v = new Value("v"); + @O@ key = (@O@)(@k@) 1; + map.put(key, v); + assertEquals(1, map.size()); + assertSame(v, map.get(key)); + } + @Test public void putAllShouldSucceed() { + @K@ObjectHashMap other = new @K@ObjectHashMap(); + @k@ k1 = 1; @k@ k2 = 2; @k@ k3 = 3; Value v1 = new Value("v1"); Value v2 = new Value("v2"); Value v3 = new Value("v3"); - map.put(k1, v1); - map.put(k2, v2); - map.put(k3, v3); + other.put(k1, v1); + other.put(k2, v2); + other.put(k3, v3); - @K@ObjectHashMap map2 = new @K@ObjectHashMap(); - map2.putAll(map); - assertEquals(3, map2.size()); - assertSame(v1, map2.get(k1)); - assertSame(v2, map2.get(k2)); - assertSame(v3, map2.get(k3)); + map.putAll(other); + assertEquals(3, map.size()); + assertSame(v1, map.get(k1)); + assertSame(v2, map.get(k2)); + assertSame(v3, map.get(k3)); + } + + @Test + public void putAllShouldSucceed_mapApi() { + @K@ObjectHashMap other = new @K@ObjectHashMap(); + + @O@ k1 = (@O@)(@k@) 1; + @O@ k2 = (@O@)(@k@) 2; + @O@ k3 = (@O@)(@k@) 3; + Value v1 = new Value("v1"); + Value v2 = new Value("v2"); + Value v3 = new Value("v3"); + other.put(k1, v1); + other.put(k2, v2); + other.put(k3, v3); + + map.putAll(other); + assertEquals(3, map.size()); + assertSame(v1, map.get(k1)); + assertSame(v2, map.get(k2)); + assertSame(v3, map.get(k3)); + } + + @Test + public void putAllWithJavaMapShouldSucceed_mapApi() { + Map<@O@, Value> other = new HashMap<@O@, Value>(); + + @O@ k1 = (@O@)(@k@) 1; + @O@ k2 = (@O@)(@k@) 2; + @O@ k3 = (@O@)(@k@) 3; + Value v1 = new Value("v1"); + Value v2 = new Value("v2"); + Value v3 = new Value("v3"); + other.put(k1, v1); + other.put(k2, v2); + other.put(k3, v3); + + map.putAll(other); + assertEquals(3, map.size()); + assertSame(v1, map.get(k1)); + assertSame(v2, map.get(k2)); + assertSame(v3, map.get(k3)); } @Test @@ -201,6 +324,14 @@ public class @K@ObjectHashMapTest { assertTrue(map.containsValue(null)); } + @Test + public void containsValueShouldFindNull_mapApi() { + map.put((@O@)(@k@) 1, new Value("v1")); + map.put((@O@)(@k@) 2, null); + map.put((@O@)(@k@) 3, new Value("v2")); + assertTrue(map.containsValue(null)); + } + @Test public void containsValueShouldFindInstance() { Value v = new Value("v1"); @@ -210,6 +341,15 @@ public class @K@ObjectHashMapTest { assertTrue(map.containsValue(v)); } + @Test + public void containsValueShouldFindInstance_mapApi() { + Value v = new Value("v1"); + map.put((@O@)(@k@) 1, new Value("v2")); + map.put((@O@)(@k@) 2, new Value("v3")); + map.put((@O@)(@k@) 3, v); + assertTrue(map.containsValue(v)); + } + @Test public void containsValueShouldFindEquivalentValue() { map.put((@k@) 1, new Value("v1")); @@ -218,6 +358,14 @@ public class @K@ObjectHashMapTest { assertTrue(map.containsValue(new Value("v2"))); } + @Test + public void containsValueShouldFindEquivalentValue_mapApi() { + map.put((@O@)(@k@) 1, new Value("v1")); + map.put((@O@)(@k@) 2, new Value("v2")); + map.put((@O@)(@k@) 3, new Value("v3")); + assertTrue(map.containsValue(new Value("v2"))); + } + @Test public void containsValueNotFindMissingValue() { map.put((@k@) 1, new Value("v1")); @@ -226,6 +374,14 @@ public class @K@ObjectHashMapTest { assertFalse(map.containsValue(new Value("v4"))); } + @Test + public void containsValueNotFindMissingValue_mapApi() { + map.put((@O@)(@k@) 1, new Value("v1")); + map.put((@O@)(@k@) 2, new Value("v2")); + map.put((@O@)(@k@) 3, new Value("v3")); + assertFalse(map.containsValue(new Value("v4"))); + } + @Test public void iteratorShouldTraverseEntries() { @k@ k1 = 1; @@ -241,8 +397,8 @@ public class @K@ObjectHashMapTest { map.remove(k4); Set<@O@> found = new HashSet<@O@>(); - for (@K@ObjectMap.Entry entry : map.entries()) { - assertTrue(found.add(entry.key())); + for (@K@ObjectMap.Entry<@O@, Value> entry : map.entrySet()) { + assertTrue(found.add(entry.getKey())); } assertEquals(3, found.size()); assertTrue(found.contains(k1)); @@ -264,8 +420,8 @@ public class @K@ObjectHashMapTest { map.put(k4, new Value("v4")); map.remove(k4); - @k@[] keys = map.keys(); - assertEquals(3, keys.length); + Set<@O@> keys = map.keySet(); + assertEquals(3, keys.size()); Set<@O@> expected = new HashSet<@O@>(); expected.add(k1); @@ -298,25 +454,11 @@ public class @K@ObjectHashMapTest { // Ensure values() return all values. Set expected = new HashSet(); - Set actual = new HashSet(); - expected.add(v1); expected.add(v2); expected.add(v3); - Value[] valueArray = map.values(Value.class); - assertEquals(3, valueArray.length); - for (Value value : valueArray) { - assertTrue(actual.add(value)); - } - assertEquals(expected, actual); - actual.clear(); - - Collection valueCollection = map.values(); - assertEquals(3, valueCollection.size()); - for (Value value : valueCollection) { - assertTrue(actual.add(value)); - } + Set actual = new HashSet(map.values()); assertEquals(expected, actual); } @@ -332,6 +474,18 @@ public class @K@ObjectHashMapTest { } } + @Test + public void mapShouldSupportHashingConflicts_mapApi() { + for (int mod = 0; mod < 10; ++mod) { + for (int sz = 1; sz <= 101; sz += 2) { + @K@ObjectHashMap map = new @K@ObjectHashMap(sz); + for (int i = 0; i < 100; ++i) { + map.put((@O@)(@k@)(i * mod), ""); + } + } + } + } + @Test public void hashcodeEqualsTest() { @K@ObjectHashMap<@O@> map1 = new @K@ObjectHashMap<@O@>(); @@ -345,23 +499,27 @@ public class @K@ObjectHashMapTest { assertEquals(map1.hashCode(), map2.hashCode()); assertEquals(map1, map2); // Remove one "middle" element, maps should now be non-equals. - @k@[] keys = map1.keys(); - map2.remove(keys[50]); + Set<@O@> keys = map1.keySet(); + @O@ removed = null; + Iterator<@O@> iter = keys.iterator(); + for (int ix = 0; iter.hasNext() && ix < 50; ++ix) { + removed = iter.next(); + } + map2.remove(removed); assertFalse(map1.equals(map2)); // Put it back; will likely be in a different position, but maps will be equal again. - map2.put(keys[50], @O@.valueOf(map1.keys()[50])); + map2.put(removed, removed); assertEquals(map1, map2); assertEquals(map1.hashCode(), map2.hashCode()); // Make map2 have one extra element, will be non-equal. - map2.put((@k@) 100, (@k@) 100); + map2.put((@k@) 100, (@O@)(@k@) 100); assertFalse(map1.equals(map2)); // Rebuild map2 with elements in a different order, again the maps should be equal. // (These tests with same elements in different order also show that the hashCode // function does not depend on the internal ordering of entries.) map2.clear(); - Arrays.sort(keys); - for (@k@ key : keys) { - map2.put(key, @O@.valueOf(key)); + for (@O@ key : map1.keySet()) { + map2.put(key, key); } assertEquals(map1.hashCode(), map2.hashCode()); assertEquals(map1, map2); @@ -424,14 +582,14 @@ public class @K@ObjectHashMapTest { assertEquals(goodMap.size(), map.size()); @O@[] goodKeys = goodMap.keySet().toArray(new @O@[goodMap.size()]); Arrays.sort(goodKeys); - @k@ [] keys = map.keys(); + @O@[] keys = map.keySet().toArray(new @O@[map.size()]); Arrays.sort(keys); for (int i = 0; i < goodKeys.length; ++i) { - assertEquals((@k@) goodKeys[i], keys[i]); + assertEquals(goodKeys[i], keys[i]); } // Finally drain the map. - for (@k@ key : map.keys()) { + for (@k@ key : keys) { assertEquals(goodMap.remove(key), map.remove(key)); } assertTrue(map.isEmpty()); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index 64aed60c80..ae32526e55 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -28,6 +28,7 @@ import io.netty.util.internal.logging.InternalLoggerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Map; import java.util.Queue; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; @@ -289,8 +290,8 @@ final class EpollEventLoop extends SingleThreadEventLoop { } Collection array = new ArrayList(channels.size()); - for (IntObjectMap.Entry entry: channels.entries()) { - array.add(entry.value()); + for (AbstractEpollChannel channel: channels.values()) { + array.add(channel); } for (AbstractEpollChannel ch: array) {