[Mono-list] System.Collections.Stack
Garrett Rooney
rooneg@electricjellyfish.net
Mon, 16 Jul 2001 09:02:07 -0400
--jRHKVT23PllUwdXP
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Here is my implementation of System.Collections.Stack.
It passes all the tests that I put it through. I'm not positive I did the
thread safe wrapper correctly, so if someone could check that out I would
appreciate it.
Some unit tests will follow as soon as I get a chance to download and install
nunit. By the way, are there any conventions we are planning to use for the
tests?
-garrett
--
garrett rooney Unix was not designed to stop you from
rooneg@electricjellyfish.net doing stupid things, because that would
http://electricjellyfish.net/ stop you from doing clever things.
--jRHKVT23PllUwdXP
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="Stack.cs"
// -*- Mode: C; tab-width: 8; c-basic-offset: 8 -*-
//
// System.Collections.Stack
//
// Author:
// Garrett Rooney (rooneg@electricjellyfish.net)
//
// (C) 2001 Garrett Rooney
//
namespace System.Collections {
public class Stack : ICollection, IEnumerable, ICloneable {
// properties
private object[] contents;
private int current = -1;
private int count = 0;
private int capacity = 16;
private bool readOnly = false;
private bool synchronized = false;
private void Resize(int ncapacity) {
object[] ncontents = new object[ncapacity];
for (int i = 0; i < capacity; i++) {
ncontents[i] = contents[i];
}
capacity = ncapacity;
contents = ncontents;
}
public Stack() {
contents = new object[capacity];
}
public Stack(ICollection collection) {
capacity = collection.Count;
contents = new object[capacity];
current = capacity - 1;
count = capacity;
int i = 0;
foreach (object o in collection) {
contents[i++] = o;
}
}
public Stack(int c) {
capacity = c;
contents = new object[capacity];
}
// The Synchronized version of Stack uses lock(this) to make
// it thread safe. This should be ok, even though we wrap an
// Array, which is a Collection, since there is no way for the
// outside world to get a reference to the Array. If I'm
// wrong about this, then we should change lock(this) to
// lock(contents.SyncRoot).
private class SyncStack : Stack {
public SyncStack(Stack s) {
contents = s.contents;
current = s.current;
count = s.count;
capacity = s.capacity;
readOnly = s.readOnly;
synchronized = true;
}
public override int Count {
get { lock(this) { return count; } }
}
public override bool IsReadOnly {
get { lock(this) { return readOnly; } }
}
public override bool IsSynchronized {
get { lock(this) { return synchronized; } }
}
public override object SyncRoot {
get { lock(this) { return this; } }
}
public override void Clear() {
lock(this) { base.Clear(); }
}
public override object Clone() {
lock (this) { return base.Clone(); }
}
public override bool Contains(object obj) {
lock (this) { return base.Contains(obj); }
}
public override void CopyTo(Array array, int index) {
lock (this) { base.CopyTo(array, index); }
}
// As noted above, this uses lock(this), and if that
// turns out to be unsafe, it should be changed to
// lock(contents.SyncRoot).
private class SyncEnumerator : Enumerator {
internal SyncEnumerator(Stack s) : base(s) {}
public override object Current {
get {
lock (this) {
return base.Current;
}
}
}
public override bool MoveNext() {
lock (this) { return base.MoveNext(); }
}
public override void Reset() {
lock (this) { base.Reset(); }
}
}
public override IEnumerator GetEnumerator() {
lock (this) {
return new SyncEnumerator(this);
}
}
public override object Peek() {
lock (this) { return base.Peek(); }
}
public override object Pop() {
lock (this) { return base.Pop(); }
}
public override void Push(object obj) {
lock (this) { base.Push(obj); }
}
public override object[] ToArray() {
lock (this) { return base.ToArray(); }
}
}
public static Stack Syncronized(Stack s) {
if (s == null) {
throw new ArgumentNullException();
}
return new SyncStack(s);
}
public virtual int Count {
get { return count; }
}
public virtual bool IsReadOnly {
get { return readOnly; }
}
public virtual bool IsSynchronized {
get { return synchronized; }
}
// If using this for the SyncRoot is unsafe, we should use
// contents.SyncRoot instead. I think this is ok though.
public virtual object SyncRoot {
get { return this; }
}
public virtual void Clear() {
for (int i = 0; i < count; i++) {
contents[i] = null;
}
count = 0;
current = -1;
}
public virtual object Clone() {
Stack stack;
if (synchronized == false) {
stack = new Stack();
stack.current = current;
stack.contents = contents;
stack.count = count;
stack.capacity = capacity;
stack.readOnly = readOnly;
stack.synchronized = synchronized;
} else {
stack = new SyncStack(this);
}
return stack;
}
public virtual bool Contains(object obj) {
if (count == 0)
return false;
for (int i = 0; i < count; i++) {
if (contents[i].Equals(obj))
return true;
}
return false;
}
public virtual void CopyTo (Array array, int index) {
if (array == null) {
throw new ArgumentNullException();
}
if (index < 0) {
throw new ArgumentOutOfRangeException();
}
if (array.Rank > 1 ||
index >= array.Length ||
count > array.Length - index) {
throw new ArgumentException();
}
for (int i = current; i != -1; i--) {
array.SetValue(contents[i],
count - (i + 1) + index);
}
}
// I made several methods of this class virtual, so that they
// could be overriden by a thread safe version of the
// Enumerator for use by SyncStack. I don't know if MS does
// that in their implimentation, but it seemed like one should
// reasonably be able to expect a thread safe Collection to
// return a thread safe Enumerator. If this is a problem, it
// could be ripped out. Realistically speaking, I doubt if
// many people would ever notice if the Enumerator was thread
// safe, as I cannot concieve of a situation where an
// Enumerator would be accessed by more than one thread.
private class Enumerator : IEnumerator {
private Array contents;
private int current;
private int count;
private int begin;
internal Enumerator(Stack s) {
// this is odd. it seems that you need to
// start one further ahead than current, since
// MoveNext() gets called first when using an
// Enumeration...
begin = s.current + 1;
current = begin;
count = s.count;
contents = (Array) s.contents.Clone();
}
public virtual object Current {
get {
if (current == -1 || current > count)
throw new InvalidOperationException();
return contents.GetValue(current);
}
}
public virtual bool MoveNext() {
if (current == -1) {
throw new InvalidOperationException();
}
current--;
if (current == -1) {
return false;
} else {
return true;
}
}
public virtual void Reset() {
// start one ahead of stack.current, so the
// first MoveNext() will put us at the top
current = begin;
}
}
public virtual IEnumerator GetEnumerator() {
return new Enumerator(this);
}
public virtual object Peek() {
if (current == -1) {
throw new InvalidOperationException();
} else {
return contents[current];
}
}
public virtual object Pop() {
if (current == -1) {
throw new InvalidOperationException();
} else {
object ret = contents[current];
count--;
current--;
return ret;
}
}
// FIXME: We should probably be a bit smarter about our
// resizing. After a certain point, doubling isn't that smart.
// We just need to find out what that point is...
public virtual void Push(Object o) {
if (capacity == count) {
Resize(capacity * 2);
}
count++;
current++;
contents[current] = o;
}
public virtual object[] ToArray() {
object[] ret = new object[count];
Array.Copy(contents, ret, count);
// ret needs to be in LIFO order
Array.Reverse(ret);
return ret;
}
}
}
--jRHKVT23PllUwdXP--