All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.netflix.zeno.fastblob.state.WeakObjectOrdinalMap Maven / Gradle / Ivy
/*
*
* Copyright 2014 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.netflix.zeno.fastblob.state;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Arrays;
/**
* Weak hash lookup map associate object references to already seen ordinals.
* The fundamental assumption made here is that objects are immutable, so that
* once the ordinal is assigned to an object, the ordinal stays the same
* throughout the life of the object.
*
* @author timurua
*
*/
public class WeakObjectOrdinalMap {
/**
* Hashmap entry
*/
public static final class Entry extends WeakReference {
// identity hashcode
private int hash;
// ordinal
private int ordinal;
// membership flags
private long imageMembershipsFlags;
// linked list pointer
private Entry next;
/**
* Creates new entry.
*/
Entry(Object key, ReferenceQueue queue, int hash, int ordinal, long imageMembershipsFlags, Entry next) {
super(key, queue);
this.hash = hash;
this.ordinal = ordinal;
this.imageMembershipsFlags = imageMembershipsFlags;
this.next = next;
}
public int getOrdinal() {
return ordinal;
}
public long getImageMembershipsFlags() {
return imageMembershipsFlags;
}
public boolean hasImageMembershipsFlags(long newImageMembershipsFlags) {
return (imageMembershipsFlags | newImageMembershipsFlags) == imageMembershipsFlags;
}
@Override
public String toString() {
Object v = get();
return v == null ? "null" : v.toString();
}
}
/**
* The map is divided into segments to increase concurrency
*/
private class Segment {
// The same concept as in HashMap. If the entry array is becoming too
// dense, it should be increased
private static final int LOAD_FACTOR_PERCENT = 75;
private static final int MINIMUM_CAPACITY = 256;
private static final int MAXIMUM_CAPACITY = (1<<30);
private int count = 0;
private int maxThreshold = 0;
private int minThreshold = 0;
private Entry[] entries;
private final ReferenceQueue queue = new ReferenceQueue();
public Segment(){
resize(MINIMUM_CAPACITY);
}
public synchronized void put(Object object, int hashCode, int ordinal, long imageMembershipsFlags) {
removeGarbageCollectedEntities();
int index = index(hashCode, entries.length);
Entry current = entries[index];
Entry prev = null;
while (current != null) {
if (current.hash == hashCode) {
Object currentObject = current.get();
if( currentObject == null){
deleteEntry(index, current, prev);
current = current.next;
continue;
} else if (currentObject == object) {
current.imageMembershipsFlags = (current.imageMembershipsFlags | imageMembershipsFlags);
return;
}
}
prev = current;
current = current.next;
}
count++;
Entry first = entries[index];
Entry entry = new Entry(object, queue, hashCode, ordinal, imageMembershipsFlags, first);
entries[index] = entry;
entry.next = first;
checkSize();
return;
}
public synchronized Entry get(Object object, int hashCode) {
removeGarbageCollectedEntities();
int index = index(hashCode, entries.length);
Entry current = entries[index];
Entry prev = null;
while (current != null) {
if (current.hash == hashCode) {
Object currentObject = current.get();
if( currentObject == null){
deleteEntry(index, current, prev);
current = current.next;
continue;
} else if (currentObject == object) {
return current;
}
}
prev = current;
current = current.next;
}
return null;
}
private void checkSize() {
if( count >= minThreshold && count <= maxThreshold ){
return;
}
int newCapacity;
if( count < minThreshold ) {
newCapacity = Math.max(MINIMUM_CAPACITY, entries.length >> 1);
} else {
newCapacity = Math.min(MAXIMUM_CAPACITY, entries.length << 1);
}
// nothing should be done, since capacity is not changed
if (newCapacity == entries.length) {
return;
}
resize(newCapacity);
}
private void resize(int newCapacity) {
Entry[] newEntries = new Entry[newCapacity];
if( entries != null){
for(Entry entry : entries){
Entry current = entry;
while(current != null){
Entry newEntry = current;
current = current.next;
int index = index(newEntry.hash, newEntries.length);
newEntry.next = newEntries[index];
newEntries[index] = newEntry;
}
}
}
minThreshold = (newEntries.length == MINIMUM_CAPACITY) ? 0 : (newEntries.length * LOAD_FACTOR_PERCENT / 200);
maxThreshold = (newEntries.length == MAXIMUM_CAPACITY) ? Integer.MAX_VALUE : newEntries.length * LOAD_FACTOR_PERCENT / 100;
entries = newEntries;
}
private void removeGarbageCollectedEntities() {
for (Object x; (x = queue.poll()) != null; ) {
Entry entry = (Entry) x;
int index = index(entry.hash, entries.length);
Entry current = entries[index];
Entry prev = null;
while (current != null) {
if (current == entry) {
deleteEntry(index, current, prev);
break;
}
prev = current;
current = current.next;
}
}
checkSize();
}
private void deleteEntry(int index, Entry current, Entry prev) {
count--;
if (prev != null) {
prev.next = current.next;
} else {
entries[index] = current.next;
}
}
private final int index(int hashCode, int capacity) {
return (hashCode >>> WeakObjectOrdinalMap.this.logOfSegmentNumber) % capacity;
}
public synchronized void clear() {
while (queue.poll() != null)
;
Arrays.fill(entries, null);
count = 0;
resize(MINIMUM_CAPACITY);
while (queue.poll() != null)
;
}
public synchronized int size() {
removeGarbageCollectedEntities();
return count;
}
}
private final Segment[] segments;
private final int mask;
private final int logOfSegmentNumber;
public WeakObjectOrdinalMap(int logOfSegmentNumber) {
if (logOfSegmentNumber < 1 && logOfSegmentNumber > 32) {
throw new RuntimeException("Invalid power level");
}
segments = new Segment[2 << logOfSegmentNumber];
for(int i=0; i