Added a StreamableEnumSet class (not actually derived from

EnumSet, because Java's implementation of that is private).  The 
performance could conceivably be improved; Java uses two 
implementations, one using a single long to store the element 
flags (for enumerated types with 64 or fewer constants) and one 
using an array of longs (for types with more than 64).  This uses 
an array of bytes.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5011 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Andrzej Kapolka
2008-04-23 02:47:14 +00:00
parent 4328cc7875
commit 86e09be9c6
@@ -0,0 +1,237 @@
//
// $Id: StreamableHashSet.java 4893 2007-12-03 21:07:34Z dhoover $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.util;
import java.io.IOException;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.EnumSet;
import java.util.Iterator;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;
/**
* An {@link EnumSet} equivalent (not a subclass, because EnumSet's implementation is private)
* that can be streamed.
*
* @see Streamable
*/
public class StreamableEnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, Streamable
{
/**
* Creates a new, empty enum set for storing elements of the specified class.
*/
public StreamableEnumSet (Class<E> elementType)
{
_elementType = elementType;
initContents();
}
/**
* No-arg constructor for deserialization.
*/
public StreamableEnumSet ()
{
}
@Override // documentation inherited
public Iterator<E> iterator ()
{
return new Iterator<E>() {
public boolean hasNext () {
checkConcurrentModification();
return _count < _size;
}
public E next () {
checkConcurrentModification();
do {
_idx += (++_bit >> 3);
_bit &= 0x07;
} while ((_contents[_idx] & (1 << _bit)) == 0);
_count++;
return _elementType.getEnumConstants()[(_idx << 3) | _bit];
}
public void remove () {
checkConcurrentModification();
_contents[_idx] &= ~(1 << _bit);
_size--;
_count--;
_omodcount = ++_modcount;
}
protected void checkConcurrentModification () {
if (_modcount != _omodcount) {
throw new ConcurrentModificationException();
}
}
protected int _idx, _bit = -1;
protected int _count;
protected int _omodcount = _modcount;
};
}
@Override // documentation inherited
public int size ()
{
return _size;
}
@Override // documentation inherited
public boolean contains (Object o)
{
if (!_elementType.isInstance(o)) {
return false;
}
int ordinal = ((Enum)o).ordinal();
int idx = ordinal >> 3, mask = 1 << (ordinal & 0x07);
return (_contents[idx] & mask) != 0;
}
@Override // documentation inherited
public boolean add (E o)
{
int ordinal = _elementType.cast(o).ordinal();
int idx = ordinal >> 3, mask = 1 << (ordinal & 0x07);
if ((_contents[idx] & mask) == 0) {
_contents[idx] |= mask;
_size++;
_modcount++;
return true;
}
return false;
}
@Override // documentation inherited
public boolean remove (Object o)
{
if (!_elementType.isInstance(o)) {
return false;
}
int ordinal = ((Enum)o).ordinal();
int idx = ordinal >> 3, mask = 1 << (ordinal & 0x07);
if ((_contents[idx] & mask) != 0) {
_contents[idx] &= ~mask;
_size--;
_modcount++;
return true;
}
return false;
}
@Override // documentation inherited
public void clear ()
{
Arrays.fill(_contents, (byte)0);
_size = 0;
_modcount++;
}
@Override // documentation inherited
public Object clone ()
{
try {
// make a deep clone of the contents
StreamableEnumSet cset = (StreamableEnumSet)super.clone();
cset._contents = _contents.clone();
return cset;
} catch (CloneNotSupportedException e) {
return null; // won't happen
}
}
/**
* Writes our custom streamable fields.
*/
public void writeObject (ObjectOutputStream out)
throws IOException
{
if (!classDefinesElementType()) {
out.writeUTF(_elementType.getName());
}
out.write(_contents);
}
/**
* Reads our custom streamable fields.
*/
public void readObject (ObjectInputStream in)
throws IOException, ClassNotFoundException
{
if (!classDefinesElementType()) {
@SuppressWarnings("unchecked") Class<E> elementType =
(Class<E>)Class.forName(in.readUTF());
_elementType = elementType;
} else if (_elementType == null) {
throw new RuntimeException("No element type defined in constructor.");
}
initContents();
in.read(_contents);
updateSize();
}
/**
* Subclasses that only store elements of a single enum type (initialized in their no-arg
* constructors) can return <code>true</code> here to avoid the overhead of streaming the
* enum type for each instance.
*/
protected boolean classDefinesElementType ()
{
return false;
}
/**
* Creates the contents array.
*/
protected void initContents ()
{
int constants = _elementType.getEnumConstants().length;
_contents = new byte[(constants >> 3) + ((constants & 0x07) == 0 ? 0 : 1)];
}
/**
* Computes the size from the contents.
*/
protected void updateSize ()
{
_size = 0;
for (byte b : _contents) {
_size += Integer.bitCount(b & 0xFF);
}
}
/** The element type. */
protected Class<E> _elementType;
/** A byte array with bits set for each element in the set. */
protected byte[] _contents;
/** The number of elements in the set. */
protected int _size;
/** The modification count (used to detect concurrent modifications). */
protected int _modcount;
}