All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ru.shemplo.snowball.stuctures.CacheLine Maven / Gradle / Ivy

There is a newer version: 1.3.1
Show newest version
package ru.shemplo.snowball.stuctures;

import java.util.Comparator;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

public class CacheLine , V> {
    
    private final ConcurrentMap  VALUES = new ConcurrentHashMap <> ();
    private final AtomicReference > HEAD = new AtomicReference <> ();
    
    private final Comparator  ORDER;
    private final int SIZE_LIMIT;
    
    private K lastKnownMinKey;
    
    public CacheLine (int size, Comparator  comparator) {
        this.ORDER = comparator;
        this.SIZE_LIMIT = size;
    }
    
    private static class Node  {
        
        private final AtomicReference > 
            NEXT = new AtomicReference <> ();
        
        public final V VALUE; 
        public final K KEY;
        
        public Node (K key, V value) {
            this.VALUE = value;
            this.KEY = key;
        }
        
    }
    
    @Override
    public String toString () {
        StringBuilder sb = new StringBuilder ("[");
        Node  current = HEAD.get ();
        
        while (!Objects.isNull (current)) {
            sb.append ("<");
            sb.append (current.KEY);
            sb.append ("; ");
            sb.append (current.VALUE);
            sb.append (">");
            
            current = current.NEXT.get ();
        }
        
        sb.append ("]");
        return sb.toString ();
    }
    
    private static final Object HM_DUMMY = new Object ();
    
    public void insert (K key, V value) {
        if (Objects.isNull (key)) {
            throw new IllegalArgumentException ("key == null");
        }
        
        if (VALUES.containsKey (value)) { return; }
        // Sooner or later, this value will still be added
        VALUES.put (value, HM_DUMMY);
        
        Node  node = new Node <> (key, value);
        
        while (true) {
            // In case when CacheLine is empty
            if (HEAD.compareAndSet (null, node)) { break; }
            
            Node  current = HEAD.get (), previous = null;
            int carriage = 0;
            
            while (!Objects.isNull (current)) {
                if (ORDER.compare (key, current.KEY) > 0) {
                    break;
                }
                
                previous = current; carriage += 1;
                current = current.NEXT.get ();
            }
            
            // Key of inserting value is too small
            if (carriage >= SIZE_LIMIT) {
                // Perhaps another time
                VALUES.remove (value);
                
                Node  tail = previous.NEXT.get ();
                lastKnownMinKey = previous.KEY;
                previous.NEXT.set (null);
                
                while (!Objects.isNull (tail)) {
                    VALUES.remove (tail.VALUE);
                    tail = tail.NEXT.get ();
                }
                
                break;
            }
            
            if (Objects.isNull (previous)) { 
                // It means that inserting value has the biggest key
                
                node.NEXT.set (current);
                if (HEAD.compareAndSet (current, node)) {
                    break;
                }
            } else {
                // It means that new value will be inserted in center
                
                node.NEXT.set (current);
                if (previous.NEXT.compareAndSet (current, node)) {
                    break; 
                }
            }
        }
    }
    
    public void forEach (Consumer > action) {
        if (Objects.isNull (action)) {
            throw new IllegalArgumentException ("action == null");
        }
        
        Node  current = HEAD.get ();
        int carriage = 0;
        
        while (!Objects.isNull (current) && carriage < SIZE_LIMIT) {
            action.accept (Pair.mp (current.KEY, current.VALUE));
            current = current.NEXT.get ();
            carriage += 1;
        }
        
        if (!Objects.isNull (current) && !Objects.isNull (lastKnownMinKey)) {
            if (ORDER.compare (current.KEY, lastKnownMinKey) < 0) {
                this.lastKnownMinKey = current.KEY;
            }
            
            current.NEXT.set (null);
        }
    }
    
    public int getApproximateSize () {
        // It works strangely
        return VALUES.size ();
    }
    
    public K getLastKnownMinKey () {
        return lastKnownMinKey;
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy