Improved memory/gc usage, replaced generic hashmaps with implementation

specific versions.
This commit is contained in:
nathan 2018-01-28 17:56:36 +01:00
parent 4d87ba10c1
commit dc54c9b226
5 changed files with 113 additions and 128 deletions

View File

@ -2,7 +2,9 @@
package dorkbox.network.dns; package dorkbox.network.dns;
import java.util.HashMap; import com.esotericsoftware.kryo.util.IntMap;
import dorkbox.util.collections.ObjectIntMap;
/** /**
* A utility class for converting between numeric codes and mnemonics * A utility class for converting between numeric codes and mnemonics
@ -13,29 +15,21 @@ import java.util.HashMap;
public public
class Mnemonic { class Mnemonic {
private static Integer cachedInts[] = new Integer[64];
/* Strings are case-sensitive. */ /* Strings are case-sensitive. */
public static final int CASE_SENSITIVE = 1; public static final int CASE_SENSITIVE = 1;
/* Strings will be stored/searched for in uppercase. */ /* Strings will be stored/searched for in uppercase. */
public static final int CASE_UPPER = 2; public static final int CASE_UPPER = 2;
/* Strings will be stored/searched for in lowercase. */ /* Strings will be stored/searched for in lowercase. */
public static final int CASE_LOWER = 3; public static final int CASE_LOWER = 3;
private static final int INVALID_VALUE = -1;
private ObjectIntMap<String> strings;
private HashMap<String, Integer> strings; private IntMap<String> values;
private HashMap<Integer, String> values;
private String description; private String description;
private int wordcase; private int wordcase;
private String prefix; private String prefix;
private int max; private int max;
private boolean numericok; private boolean numericok;
static {
for (int i = 0; i < cachedInts.length; i++) {
cachedInts[i] = new Integer(i);
}
}
/** /**
* Creates a new Mnemonic table. * Creates a new Mnemonic table.
* *
@ -48,8 +42,8 @@ class Mnemonic {
Mnemonic(String description, int wordcase) { Mnemonic(String description, int wordcase) {
this.description = description; this.description = description;
this.wordcase = wordcase; this.wordcase = wordcase;
strings = new HashMap(); strings = new ObjectIntMap<String>();
values = new HashMap(); values = new IntMap<String>();
max = Integer.MAX_VALUE; max = Integer.MAX_VALUE;
} }
@ -93,29 +87,17 @@ class Mnemonic {
/** /**
* Defines the text representation of a numeric value. * Defines the text representation of a numeric value.
* *
* @param val The numeric value * @param value The numeric value
* @param string The text string * @param string The text string
*/ */
public public
void add(int val, String string) { void add(int value, String string) {
check(val); check(value);
Integer value = toInteger(val);
string = sanitize(string); string = sanitize(string);
strings.put(string, value); strings.put(string, value);
values.put(value, string); values.put(value, string);
} }
/**
* Converts an int into a possibly cached Integer.
*/
public static
Integer toInteger(int val) {
if (val >= 0 && val < cachedInts.length) {
return (cachedInts[val]);
}
return new Integer(val);
}
/** /**
* Checks that a numeric value is within the range [0..max] * Checks that a numeric value is within the range [0..max]
*/ */
@ -130,13 +112,12 @@ class Mnemonic {
* Defines an additional text representation of a numeric value. This will * Defines an additional text representation of a numeric value. This will
* be used by getValue(), but not getText(). * be used by getValue(), but not getText().
* *
* @param val The numeric value * @param value The numeric value
* @param string The text string * @param string The text string
*/ */
public public
void addAlias(int val, String string) { void addAlias(int value, String string) {
check(val); check(value);
Integer value = toInteger(val);
string = sanitize(string); string = sanitize(string);
strings.put(string, value); strings.put(string, value);
} }
@ -154,6 +135,7 @@ class Mnemonic {
if (wordcase != source.wordcase) { if (wordcase != source.wordcase) {
throw new IllegalArgumentException(source.description + ": wordcases do not match"); throw new IllegalArgumentException(source.description + ": wordcases do not match");
} }
strings.putAll(source.strings); strings.putAll(source.strings);
values.putAll(source.values); values.putAll(source.values);
} }
@ -161,18 +143,19 @@ class Mnemonic {
/** /**
* Gets the text mnemonic corresponding to a numeric value. * Gets the text mnemonic corresponding to a numeric value.
* *
* @param val The numeric value * @param value The numeric value
* *
* @return The corresponding text mnemonic. * @return The corresponding text mnemonic.
*/ */
public public
String getText(int val) { String getText(int value) {
check(val); check(value);
String str = (String) values.get(toInteger(val)); String str = values.get(value);
if (str != null) { if (str != null) {
return str; return str;
} }
str = Integer.toString(val);
str = Integer.toString(value);
if (prefix != null) { if (prefix != null) {
return prefix + str; return prefix + str;
} }
@ -189,9 +172,10 @@ class Mnemonic {
public public
int getValue(String str) { int getValue(String str) {
str = sanitize(str); str = sanitize(str);
Integer value = (Integer) strings.get(str); int value = strings.get(str, INVALID_VALUE);
if (value != null) {
return value.intValue(); if (value != INVALID_VALUE) {
return value;
} }
if (prefix != null) { if (prefix != null) {
if (str.startsWith(prefix)) { if (str.startsWith(prefix)) {
@ -204,7 +188,8 @@ class Mnemonic {
if (numericok) { if (numericok) {
return parseNumeric(str); return parseNumeric(str);
} }
return -1;
return INVALID_VALUE;
} }
private private
@ -214,9 +199,9 @@ class Mnemonic {
if (val >= 0 && val <= max) { if (val >= 0 && val <= max) {
return val; return val;
} }
} catch (NumberFormatException e) { } catch (NumberFormatException ignored) {
} }
return -1;
}
return INVALID_VALUE;
}
} }

View File

@ -2,12 +2,11 @@
package dorkbox.network.dns.constants; package dorkbox.network.dns.constants;
import java.util.HashMap;
import dorkbox.network.dns.Mnemonic; import dorkbox.network.dns.Mnemonic;
import dorkbox.network.dns.exceptions.InvalidTypeException; import dorkbox.network.dns.exceptions.InvalidTypeException;
import dorkbox.network.dns.records.DnsRecord; import dorkbox.network.dns.records.DnsRecord;
import dorkbox.network.dns.records.DnsTypeProtoAssignment; import dorkbox.network.dns.records.DnsTypeProtoAssignment;
import dorkbox.util.collections.IntMap;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
/** /**
@ -449,25 +448,26 @@ class DnsRecordType {
public static public static
class TypeMnemonic extends Mnemonic { class TypeMnemonic extends Mnemonic {
private HashMap objects; private IntMap<DnsRecord> objects;
public public
TypeMnemonic() { TypeMnemonic() {
super("DnsRecordType", CASE_UPPER); super("DnsRecordType", CASE_UPPER);
setPrefix("TYPE"); setPrefix("TYPE");
objects = new HashMap(); objects = new IntMap<DnsRecord>();
} }
public public
void add(int val, String str, DnsRecord proto) { void add(int value, String str, DnsRecord proto) {
super.add(val, str); super.add(value, str);
objects.put(Mnemonic.toInteger(val), proto); objects.put(value, proto);
} }
@SuppressWarnings("unchecked")
public public
<T extends DnsRecord> T getProto(int val) { <T extends DnsRecord> T getProto(int value) {
check(val); check(value);
return (T) objects.get(toInteger(val)); return (T) objects.get(value);
} }
@Override @Override

View File

@ -5,16 +5,14 @@ package dorkbox.network.dns.records;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.crypto.Mac; import javax.crypto.Mac;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import com.esotericsoftware.kryo.util.ObjectMap;
import dorkbox.network.dns.DnsOutput; import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Name; import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsClass; import dorkbox.network.dns.constants.DnsClass;
@ -32,6 +30,7 @@ import dorkbox.util.Base64Fast;
* @see TSIGRecord * @see TSIGRecord
*/ */
@SuppressWarnings("WeakerAccess")
public public
class TSIG { class TSIG {
@ -72,9 +71,10 @@ class TSIG {
*/ */
public static final Name HMAC_SHA512 = Name.fromConstantString("hmac-sha512."); public static final Name HMAC_SHA512 = Name.fromConstantString("hmac-sha512.");
private static Map algMap; private static final ObjectMap<Name, String> algMap = new ObjectMap<Name, String>();
/** /**
* The default fudge value for outgoing packets. Can be overriden by the * The default fudge value for outgoing packets. Can be overridden by the
* tsigfudge option. * tsigfudge option.
*/ */
public static final short FUDGE = 300; public static final short FUDGE = 300;
@ -82,14 +82,12 @@ class TSIG {
private Mac hmac; private Mac hmac;
static { static {
Map out = new HashMap(); algMap.put(HMAC_MD5, "HmacMD5");
out.put(HMAC_MD5, "HmacMD5"); algMap.put(HMAC_SHA1, "HmacSHA1");
out.put(HMAC_SHA1, "HmacSHA1"); algMap.put(HMAC_SHA224, "HmacSHA224");
out.put(HMAC_SHA224, "HmacSHA224"); algMap.put(HMAC_SHA256, "HmacSHA256");
out.put(HMAC_SHA256, "HmacSHA256"); algMap.put(HMAC_SHA384, "HmacSHA384");
out.put(HMAC_SHA384, "HmacSHA384"); algMap.put(HMAC_SHA512, "HmacSHA512");
out.put(HMAC_SHA512, "HmacSHA512");
algMap = Collections.unmodifiableMap(out);
} }
/** /**
@ -143,7 +141,7 @@ class TSIG {
public static public static
String nameToAlgorithm(Name name) { String nameToAlgorithm(Name name) {
String alg = (String) algMap.get(name); String alg = algMap.get(name);
if (alg != null) { if (alg != null) {
return alg; return alg;
} }
@ -177,14 +175,13 @@ class TSIG {
public static public static
Name algorithmToName(String alg) { Name algorithmToName(String alg) {
Iterator it = algMap.entrySet()
.iterator(); // false identity check because it's string comparisons.
while (it.hasNext()) { Name foundKey = algMap.findKey(alg, false);
Map.Entry entry = (Map.Entry) it.next(); if (foundKey != null) {
if (alg.equalsIgnoreCase((String) entry.getValue())) { return foundKey;
return (Name) entry.getKey();
}
} }
throw new IllegalArgumentException("Unknown algorithm"); throw new IllegalArgumentException("Unknown algorithm");
} }

View File

@ -15,59 +15,66 @@ import java.util.TreeSet;
import dorkbox.network.dns.DnsInput; import dorkbox.network.dns.DnsInput;
import dorkbox.network.dns.DnsOutput; import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.Mnemonic;
import dorkbox.network.dns.constants.DnsRecordType; import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.exceptions.WireParseException; import dorkbox.network.dns.exceptions.WireParseException;
import dorkbox.network.dns.utils.Tokenizer; import dorkbox.network.dns.utils.Tokenizer;
import dorkbox.util.collections.IntMap;
import dorkbox.util.collections.IntMap.Keys;
final final
class TypeBitmap implements Serializable { class TypeBitmap implements Serializable {
private static final long serialVersionUID = -125354057735389003L; private static final long serialVersionUID = -125354057735389003L;
private TreeSet types; private IntMap<Boolean> types;
public public
TypeBitmap(int[] array) { TypeBitmap(int[] array) {
this(); this();
for (int i = 0; i < array.length; i++) { for (int i = 0; i < array.length; i++) {
DnsRecordType.check(array[i]); DnsRecordType.check(array[i]);
types.add(new Integer(array[i])); types.put(array[i], Boolean.TRUE);
} }
} }
private private
TypeBitmap() { TypeBitmap() {
types = new TreeSet(); types = new IntMap<Boolean>();
} }
public public
TypeBitmap(DnsInput in) throws WireParseException { TypeBitmap(DnsInput in) throws WireParseException {
this(); this();
int lastbase = -1; int lastbase = -1;
while (in.remaining() > 0) { while (in.remaining() > 0) {
if (in.remaining() < 2) { if (in.remaining() < 2) {
throw new WireParseException("invalid bitmap descriptor"); throw new WireParseException("invalid bitmap descriptor");
} }
int mapbase = in.readU8(); int mapbase = in.readU8();
if (mapbase < lastbase) { if (mapbase < lastbase) {
throw new WireParseException("invalid ordering"); throw new WireParseException("invalid ordering");
} }
int maplength = in.readU8(); int maplength = in.readU8();
if (maplength > in.remaining()) { if (maplength > in.remaining()) {
throw new WireParseException("invalid bitmap"); throw new WireParseException("invalid bitmap");
} }
for (int i = 0; i < maplength; i++) { for (int i = 0; i < maplength; i++) {
int current = in.readU8(); int current = in.readU8();
if (current == 0) { if (current == 0) {
continue; continue;
} }
for (int j = 0; j < 8; j++) { for (int j = 0; j < 8; j++) {
if ((current & (1 << (7 - j))) == 0) { if ((current & (1 << (7 - j))) == 0) {
continue; continue;
} }
int typecode = mapbase * 256 + +i * 8 + j; int typecode = mapbase * 256 + +i * 8 + j;
types.add(Mnemonic.toInteger(typecode)); types.put(typecode, Boolean.TRUE);
} }
} }
} }
@ -76,54 +83,71 @@ class TypeBitmap implements Serializable {
public public
TypeBitmap(Tokenizer st) throws IOException { TypeBitmap(Tokenizer st) throws IOException {
this(); this();
while (true) { while (true) {
Tokenizer.Token t = st.get(); Tokenizer.Token t = st.get();
if (!t.isString()) { if (!t.isString()) {
break; break;
} }
int typecode = DnsRecordType.value(t.value); int typecode = DnsRecordType.value(t.value);
if (typecode < 0) { if (typecode < 0) {
throw st.exception("Invalid type: " + t.value); throw st.exception("Invalid type: " + t.value);
} }
types.add(Mnemonic.toInteger(typecode));
types.put(typecode, Boolean.TRUE);
} }
st.unget(); st.unget();
} }
public public
int[] toArray() { int[] toArray() {
int[] array = new int[types.size()]; int[] array = new int[types.size];
int n = 0; int n = 0;
for (Iterator it = types.iterator(); it.hasNext(); ) {
array[n++] = ((Integer) it.next()).intValue();
Keys keys = types.keys();
while (keys.hasNext) {
array[n++] = keys.next();
} }
return array; return array;
} }
@Override
public public
String toString() { String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (Iterator it = types.iterator(); it.hasNext(); ) {
int t = ((Integer) it.next()).intValue(); Keys keys = types.keys();
sb.append(DnsRecordType.string(t)); while (keys.hasNext) {
if (it.hasNext()) { int t = keys.next();
sb.append(' '); sb.append(DnsRecordType.string(t))
} .append(' ');
} }
// remove the last ' '
int length = sb.length();
if (length > 1) {
sb.delete(length - 1, length);
}
return sb.toString(); return sb.toString();
} }
public public
void toWire(DnsOutput out) { void toWire(DnsOutput out) {
if (types.size() == 0) { if (types.size == 0) {
return; return;
} }
int mapbase = -1; int mapbase = -1;
TreeSet map = new TreeSet(); TreeSet map = new TreeSet();
for (Iterator it = types.iterator(); it.hasNext(); ) { Keys keys = types.keys();
int t = ((Integer) it.next()).intValue(); while (keys.hasNext) {
int t = keys.next();
int base = t >> 8; int base = t >> 8;
if (base != mapbase) { if (base != mapbase) {
if (map.size() > 0) { if (map.size() > 0) {
@ -134,20 +158,27 @@ class TypeBitmap implements Serializable {
} }
map.add(new Integer(t)); map.add(new Integer(t));
} }
mapToWire(out, map, mapbase); mapToWire(out, map, mapbase);
} }
/**
* @param map this must be an ordered data structure!
*/
private static private static
void mapToWire(DnsOutput out, TreeSet map, int mapbase) { void mapToWire(DnsOutput out, TreeSet map, int mapbase) {
int arraymax = (((Integer) map.last()).intValue()) & 0xFF; int arraymax = (((Integer) map.last()).intValue()) & 0xFF;
int arraylength = (arraymax / 8) + 1; int arraylength = (arraymax / 8) + 1;
int[] array = new int[arraylength]; int[] array = new int[arraylength];
out.writeU8(mapbase); out.writeU8(mapbase);
out.writeU8(arraylength); out.writeU8(arraylength);
for (Iterator it = map.iterator(); it.hasNext(); ) { for (Iterator it = map.iterator(); it.hasNext(); ) {
int typecode = ((Integer) it.next()).intValue(); int typecode = ((Integer) it.next()).intValue();
array[(typecode & 0xFF) / 8] |= (1 << (7 - typecode % 8)); array[(typecode & 0xFF) / 8] |= (1 << (7 - typecode % 8));
} }
for (int j = 0; j < arraylength; j++) { for (int j = 0; j < arraylength; j++) {
out.writeU8(array[j]); out.writeU8(array[j]);
} }
@ -155,12 +186,11 @@ class TypeBitmap implements Serializable {
public public
boolean empty() { boolean empty() {
return types.isEmpty(); return types.size == 0;
} }
public public
boolean contains(int typecode) { boolean contains(int typecode) {
return types.contains(Mnemonic.toInteger(typecode)); return types.containsKey(typecode);
} }
} }

View File

@ -54,39 +54,12 @@ class MnemonicTest extends TestCase {
m_mn = new Mnemonic(MnemonicTest.class.getName() + " UPPER", Mnemonic.CASE_UPPER); m_mn = new Mnemonic(MnemonicTest.class.getName() + " UPPER", Mnemonic.CASE_UPPER);
} }
public
void test_toInteger() {
Integer i = Mnemonic.toInteger(64);
assertEquals(new Integer(64), i);
Integer i2 = Mnemonic.toInteger(64);
assertEquals(i, i2);
assertNotSame(i, i2);
i = Mnemonic.toInteger(-1);
assertEquals(new Integer(-1), i);
i2 = Mnemonic.toInteger(-1);
assertEquals(i, i2);
assertNotSame(i, i2);
i = Mnemonic.toInteger(0);
assertEquals(new Integer(0), i);
i2 = Mnemonic.toInteger(0);
assertEquals(i, i2);
assertSame(i, i2);
i = Mnemonic.toInteger(63);
assertEquals(new Integer(63), i);
i2 = Mnemonic.toInteger(63);
assertEquals(i, i2);
assertSame(i, i2);
}
public public
void test_no_maximum() { void test_no_maximum() {
try { try {
m_mn.check(-1); m_mn.check(-1);
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException ignored) {
} }
try { try {
m_mn.check(0); m_mn.check(0);
@ -117,7 +90,7 @@ class MnemonicTest extends TestCase {
try { try {
m_mn.check(-1); m_mn.check(-1);
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException ignored) {
} }
try { try {
m_mn.check(0); m_mn.check(0);
@ -132,7 +105,7 @@ class MnemonicTest extends TestCase {
try { try {
m_mn.check(16); m_mn.check(16);
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException ignored) {
} }
// need numericok to exercise the usage of max in parseNumeric // need numericok to exercise the usage of max in parseNumeric