* DataHelper:

- Undeprecate fromProperties() and toProperties()
      - Undeprecate BOOLEAN_FALSE and BOOLEAN_TRUE
      - Fix fromProperties() and toProperties() to correctly throw DataFormatExceptions
      - Add sort option
      - Efficiency improvements in writeProperties(), toProperties(), storeProps(), and toString()
This commit is contained in:
zzz
2011-03-23 21:44:38 +00:00
parent c85931cbc5
commit d6999a3327
3 changed files with 73 additions and 45 deletions

View File

@ -681,7 +681,11 @@ public class BlockfileNamingService extends DummyNamingService {
private static class PropertiesSerializer implements Serializer {
public byte[] getBytes(Object o) {
Properties p = (Properties) o;
return DataHelper.toProperties(p);
try {
return DataHelper.toProperties(p);
} catch (DataFormatException dfe) {
return null;
}
}
public Object construct(byte[] b) {

View File

@ -35,6 +35,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.zip.Deflater;
@ -102,6 +103,7 @@ public class DataHelper {
/**
* Write a mapping to the stream, as defined by the I2P data structure spec,
* and store it into a Properties object. See readProperties for the format.
* Output is sorted by property name.
* Property keys and values must not contain '=' or ';', this is not checked and they are not escaped
* Keys and values must be 255 bytes or less,
* Formatted length must not exceed 65535 bytes
@ -131,16 +133,46 @@ public class DataHelper {
*
* Use utf8 = false for RouterAddress (fast, non UTF-8)
* Use utf8 = true for SessionConfig (slow, UTF-8)
* @param props source may be null
*/
public static void writeProperties(OutputStream rawStream, Properties props, boolean utf8)
throws DataFormatException, IOException {
writeProperties(rawStream, props, utf8, props != null && !(props instanceof OrderedProperties));
}
/**
* Writes the props to the stream, sorted by property name if sort == true or
* if props is an OrderedProperties.
* See readProperties() for the format.
* Property keys and values must not contain '=' or ';', this is not checked and they are not escaped
* Keys and values must be 255 bytes or less,
* Formatted length must not exceed 65535 bytes
* @throws DataFormatException if either is too long.
*
* jrandom disabled UTF-8 in mid-2004, for performance reasons,
* i.e. slow foo.getBytes("UTF-8")
* Re-enable it so we can pass UTF-8 tunnel names through the I2CP SessionConfig.
*
* Use utf8 = false for RouterAddress (fast, non UTF-8)
* Use utf8 = true for SessionConfig (slow, UTF-8)
* @param props source may be null
* @param sort should we sort the properties? (set to false if already sorted, e.g. OrderedProperties)
* @since 0.8.6
*/
public static void writeProperties(OutputStream rawStream, Properties props, boolean utf8, boolean sort)
throws DataFormatException, IOException {
if (props != null) {
OrderedProperties p = new OrderedProperties();
p.putAll(props);
Properties p;
if (sort) {
p = new OrderedProperties();
p.putAll(props);
} else {
p = props;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(p.size() * 64);
for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = p.getProperty(key);
for (Map.Entry entry : p.entrySet()) {
String key = (String) entry.getKey();
String val = (String) entry.getValue();
if (utf8)
writeStringUTF8(baos, key);
else
@ -168,36 +200,36 @@ public class DataHelper {
* See readProperties() for the format.
* Property keys and values must not contain '=' or ';', this is not checked and they are not escaped
* Keys and values must be 255 bytes or less,
* @throws DataFormatException if too long.
* Formatted length must not exceed 65535 bytes
* Should throw DataFormatException if too long but it doesn't.
* Strings will be UTF-8 encoded in the byte array.
* Warning - confusing method name, Properties is the source.
*
* @deprecated unused
*
* @param target returned array as specified in data structure spec
* @param props source
* @param props source may be null
* @return new offset
* @throws DataFormatException if any string is over 255 bytes long, or if the total length
* is greater than 65535 bytes.
*/
@Deprecated
public static int toProperties(byte target[], int offset, Properties props) throws DataFormatException, IOException {
if (props != null) {
OrderedProperties p = new OrderedProperties();
p.putAll(props);
ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = p.getProperty(key);
// now make sure they're in UTF-8
//key = new String(key.getBytes(), "UTF-8");
//val = new String(val.getBytes(), "UTF-8");
writeString(baos, key);
ByteArrayOutputStream baos = new ByteArrayOutputStream(p.size() * 64);
for (Map.Entry entry : p.entrySet()) {
String key = (String) entry.getKey();
String val = (String) entry.getValue();
writeStringUTF8(baos, key);
baos.write(EQUAL_BYTES);
writeString(baos, val);
writeStringUTF8(baos, val);
baos.write(SEMICOLON_BYTES);
}
baos.close();
byte propBytes[] = baos.toByteArray();
if (propBytes.length > 65535)
throw new DataFormatException("Properties too big (65535 max): " + propBytes.length);
toLong(target, offset, 2, propBytes.length);
offset += 2;
System.arraycopy(propBytes, 0, target, offset, propBytes.length);
@ -213,14 +245,12 @@ public class DataHelper {
* Reads the props from the byte array and puts them in the Properties target
* See readProperties() for the format.
* Warning - confusing method name, Properties is the target.
*
* @deprecated unused
* Strings must be UTF-8 encoded in the byte array.
*
* @param source source
* @param target returned Properties
* @return new offset
*/
@Deprecated
public static int fromProperties(byte source[], int offset, Properties target) throws DataFormatException, IOException {
int size = (int)fromLong(source, offset, 2);
offset += 2;
@ -231,12 +261,12 @@ public class DataHelper {
String key = readString(in);
int read = read(in, eqBuf);
if ((read != eqBuf.length) || (!eq(eqBuf, EQUAL_BYTES))) {
break;
throw new DataFormatException("Bad key");
}
String val = readString(in);
read = read(in, semiBuf);
if ((read != semiBuf.length) || (!eq(semiBuf, SEMICOLON_BYTES))) {
break;
throw new DataFormatException("Bad value");
}
target.put(key, val);
}
@ -246,24 +276,20 @@ public class DataHelper {
/**
* Writes the props to returned byte array, not sorted
* (unless the opts param is an OrderedProperties)
* Strings will be UTF-8 encoded in the byte array.
* See readProperties() for the format.
* Property keys and values must not contain '=' or ';', this is not checked and they are not escaped
* Keys and values must be 255 bytes or less,
* Formatted length must not exceed 65535 bytes
* Warning - confusing method name, Properties is the source.
*
* @deprecated unused
*
* @throws RuntimeException if either is too long.
* @throws DataFormatException if key, value, or total is too long
*/
@Deprecated
public static byte[] toProperties(Properties opts) {
public static byte[] toProperties(Properties opts) throws DataFormatException {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(2);
writeProperties(baos, opts);
ByteArrayOutputStream baos = new ByteArrayOutputStream(2 + (32 * opts.size()));
writeProperties(baos, opts, true, false);
return baos.toByteArray();
} catch (DataFormatException dfe) {
throw new RuntimeException("Format error writing to memory?! " + dfe.getMessage());
} catch (IOException ioe) {
throw new RuntimeException("IO error writing to memory?! " + ioe.getMessage());
}
@ -276,9 +302,9 @@ public class DataHelper {
public static String toString(Properties options) {
StringBuilder buf = new StringBuilder();
if (options != null) {
for (Iterator iter = options.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = options.getProperty(key);
for (Map.Entry entry : options.entrySet()) {
String key = (String) entry.getKey();
String val = (String) entry.getValue();
buf.append("[").append(key).append("] = [").append(val).append("]");
}
} else {
@ -353,9 +379,9 @@ public class DataHelper {
try {
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
out.println("# NOTE: This I2P config file must use UTF-8 encoding");
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
String val = props.getProperty(name);
for (Map.Entry entry : props.entrySet()) {
String name = (String) entry.getKey();
String val = (String) entry.getValue();
out.println(name + "=" + val);
}
out.flush();
@ -720,10 +746,8 @@ public class DataHelper {
}
/** deprecated - used only in DatabaseLookupMessage */
@Deprecated
public static final byte BOOLEAN_TRUE = 0x1;
/** deprecated - used only in DatabaseLookupMessage */
@Deprecated
public static final byte BOOLEAN_FALSE = 0x0;
/** @deprecated unused */
@Deprecated

View File

@ -40,15 +40,15 @@ public class OrderedProperties extends Properties {
}
@Override
public Set entrySet() {
TreeSet rv = new TreeSet(new EntryComparator());
public Set<Map.Entry<Object, Object>> entrySet() {
TreeSet<Map.Entry<Object, Object>> rv = new TreeSet(new EntryComparator());
rv.addAll(super.entrySet());
return Collections.unmodifiableSortedSet(rv);
}
private static class EntryComparator implements Comparator {
public int compare(Object l, Object r) {
return ((String)((Map.Entry)l).getKey()).compareTo(((String)((Map.Entry)r).getKey()));
private static class EntryComparator implements Comparator<Map.Entry> {
public int compare(Map.Entry l, Map.Entry r) {
return ((String)l.getKey()).compareTo(((String)r.getKey()));
}
}
}