Skip to content

Commit

Permalink
Merge pull request #35 from saalfeldlab/fix/serializationHashCollision
Browse files Browse the repository at this point in the history
Fix/serialization hash collision
  • Loading branch information
cmhulbert authored Oct 3, 2024
2 parents 7b2be4b + 9d1de36 commit e93ad47
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package net.imglib2.type.label;

class ComparableLabelMultisetEntryList extends LabelMultisetEntryList implements Comparable<LabelMultisetEntryList> {

private final LabelMultisetEntry ref;

ComparableLabelMultisetEntryList() {
this(new LabelMultisetEntry());
}

ComparableLabelMultisetEntryList(final LabelMultisetEntry ref) {

this.ref = ref;
}

ComparableLabelMultisetEntryList(final LabelMultisetEntryList list) {
this(list, new LabelMultisetEntry());

}
ComparableLabelMultisetEntryList(final LabelMultisetEntryList list, final LabelMultisetEntry ref) {

this.ref = ref;
referToDataAt(list.data, list.getBaseOffset());
}

@Override public LabelMultisetEntry createRef() {

return ref;
}

@Override public int compareTo(LabelMultisetEntryList o) {

final int size = size();

/* if different sizes or empty, return result of compare size*/
final int sizeCompare = Integer.compare(size, o.size());
if (sizeCompare != 0 || size == 0)
return sizeCompare;

final RefIterator<LabelMultisetEntry> thisIter = iterator();
final RefIterator<LabelMultisetEntry> otherIter = o.iterator();

for (int i = 0; i < size; i++) {
final LabelMultisetEntry thisNext = thisIter.next();
final LabelMultisetEntry otherNext = otherIter.next();

final int idCompare = Long.compare(thisNext.getId(), otherNext.getId());
if (idCompare != 0)
return idCompare;

final int countCompare = Integer.compare(thisNext.getCount(), otherNext.getCount());
if (countCompare != 0)
return countCompare;
}

return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import net.imglib2.type.label.LabelMultisetType.Entry;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
Expand Down Expand Up @@ -43,9 +42,9 @@ public boolean equals(Object o) {
if (!(o instanceof LabelMultisetEntryList)) return false;
final LabelMultisetEntryList other = (LabelMultisetEntryList)o;
if (other.size() != size()) return false;
final long[] thisData = ((LongMappedAccessData) data).getData();
final long[] otherData = ((LongMappedAccessData) other.data).getData();
return Arrays.equals(thisData, otherData);
final ComparableLabelMultisetEntryList thisComparable = new ComparableLabelMultisetEntryList(this);
final ComparableLabelMultisetEntryList otherComparable = new ComparableLabelMultisetEntryList(other);
return thisComparable.compareTo(otherComparable) == 0;
}

public LabelMultisetEntryList(final int capacity) {
Expand Down
15 changes: 10 additions & 5 deletions src/main/java/net/imglib2/type/label/LabelMultisetType.java
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,7 @@ public LabelMultisetType copy() {
access.isValid(),
access.argMaxCopy());
/* get a new type instance */
final LabelMultisetType that = new LabelMultisetType(null, accessCopy);
return that;
return new LabelMultisetType(null, accessCopy);
}
}

Expand Down Expand Up @@ -702,10 +701,16 @@ public int compareTo(final LabelMultisetType arg0) {

final long ours = argMax();
final long theirs = arg0.argMax();
final int initialComparison = Long.compare(ours, theirs);

if (initialComparison != 0) return initialComparison;
else return Long.compare(count(ours), count(theirs));
final int argMaxCompare = Long.compare(ours, theirs);
if (argMaxCompare != 0) return argMaxCompare;

final int countCompare = Long.compare(count(ours), count(theirs));
if (countCompare != 0) return countCompare;

final ComparableLabelMultisetEntryList thisComparable = new ComparableLabelMultisetEntryList(labelMultisetEntries());
final ComparableLabelMultisetEntryList otherComparable = new ComparableLabelMultisetEntryList(arg0.labelMultisetEntries());
return thisComparable.compareTo(otherComparable);
}

@Override
Expand Down
40 changes: 23 additions & 17 deletions src/main/java/net/imglib2/type/label/LabelUtils.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package net.imglib2.type.label;

import gnu.trove.impl.Constants;
import gnu.trove.map.hash.TIntIntHashMap;
import gnu.trove.map.hash.TIntLongHashMap;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.converter.Converters;
Expand All @@ -15,12 +13,13 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collection;
import java.util.Set;
import java.util.TreeMap;

import static gnu.trove.impl.Constants.DEFAULT_CAPACITY;
import static gnu.trove.impl.Constants.DEFAULT_LOAD_FACTOR;
import static net.imglib2.type.label.AbstractLabelMultisetLoader.argMaxListSizeInBytes;
import static net.imglib2.type.label.AbstractLabelMultisetLoader.listOffsetsSizeInBytes;
import static net.imglib2.type.label.ByteUtils.INT_SIZE;

public class LabelUtils {

Expand Down Expand Up @@ -52,40 +51,47 @@ public static byte[] serializeLabelMultisetTypes(

final LabelMultisetEntry entryReference = new LabelMultisetEntry(0, 1);

final int argMaxSize = INT_SIZE; // in reality, the int value is always `0`, with size of 4 bytes
final int offsetListSize = INT_SIZE * numElements;
final ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();
/* No longer serialized out ArgMax; we do this by specifying the ArgMax size as 0;
* It's now calculated during deserializtaion instead.*/
writeInt(dataBuffer, 0, ByteOrder.BIG_ENDIAN);

final TIntIntHashMap listOffsets = new TIntIntHashMap(DEFAULT_CAPACITY, Constants.DEFAULT_LOAD_FACTOR, -1, -1);
final TreeMap<ComparableLabelMultisetEntryList, Integer> listOffsets = new TreeMap<>();

int nextListOffset = 0;

int nonEmptyListCount = 0;
boolean allListsEmpty = true;

final ByteArrayOutputStream entryList = new ByteArrayOutputStream();
ComparableLabelMultisetEntryList listRef = new ComparableLabelMultisetEntryList();
for (final LabelMultisetType lmt : lmts) {
if (!(lmt.isEmpty() || (lmt.size() == 0) ))
nonEmptyListCount++;
final int listHash = lmt.listHashCode();
int listOffset = listOffsets.get(listHash);
if (listOffset != listOffsets.getNoEntryValue()) {

lmt.getAccess().getValue(lmt.index().get(), listRef);
final Integer listOffset = listOffsets.putIfAbsent(listRef, nextListOffset);
if (listOffset != null) {
writeInt(dataBuffer, listOffset, ByteOrder.BIG_ENDIAN);
} else {
writeInt(entryList, lmt.entrySet().size(), ByteOrder.LITTLE_ENDIAN);
for (final Entry<Label> entry : lmt.entrySetWithRef(entryReference)) {
/* deep copy the list (in-place) ONLY if we are a new list just added to the treeView */
final ComparableLabelMultisetEntryList swap = new ComparableLabelMultisetEntryList();
swap.addAll(listRef);
listRef.referToDataAt(swap.data, swap.getBaseOffset());
listRef = swap;

final Set<Entry<Label>> entries = lmt.entrySetWithRef(entryReference);
writeInt(entryList, entries.size(), ByteOrder.LITTLE_ENDIAN);
for (final Entry<Label> entry : entries) {
writeLong(entryList, entry.getElement().id(), ByteOrder.LITTLE_ENDIAN);
writeInt(entryList, entry.getCount(), ByteOrder.LITTLE_ENDIAN);
final int count = entry.getCount();
writeInt(entryList, count, ByteOrder.LITTLE_ENDIAN);

allListsEmpty = allListsEmpty && count == 0;
}
listOffsets.put(listHash, nextListOffset);
writeInt(dataBuffer, nextListOffset, ByteOrder.BIG_ENDIAN);
nextListOffset = entryList.size(); //Another quirk to maintain size compatibility, see list NOTE above.
}
}

if (nonEmptyListCount == 0)
if (allListsEmpty)
return null;

final byte[] entryListBytes = entryList.toByteArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ public int getArrayLength() {

public long argMax(final int offset) {

if (data.length == 0)
return Label.INVALID;
return this.argMax[offset];
}

Expand Down

0 comments on commit e93ad47

Please sign in to comment.