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

View File

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

View File

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

View File

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