Xamarin Public Jenkins (auto-signing) e79aa3c0ed Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
2016-08-03 10:59:49 +00:00

732 lines
22 KiB
C#

//------------------------------------------------------------------------------
// <copyright file="MTNameTable.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">helenak</owner>
//------------------------------------------------------------------------------
#if MTNAMETABLE
using System;
using System.IO;
using System.Collections;
using System.Threading;
namespace System.Xml {
#if !SPLAY_MTNAMETABLE
// MTNameTable is a modified version of our normal NameTable
// that is thread-safe on read & write. The design is kept
// simple by using the Entry[] as the atomic update pivot point.
public class MTNameTable : XmlNameTable {
//
// Private types
//
class Entry {
internal string str;
internal int hashCode;
internal Entry next;
internal Entry( string str, int hashCode, Entry next ) {
this.str = str;
this.hashCode = hashCode;
this.next = next;
}
}
//
// Fields
//
Entry[] entries;
int count;
int hashCodeRandomizer;
//
// Constructor
//
public MTNameTable() {
entries = new Entry[32];
hashCodeRandomizer = Environment.TickCount;
}
//
// XmlNameTable public methods
//
public override string Add( string key ) {
if ( key == null ) {
throw new ArgumentNullException( "key" );
}
int len = key.Length;
if ( len == 0 ) {
return string.Empty;
}
int hashCode = len + hashCodeRandomizer;
// use key.Length to eliminate the rangecheck
for ( int i = 0; i < key.Length; i++ ) {
hashCode += ( hashCode << 7 ) ^ key[i];
}
// mix it a bit more
hashCode -= hashCode >> 17;
hashCode -= hashCode >> 11;
hashCode -= hashCode >> 5;
Entry[] entries = this.entries;
for ( Entry e = entries[hashCode & (entries.Length-1)];
e != null;
e = e.next ) {
if ( e.hashCode == hashCode && e.str.Equals( key ) ) {
return e.str;
}
}
return AddEntry( key, hashCode );
}
public override string Add( char[] key, int start, int len ) {
if ( len == 0 ) {
return string.Empty;
}
int hashCode = len + hashCodeRandomizer;
hashCode += ( hashCode << 7 ) ^ key[start]; // this will throw IndexOutOfRangeException in case the start index is invalid
int end = start+len;
for ( int i = start + 1; i < end; i++) {
hashCode += ( hashCode << 7 ) ^ key[i];
}
// mix it a bit more
hashCode -= hashCode >> 17;
hashCode -= hashCode >> 11;
hashCode -= hashCode >> 5;
Entry[] entries = this.entries;
for ( Entry e = entries[hashCode & (entries.Length-1)];
e != null;
e = e.next ) {
if ( e.hashCode == hashCode && TextEquals( e.str, key, start ) ) {
return e.str;
}
}
return AddEntry( new string( key, start, len ), hashCode );
}
public override string Get( string value ) {
if ( value == null ) {
throw new ArgumentNullException("value");
}
if ( value.Length == 0 ) {
return string.Empty;
}
int len = value.Length + hashCodeRandomizer;
int hashCode = len;
// use value.Length to eliminate the rangecheck
for ( int i = 0; i < value.Length; i++ ) {
hashCode += ( hashCode << 7 ) ^ value[i];
}
// mix it a bit more
hashCode -= hashCode >> 17;
hashCode -= hashCode >> 11;
hashCode -= hashCode >> 5;
Entry[] entries = this.entries;
for ( Entry e = entries[hashCode & (entries.Length-1)];
e != null;
e = e.next ) {
if ( e.hashCode == hashCode && e.str.Equals( value ) ) {
return e.str;
}
}
return null;
}
public override string Get( char[] key, int start, int len ) {
if ( len == 0 ) {
return string.Empty;
}
int hashCode = len + hashCodeRandomizer;
hashCode += ( hashCode << 7 ) ^ key[start]; // this will throw IndexOutOfRangeException in case the start index is invalid
int end = start+len;
for ( int i = start + 1; i < end; i++) {
hashCode += ( hashCode << 7 ) ^ key[i];
}
// mix it a bit more
hashCode -= hashCode >> 17;
hashCode -= hashCode >> 11;
hashCode -= hashCode >> 5;
Entry[] entries = this.entries;
for ( Entry e = entries[hashCode & (entries.Length-1)];
e != null;
e = e.next ) {
if ( e.hashCode == hashCode && TextEquals( e.str, key, start ) ) {
return e.str;
}
}
return null;
}
//
// Private methods
//
private string AddEntry( string str, int hashCode ) {
Entry e;
lock (this) {
Entry[] entries = this.entries;
int index = hashCode & entries.Length-1;
for ( e = entries[index]; e != null; e = e.next ) {
if ( e.hashCode == hashCode && e.str.Equals( str ) ) {
return e.str;
}
}
e = new Entry( str, hashCode, entries[index] );
entries[index] = e;
if ( count++ == mask ) {
Grow();
}
}
return e.str;
}
private void Grow() {
int newMask = mask * 2 + 1;
Entry[] oldEntries = entries;
Entry[] newEntries = new Entry[newMask+1];
// use oldEntries.Length to eliminate the rangecheck
for ( int i = 0; i < oldEntries.Length; i++ ) {
Entry e = oldEntries[i];
while ( e != null ) {
int newIndex = e.hashCode & newMask;
Entry tmp = e.next;
e.next = newEntries[newIndex];
newEntries[newIndex] = e;
e = tmp;
}
}
entries = newEntries;
mask = newMask;
}
private static bool TextEquals( string array, char[] text, int start ) {
// use array.Length to eliminate the rangecheck
for ( int i = 0; i < array.Length; i++ ) {
if ( array[i] != text[start+i] ) {
return false;
}
}
return true;
}
}
#else
// XmlNameTable implemented as a multi-threaded splay tree.
[Obsolete("This class is going away")]
public class MTNameTable : XmlNameTable {
internal MTNameTableNode rootNode;
internal ReaderWriterLock rwLock;
internal int timeout;
public MTNameTable( bool isThreadSafe, int timeout ) {
if (isThreadSafe) {
this.rwLock = new ReaderWriterLock();
this.timeout = timeout;
}
}
public MTNameTable( bool isThreadSafe ): this( isThreadSafe, Timeout.Infinite ) {
}
public MTNameTable(): this( false ) {
}
public IEnumerator GetEnumerator() {
return new MTNameTableEnumerator( this );
}
public override String Get( String value ) {
if (value == null) {
throw new ArgumentNullException("value");
}
MTNameTableName name = new MTNameTableName(value);
return Get( ref name );
}
public override String Get( char[] key, int start, int len ) {
if (key == null) {
throw new ArgumentNullException("key");
}
else {
if ((start < 0) || (len < 0) || (start > key.Length - len))
throw new ArgumentOutOfRangeException();
}
MTNameTableName name = new MTNameTableName(key, start, len);
return Get( ref name );
}
private String Get( ref MTNameTableName nn ) {
String name = null;
if (rootNode != null) {
if (rwLock != null)
rwLock.AcquireReaderLock(timeout);
MTNameTableNode currentNode = rootNode;
while (true) {
Int64 d = currentNode.Compare(ref nn);
if (d == 0) {
Promote( currentNode );
name = currentNode.value;
break;
}
else if (d < 0) {
if (currentNode.leftNode == null)
break;
currentNode = currentNode.leftNode;
}
else {
if (currentNode.rightNode == null)
break;
currentNode = currentNode.rightNode;
}
}
if (rwLock != null)
rwLock.ReleaseReaderLock();
}
return name;
}
// Find the matching string atom given a string, or
// insert a new one.
public override String Add(String value) {
if (value == null) {
throw new ArgumentNullException("value");
}
MTNameTableName name = new MTNameTableName( value );
return Add( ref name, rwLock != null ).value;
}
public override String Add(char[] key, int start, int len) {
if (key == null) {
throw new ArgumentNullException("key");
}
else {
if ((start < 0) || (len < 0) || (start > key.Length - len))
throw new ArgumentOutOfRangeException();
}
MTNameTableName name = new MTNameTableName( key, start, len );
return Add( ref name, rwLock != null ).value;
}
private MTNameTableNode Add( ref MTNameTableName name, bool fLock) {
if (fLock)
rwLock.AcquireReaderLock(timeout);
MTNameTableNode currentNode = rootNode;
while (true) {
if (currentNode == null) {
currentNode = AddRoot( ref name, fLock );
break;
}
else {
Int64 d = currentNode.Compare(ref name);
if (d == 0) {
Promote( currentNode );
break;
}
else if (d < 0) {
if (currentNode.leftNode == null) {
currentNode = AddLeft( currentNode, ref name, fLock );
break;
}
else {
currentNode = currentNode.leftNode;
}
}
else {
if (currentNode.rightNode == null) {
currentNode = AddRight( currentNode, ref name, fLock );
break;
}
else {
currentNode = currentNode.rightNode;
}
}
}
}
if (fLock)
rwLock.ReleaseReaderLock();
return currentNode;
}
// Sets the root node given a string
private MTNameTableNode AddRoot( ref MTNameTableName name, bool fLock ) {
MTNameTableNode newNode = null;
if (fLock) {
LockCookie lc = rwLock.UpgradeToWriterLock(timeout);
// recheck for failsafe against race-condition
if (rootNode == null) {
rootNode = newNode = new MTNameTableNode( ref name );
}
else {
// try again, with write-lock active
newNode = Add( ref name, false );
}
rwLock.DowngradeFromWriterLock(ref lc);
}
else {
rootNode = newNode = new MTNameTableNode( ref name );
}
return newNode;
}
// Adds the a node to the left of the specified node given a string
private MTNameTableNode AddLeft( MTNameTableNode node, ref MTNameTableName name, bool fLock ) {
MTNameTableNode newNode = null;
if (fLock) {
LockCookie lc = rwLock.UpgradeToWriterLock(timeout);
// recheck for failsafe against race-condition
if (node.leftNode == null) {
newNode = new MTNameTableNode( ref name );
node.leftNode = newNode;
newNode.parentNode = node;
}
else {
// try again, with write-lock active
newNode = Add( ref name, false );
}
rwLock.DowngradeFromWriterLock(ref lc);
}
else {
newNode = new MTNameTableNode( ref name );
node.leftNode = newNode;
newNode.parentNode = node;
}
return newNode;
}
// Adds the a node to the right of the specified node, given a string.
private MTNameTableNode AddRight( MTNameTableNode node, ref MTNameTableName name, bool fLock ) {
MTNameTableNode newNode = null;
if (fLock) {
LockCookie lc = rwLock.UpgradeToWriterLock(timeout);
// recheck for failsafe against race-condition
if (node.rightNode == null) {
newNode = new MTNameTableNode( ref name );
node.rightNode = newNode;
newNode.parentNode = node;
}
else {
// try again, with write-lock active
newNode = Add( ref name, false );
}
rwLock.DowngradeFromWriterLock(ref lc);
}
else {
newNode = new MTNameTableNode( ref name );
node.rightNode = newNode;
newNode.parentNode = node;
}
return newNode;
}
private const int threshhold = 20;
// Promote the node into the parent's position (1 ply closer to the rootNode)
private void Promote( MTNameTableNode node ) {
// count number of times promotion requested
node.counter++;
if (node != rootNode &&
node.counter > threshhold &&
node.counter > node.parentNode.counter * 2) {
if (rwLock != null) {
LockCookie lc = rwLock.UpgradeToWriterLock(timeout);
// recheck for failsafe against race-condition
if (node != rootNode &&
node.counter > threshhold &&
node.counter > node.parentNode.counter * 2) {
InternalPromote( node );
}
rwLock.DowngradeFromWriterLock(ref lc);
}
else {
InternalPromote( node );
}
}
}
private void InternalPromote( MTNameTableNode node ) {
MTNameTableNode parent = node.parentNode;
if (parent != null) {
MTNameTableNode grandParent = parent.parentNode;
if (parent.leftNode == node) {
parent.leftNode = node.rightNode;
node.rightNode = parent;
// update lineage
if (parent.leftNode != null)
parent.leftNode.parentNode = parent;
node.parentNode = grandParent;
parent.parentNode = node;
}
else {
parent.rightNode = node.leftNode;
node.leftNode = parent;
// update lineage
if (parent.rightNode != null)
parent.rightNode.parentNode = parent;
node.parentNode = grandParent;
parent.parentNode = node;
}
// fixup pointer to promoted node in grand parent
if (grandParent == null) {
rootNode = node;
}
else {
if (grandParent.leftNode == parent) {
grandParent.leftNode = node;
}
else {
grandParent.rightNode = node;
}
}
}
}
}
internal struct MTNameTableName {
internal String str;
internal char[] array;
internal int start;
internal int len;
internal Int64 hash;
public MTNameTableName( string str ) {
this.str = str;
this.hash = Hash(str);
this.array = null;
this.start = 0;
this.len = 0;
}
public MTNameTableName( char[] array, int start, int len ) {
this.array = array;
this.start = start;
this.len = len;
this.str = null;
this.hash = Hash(array, start, len);
}
static private Int64 Hash(String value) {
Int64 hash = 0;
int len = value.Length;
if (len > 0)
hash = (((Int64)value[0]) & 0xFF) << 48;
if (len > 1)
hash = hash | ((((Int64)value[1]) & 0xFF) << 32);
if (len > 2)
hash = hash | ((((Int64)value[2]) & 0xFF) << 16);
if (len > 3)
hash = hash | (((Int64)value[3]) & 0xFF);
return hash;
}
static private Int64 Hash(char[] key, int start, int len) {
Int64 hash = 0;
if (len > 0)
hash = (((Int64)key[start]) & 0xFF) << 48;
if (len > 1)
hash = hash | ((((Int64)key[start+1]) & 0xFF) << 32);
if (len > 2)
hash = hash | ((((Int64)key[start+2]) & 0xFF) << 16);
if (len > 3)
hash = hash | (((Int64)key[start+3]) & 0xFF);
return hash;
}
}
// A MTNameTable node.
internal class MTNameTableNode {
internal String value;
internal Int64 hash;
internal Int64 counter;
internal MTNameTableNode leftNode;
internal MTNameTableNode rightNode;
internal MTNameTableNode parentNode;
internal MTNameTableNode(ref MTNameTableName name ) {
if (name.str != null) {
value = name.str;
}
else {
value = new String(name.array, name.start, name.len);
}
this.hash = name.hash;
}
internal Int64 Compare( ref MTNameTableName name ) {
if (name.array != null) {
return Compare( name.hash, name.array, name.start, name.len );
}
else {
return Compare( name.hash, name.str );
}
}
private Int64 Compare(Int64 hash, string value) {
Int64 d = hash - this.hash;
if (d == 0) {
int valueLength = this.value.Length;
d = value.Length - valueLength;
// if length is not equal, break
if (d != 0)
return(Int64)d;
for (int ii = 4; ii < valueLength; ii++) {
int dd = value[ii] - this.value[ii];
if (dd != 0) {
d = dd;
break;
}
}
}
return(Int64)d;
}
private Int64 Compare(Int64 hash, char[] key, int start, int len) {
Int64 d = hash - this.hash;
if (d == 0) {
int valueLength = this.value.Length;
d = len - valueLength;
// if length is not equal, break
if (d != 0)
return(Int64)d;
for (int ii = 4; ii < valueLength; ii++) {
int dd = key[start + ii] - this.value[ii];
if (dd != 0) {
d = dd;
break;
}
}
}
return(Int64)d;
}
}
// Enumerates all the names (strings) of a MTNameTable
internal class MTNameTableEnumerator: IEnumerator {
private ArrayList names;
private int iName;
internal MTNameTableEnumerator( MTNameTable nt ) {
if (nt.rwLock != null)
nt.rwLock.AcquireReaderLock(nt.timeout);
names = new ArrayList();
Walk( nt.rootNode );
iName = -1;
if (nt.rwLock != null)
nt.rwLock.ReleaseReaderLock();
}
internal void Walk( MTNameTableNode node ) {
if (node != null) {
if (node.leftNode != null)
Walk( node.leftNode );
names.Add( node.value );
if (node.rightNode != null)
Walk( node.rightNode );
}
}
public void Reset() {
iName = -1;
}
public bool MoveNext() {
iName++;
return iName < names.Count;
}
public object Current {
get {
if (iName < names.Count)
return names[iName];
return null;
}
}
}
#endif
}
#endif