Skip to content

Commit

Permalink
Start to factor out slot map container
Browse files Browse the repository at this point in the history
* Introduce the concept of a slot map owner.
* Remove `SlotMapContainer` when thread safety not required.
* Split large compute method.
* Only check for `EmbeddedSlotMap` promotion when resizing storage.
  • Loading branch information
aardvark179 authored Jan 23, 2025
1 parent cbe3473 commit 22f6f86
Show file tree
Hide file tree
Showing 9 changed files with 422 additions and 326 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void create() {
public Object embeddedInsert1Key(EmbeddedState state) {
Slot newSlot = null;
for (int i = 0; i < 100; i++) {
newSlot = state.emptyMap.modify(state.randomKeys[i], 0, 0);
newSlot = state.emptyMap.modify(null, state.randomKeys[i], 0, 0);
}
if (newSlot == null) {
throw new AssertionError();
Expand Down Expand Up @@ -109,7 +109,7 @@ public void create() {
public Object hashInsert1Key(HashState state) {
Slot newSlot = null;
for (int i = 0; i < 100; i++) {
newSlot = state.emptyMap.modify(state.randomKeys[i], 0, 0);
newSlot = state.emptyMap.modify(null, state.randomKeys[i], 0, 0);
}
if (newSlot == null) {
throw new AssertionError();
Expand Down Expand Up @@ -156,7 +156,7 @@ private static String makeRandomString() {
/** Insert a random key and value into the map */
private static String insertRandomEntry(SlotMap map) {
String key = makeRandomString();
Slot slot = map.modify(key, 0, 0);
Slot slot = map.modify(null, key, 0, 0);
slot.setValue(key, null, null);
return key;
}
Expand Down
93 changes: 53 additions & 40 deletions rhino/src/main/java/org/mozilla/javascript/EmbeddedSlotMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public Slot query(Object key, int index) {
* @param index index or 0 if slot holds property name.
*/
@Override
public Slot modify(Object key, int index, int attributes) {
public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) {
final int indexOrHash = (key != null ? key.hashCode() : index);
Slot slot;

Expand All @@ -110,13 +110,12 @@ public Slot modify(Object key, int index, int attributes) {
}
}

// A new slot has to be inserted.
Slot newSlot = new Slot(key, index, attributes);
createNewSlot(newSlot);
createNewSlot(owner, newSlot);
return newSlot;
}

private void createNewSlot(Slot newSlot) {
private void createNewSlot(SlotMapOwner owner, Slot newSlot) {
if (count == 0) {
// Always throw away old slots if any on empty insert.
slots = new Slot[INITIAL_SLOT_SIZE];
Expand All @@ -125,6 +124,11 @@ private void createNewSlot(Slot newSlot) {
// Check if the table is not too full before inserting.
if (4 * (count + 1) > 3 * slots.length) {
// table size must be a power of 2 -- always grow by x2!
if (count > SlotMapContainer.LARGE_HASH_SIZE) {
var newMap = new HashSlotMap(this, newSlot);
owner.setMap(newMap);
return;
}
Slot[] newSlots = new Slot[slots.length * 2];
copyTable(slots, newSlots);
slots = newSlots;
Expand All @@ -134,7 +138,8 @@ private void createNewSlot(Slot newSlot) {
}

@Override
public <S extends Slot> S compute(Object key, int index, SlotComputer<S> c) {
public <S extends Slot> S compute(
SlotMapOwner owner, Object key, int index, SlotComputer<S> c) {
final int indexOrHash = (key != null ? key.hashCode() : index);

if (slots != null) {
Expand All @@ -148,54 +153,62 @@ public <S extends Slot> S compute(Object key, int index, SlotComputer<S> c) {
prev = slot;
}
if (slot != null) {
// Modify or remove existing slot
S newSlot = c.compute(key, index, slot);
if (newSlot == null) {
// Need to delete this slot actually
removeSlot(slot, prev, slotIndex, key);
} else if (!Objects.equals(slot, newSlot)) {
// Replace slot in hash table
if (prev == slot) {
slots[slotIndex] = newSlot;
} else {
prev.next = newSlot;
}
newSlot.next = slot.next;
// Replace new slot in linked list, keeping same order
if (slot == firstAdded) {
firstAdded = newSlot;
} else {
Slot ps = firstAdded;
while ((ps != null) && (ps.orderedNext != slot)) {
ps = ps.orderedNext;
}
if (ps != null) {
ps.orderedNext = newSlot;
}
}
newSlot.orderedNext = slot.orderedNext;
if (slot == lastAdded) {
lastAdded = newSlot;
}
}
return newSlot;
return computeExisting(key, index, c, slot, prev, slotIndex);
}
}
return computeNew(owner, key, index, c);
}

// If we get here, we know we are potentially adding a new slot
private <S extends Slot> S computeNew(
SlotMapOwner owner, Object key, int index, SlotComputer<S> c) {
S newSlot = c.compute(key, index, null);
if (newSlot != null) {
createNewSlot(newSlot);
createNewSlot(owner, newSlot);
}
return newSlot;
}

private <S extends Slot> S computeExisting(
Object key, int index, SlotComputer<S> c, Slot slot, Slot prev, int slotIndex) {
// Modify or remove existing slot
S newSlot = c.compute(key, index, slot);
if (newSlot == null) {
// Need to delete this slot actually
removeSlot(slot, prev, slotIndex, key);
} else if (!Objects.equals(slot, newSlot)) {
// Replace slot in hash table
if (prev == slot) {
slots[slotIndex] = newSlot;
} else {
prev.next = newSlot;
}
newSlot.next = slot.next;
// Replace new slot in linked list, keeping same order
if (slot == firstAdded) {
firstAdded = newSlot;
} else {
Slot ps = firstAdded;
while ((ps != null) && (ps.orderedNext != slot)) {
ps = ps.orderedNext;
}
if (ps != null) {
ps.orderedNext = newSlot;
}
}
newSlot.orderedNext = slot.orderedNext;
if (slot == lastAdded) {
lastAdded = newSlot;
}
}
return newSlot;
}

@Override
public void add(Slot newSlot) {
public void add(SlotMapOwner owner, Slot newSlot) {
if (slots == null) {
slots = new Slot[INITIAL_SLOT_SIZE];
}
insertNewSlot(newSlot);
createNewSlot(owner, newSlot);
}

private void insertNewSlot(Slot newSlot) {
Expand Down
17 changes: 13 additions & 4 deletions rhino/src/main/java/org/mozilla/javascript/HashSlotMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,18 @@ public HashSlotMap() {
public HashSlotMap(SlotMap oldMap) {
map = new LinkedHashMap<>(oldMap.size());
for (Slot n : oldMap) {
add(n.copySlot());
add(null, n.copySlot());
}
}

public HashSlotMap(SlotMap oldMap, Slot newSlot) {
map = new LinkedHashMap<>(oldMap.dirtySize() + 1);
for (Slot n : oldMap) {
add(null, n.copySlot());
}
add(null, newSlot);
}

@Override
public int size() {
return map.size();
Expand All @@ -47,21 +55,22 @@ public Slot query(Object key, int index) {
}

@Override
public Slot modify(Object key, int index, int attributes) {
public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) {
Object name = makeKey(key, index);
return map.computeIfAbsent(name, n -> new Slot(key, index, attributes));
}

@SuppressWarnings("unchecked")
@Override
public <S extends Slot> S compute(Object key, int index, SlotComputer<S> c) {
public <S extends Slot> S compute(
SlotMapOwner owner, Object key, int index, SlotComputer<S> c) {
Object name = makeKey(key, index);
Slot ret = map.compute(name, (n, existing) -> c.compute(key, index, existing));
return (S) ret;
}

@Override
public void add(Slot newSlot) {
public void add(SlotMapOwner owner, Slot newSlot) {
Object name = makeKey(newSlot);
map.put(name, newSlot);
}
Expand Down
Loading

0 comments on commit 22f6f86

Please sign in to comment.