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

com.argot.util.StringWeakInterner Maven / Gradle / Ivy

package com.argot.util;

import java.lang.management.ManagementFactory;
import java.lang.ref.WeakReference;
import java.nio.CharBuffer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;

public class StringWeakInterner implements StringWeakInternerMBean
{
	private static StringWeakInterner interner = new StringWeakInterner();

	public static StringWeakInterner get()
	{
		return interner;
	}
	private IntObjectHashMap stringMap = new IntObjectHashMap();
	private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
	private final Lock r = rwl.readLock();
	private final Lock w = rwl.writeLock();

	private StringWeakInterner()
	{
		try
		{
			final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
			final ObjectName mbName = new ObjectName("com.argot:type=StringWeakInterner,Name=common");
			mbs.registerMBean(this, mbName);
		}
		catch (final MalformedObjectNameException | InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e)
		{
			System.out.println("Argot failed to register MBean u8utf8");
			e.printStackTrace();
		}
	}

	private int hash(final CharBuffer cb)
	{
		int h = 0;
		final int i = cb.limit();
		for (int p = cb.position(); p < i; p++)
		{

			h = 31 * h + cb.get(p);
		}

		return h;
	}

	private boolean compare(final CharBuffer cb, final String str)
	{

		if (str == null)
		{
			return false;
		}

		final int i = cb.limit();
		for (int p = cb.position(); p < i; p++)
		{
			if (cb.get(p) != str.charAt(p))
			{
				return false;
			}
		}
		return true;

	}

	public String get(final CharBuffer buffer)
	{
		// get the hash and divide by eight to reduce the hash
		final int hash = (hash(buffer) >> 3);

		String string = null;

		r.lock();

		// First find the correct UUIDEntry for this hash.
		StringEntry stringEntry = stringMap.get(hash);
		if (stringEntry == null)
		{
			string = add(hash, buffer);
		}
		else
		{
			// Look through the list to find the right one.
			string = stringEntry.string.get();

			while (!compare(buffer, string))
			{
				stringEntry = stringEntry.next;

				// End of the list so not there.
				if (stringEntry == null)
				{
					string = add(hash, buffer);
					break;
				}

				string = stringEntry.string.get();
			}

		}
		r.unlock();
		return string;
	}

	/*
	 * Doesn't return the number of objects, just the number of unique hashes. Indicative of size, but not accurate.
	 */
	public int size()
	{
		return stringMap.size();
	}

	/**
	 * Just required for testing purposes.
	 */
	public void reset()
	{
		stringMap = new IntObjectHashMap();
	}

	private String add(final int hash, final CharBuffer buffer)
	{
		String string = null;

		r.unlock();
		w.lock();
		// try again just in case we got blocked while another thread created the same item.
		StringEntry stringEntry = stringMap.get(hash);

		// Nothing for this hash yet.
		if (stringEntry == null)
		{

			string = buffer.toString();
			stringEntry = new StringEntry();
			stringEntry.string = new WeakReference(string);
			stringMap.put(hash, stringEntry);
		}
		else
		{
			string = stringEntry.string.get();
			StringEntry reuseEntry = null;
			while (!compare(buffer, string))
			{
				final StringEntry lastEntry = stringEntry;

				// This entry in the list can be re-used. The weakReference has lost its string.
				if (string == null)
				{
					reuseEntry = stringEntry;
				}

				// go to next one in the list.
				stringEntry = stringEntry.next;

				// End of the list so not there.
				if (stringEntry == null)
				{
					string = buffer.toString();

					if (reuseEntry != null)
					{
						stringEntry = reuseEntry;
						stringEntry.string = new WeakReference(string);

						// no need to link entry, it is already linked.
					}
					else
					{
						stringEntry = new StringEntry();
						stringEntry.string = new WeakReference(string);

						// link the last valid entry to the new entry.
						lastEntry.next = stringEntry;
					}

					break;
				}

				string = stringEntry.string.get();
			}

		}

		r.lock();
		w.unlock();

		return string;
	}

	private static class StringEntry
	{
		StringEntry next;
		WeakReference string;
	}

	@Override
	public int getSize()
	{
		return stringMap.size();
	}

	@Override
	public int getLength()
	{
		return stringMap.length();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy