private void ParseDtdFromParserContext()

in System.Xml/System/Xml/Core/XmlTextReaderImpl.cs [7930:8687]


        private void ParseDtdFromParserContext() {
            Debug.Assert( dtdInfo == null && fragmentParserContext != null && fragmentParserContext.HasDtdInfo );

            IDtdParser dtdParser = DtdParser.Create();

            // Parse DTD
            dtdInfo = dtdParser.ParseFreeFloatingDtd(fragmentParserContext.BaseURI, fragmentParserContext.DocTypeName, fragmentParserContext.PublicId,
                                                     fragmentParserContext.SystemId, fragmentParserContext.InternalSubset, new DtdParserProxy( this ) );

#if SILVERLIGHT // Needed only for XmlTextReader or XmlValidatingReader
            if (dtdInfo.HasDefaultAttributes || dtdInfo.HasNonCDataAttributes) {
#else
            if ( ( validatingReaderCompatFlag || !v1Compat ) && ( dtdInfo.HasDefaultAttributes || dtdInfo.HasNonCDataAttributes ) ) {
#endif
                addDefaultAttributesAndNormalize = true;
            }
        }

        bool InitReadContentAsBinary() {
            Debug.Assert( parsingFunction != ParsingFunction.InReadContentAsBinary );

            if ( parsingFunction == ParsingFunction.InReadValueChunk ) {
                throw new InvalidOperationException( Res.GetString( Res.Xml_MixingReadValueChunkWithBinary ) );
            }
            if ( parsingFunction == ParsingFunction.InIncrementalRead ) {
                throw new InvalidOperationException( Res.GetString( Res.Xml_MixingV1StreamingWithV2Binary ) );
            }

            if ( !XmlReader.IsTextualNode( curNode.type ) ) {
                if ( !MoveToNextContentNode( false ) ) {
                    return false;
                }
            }

            SetupReadContentAsBinaryState( ParsingFunction.InReadContentAsBinary );
            incReadLineInfo.Set( curNode.LineNo, curNode.LinePos );
            return true;
        }

        bool InitReadElementContentAsBinary() {
            Debug.Assert( parsingFunction != ParsingFunction.InReadElementContentAsBinary );
            Debug.Assert( curNode.type == XmlNodeType.Element );

            bool isEmpty = curNode.IsEmptyElement;

            // move to content or off the empty element
            outerReader.Read();
            if ( isEmpty ) {
                return false;
            }

            // make sure we are on a content node
            if ( !MoveToNextContentNode( false ) ) {
                if ( curNode.type != XmlNodeType.EndElement ) {
                    Throw( Res.Xml_InvalidNodeType, curNode.type.ToString() );
                }
                // move off end element
                outerReader.Read();
                return false;
            }
            SetupReadContentAsBinaryState( ParsingFunction.InReadElementContentAsBinary );
            incReadLineInfo.Set( curNode.LineNo, curNode.LinePos );
            return true;
        }

        bool MoveToNextContentNode( bool moveIfOnContentNode ) {
            do {
                switch ( curNode.type ) {
                    case XmlNodeType.Attribute:
                        return !moveIfOnContentNode;
                    case XmlNodeType.Text:
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                    case XmlNodeType.CDATA:
                        if ( !moveIfOnContentNode ) {
                            return true;
                        }
                        break;
                    case XmlNodeType.ProcessingInstruction:
                    case XmlNodeType.Comment:
                    case XmlNodeType.EndEntity:
                        // skip comments, pis and end entity nodes
                        break;
                    case XmlNodeType.EntityReference:
                        outerReader.ResolveEntity();
                        break;
                    default:
                        return false;
                }
                moveIfOnContentNode = false;
            } while ( outerReader.Read() );
            return false;
        }

        void SetupReadContentAsBinaryState( ParsingFunction inReadBinaryFunction ) {
            if ( parsingFunction == ParsingFunction.PartialTextValue ) {
                incReadState = IncrementalReadState.ReadContentAsBinary_OnPartialValue;
            }
            else {
                incReadState = IncrementalReadState.ReadContentAsBinary_OnCachedValue;
                nextNextParsingFunction = nextParsingFunction;
                nextParsingFunction = parsingFunction;
            }
            readValueOffset = 0;
            parsingFunction = inReadBinaryFunction;
        }

        void SetupFromParserContext( XmlParserContext context, XmlReaderSettings settings ) {
            Debug.Assert( context != null );

            // setup nameTable
            XmlNameTable nt = settings.NameTable;
            nameTableFromSettings = ( nt != null );

            // get name table from namespace manager in XmlParserContext, if available; 
            if ( context.NamespaceManager != null ) {
                // must be the same as XmlReaderSettings.NameTable, or null
                if ( nt != null && nt != context.NamespaceManager.NameTable ) {
                    throw new XmlException( Res.Xml_NametableMismatch );
                }
                // get the namespace manager from context
                namespaceManager = context.NamespaceManager;
                xmlContext.defaultNamespace = namespaceManager.LookupNamespace( string.Empty );

                // get the nametable from ns manager
                nt = namespaceManager.NameTable;

                Debug.Assert( nt != null );
                Debug.Assert( context.NameTable == null || context.NameTable == nt, "This check should have been done in XmlParserContext constructor." );
            } 
            // get name table directly from XmlParserContext
            else if ( context.NameTable != null ) {
                // must be the same as XmlReaderSettings.NameTable, or null
                if ( nt != null && nt != context.NameTable ) {
                    throw new XmlException( Res.Xml_NametableMismatch, string.Empty );
                }
                nt = context.NameTable;
            }
            // no nametable provided -> create a new one
            else if ( nt == null ) {
                nt = new NameTable();
                Debug.Assert( nameTableFromSettings == false );
            }
            nameTable = nt;

            // make sure we have namespace manager
            if ( namespaceManager == null ) {
                namespaceManager = new XmlNamespaceManager( nt );
            }

            // copy xml:space and xml:lang
            xmlContext.xmlSpace = context.XmlSpace;
            xmlContext.xmlLang = context.XmlLang;
        }

//
// DtdInfo
//
#if !SILVERLIGHT
        internal override IDtdInfo DtdInfo {
            get {
                return dtdInfo;
            }
        }

        internal void SetDtdInfo(IDtdInfo newDtdInfo) {
            Debug.Assert( dtdInfo == null );

            dtdInfo = newDtdInfo;
            if ( dtdInfo != null ) {
                if ( ( validatingReaderCompatFlag || !v1Compat ) && ( dtdInfo.HasDefaultAttributes || dtdInfo.HasNonCDataAttributes ) ) {
                    addDefaultAttributesAndNormalize = true;
                }
            }
        }
#endif

        //
// Validation support
//

#if !SILVERLIGHT // no validation in Silverlight
        internal IValidationEventHandling ValidationEventHandling {
            set {
                validationEventHandling = value;
            }
        }

        internal OnDefaultAttributeUseDelegate OnDefaultAttributeUse {
            set { onDefaultAttributeUse = value; }
        }
#endif

//
// Internal properties for XmlValidatingReader
//
#if !SILVERLIGHT // Needed only for XmlValidatingReader

        internal bool XmlValidatingReaderCompatibilityMode { 
            set {
                validatingReaderCompatFlag = value;

                // Fix for VSWhidbey 516556; These namespaces must be added to the nametable for back compat reasons.
                if ( value ) {
                    nameTable.Add( XmlReservedNs.NsXs ); // Note: this is equal to XmlReservedNs.NsXsd in Everett
                    nameTable.Add( XmlReservedNs.NsXsi );
                    nameTable.Add( XmlReservedNs.NsDataType );
                }
            }
        }

        internal XmlNodeType FragmentType {
            get {
                return fragmentType;
            }
        }

        internal void ChangeCurrentNodeType( XmlNodeType newNodeType ) {
            Debug.Assert( curNode.type == XmlNodeType.Whitespace && newNodeType == XmlNodeType.SignificantWhitespace, "Incorrect node type change!" );
            curNode.type = newNodeType;
        }

        internal XmlResolver GetResolver() {
            if (IsResolverNull)
                return null;
            else
                return xmlResolver;
        }

        internal object InternalSchemaType {
            get {
                return curNode.schemaType;
            }
            set {
                curNode.schemaType = value;
            }
        }

        internal object InternalTypedValue {
            get {
                return curNode.typedValue;
            }
            set {
                curNode.typedValue = value;
            }
        }

        internal bool StandAlone {
            get {
                return standalone;
            }
        }

        internal override XmlNamespaceManager NamespaceManager {
            get {
                return namespaceManager;
            }
        }

        internal bool V1Compat {
            get {
                return v1Compat;
            }
        }

#endif
        internal ConformanceLevel V1ComformanceLevel {
            get {
                return fragmentType == XmlNodeType.Element ? ConformanceLevel.Fragment : ConformanceLevel.Document;
            }
        }

        private bool AddDefaultAttributeDtd(IDtdDefaultAttributeInfo defAttrInfo, bool definedInDtd, NodeData[] nameSortedNodeData) {

            if ( defAttrInfo.Prefix.Length > 0 ) {
                attrNeedNamespaceLookup = true;
            }

            string localName = defAttrInfo.LocalName;
            string prefix = defAttrInfo.Prefix;

            // check for duplicates
            if (nameSortedNodeData != null) {
                if (Array.BinarySearch<object>(nameSortedNodeData, defAttrInfo, DtdDefaultAttributeInfoToNodeDataComparer.Instance) >= 0) {
                    return false;
                }
            }
            else {
                for (int i = index + 1; i < index + 1 + attrCount; i++) {
                    if ((object)nodes[i].localName == (object)localName &&
                        (object)nodes[i].prefix == (object)prefix) {
                        return false;
                    }
                }
            }

            NodeData attr = AddDefaultAttributeInternal( defAttrInfo.LocalName, null, defAttrInfo.Prefix, defAttrInfo.DefaultValueExpanded, 
                                                         defAttrInfo.LineNumber, defAttrInfo.LinePosition, 
                                                         defAttrInfo.ValueLineNumber, defAttrInfo.ValueLinePosition, defAttrInfo.IsXmlAttribute );

            Debug.Assert(attr != null);

#if !SILVERLIGHT
            if (DtdValidation) {
                if (onDefaultAttributeUse != null) {
                    onDefaultAttributeUse(defAttrInfo, this);
                }
                attr.typedValue = defAttrInfo.DefaultValueTyped;
            }
#endif
            return attr != null;
        }

#if !SILVERLIGHT // Needed only for XmlValidatingReader
        internal bool AddDefaultAttributeNonDtd( SchemaAttDef attrDef ) {
            
            // atomize names - Xsd Validator does not need to have the same nametable
            string localName = nameTable.Add( attrDef.Name.Name );
            string prefix = nameTable.Add( attrDef.Prefix );
            string ns = nameTable.Add( attrDef.Name.Namespace );

            // atomize namespace - Xsd Validator does not need to have the same nametable
            if ( prefix.Length == 0 && ns.Length > 0 ) {
                prefix = namespaceManager.LookupPrefix( ns );

                Debug.Assert( prefix != null );
                if ( prefix == null ) {
                    prefix = string.Empty;
                }
            }

           // find out if the attribute is already there
            for (int i = index + 1; i < index + 1 + attrCount; i++) {
                if ((object)nodes[i].localName == (object)localName &&
                    (((object)nodes[i].prefix == (object)prefix) || ((object)nodes[i].ns == (object)ns && ns != null))) {
                    return false;
                }
            }

            // attribute does not exist -> we need to add it
            NodeData attr = AddDefaultAttributeInternal( localName, ns, prefix, attrDef.DefaultValueExpanded, 
                                                         attrDef.LineNumber, attrDef.LinePosition, 
                                                         attrDef.ValueLineNumber, attrDef.ValueLinePosition, attrDef.Reserved != SchemaAttDef.Reserve.None );
            Debug.Assert(attr != null);

            attr.schemaType = (attrDef.SchemaType == null) ? (object)attrDef.Datatype : (object)attrDef.SchemaType;
            attr.typedValue = attrDef.DefaultValueTyped;
            return true;
        }
#endif

        private NodeData AddDefaultAttributeInternal(string localName, string ns, string prefix, string value,
                                                     int lineNo, int linePos, int valueLineNo, int valueLinePos, bool isXmlAttribute) {
            // setup the attribute 
            NodeData attr = AddAttribute( localName, prefix, prefix.Length > 0 ? null : localName );
            if ( ns != null ) {
                attr.ns = ns;
            }

            attr.SetValue( value );
            attr.IsDefaultAttribute = true;
            attr.lineInfo.Set( lineNo, linePos );
            attr.lineInfo2.Set( valueLineNo, valueLinePos );

            // handle special attributes:
            if ( attr.prefix.Length == 0 ) {
                // default namespace declaration
                if ( Ref.Equal( attr.localName, XmlNs ) ) {
                    OnDefaultNamespaceDecl( attr );
                    if ( !attrNeedNamespaceLookup ) {
                        // change element default namespace
                        Debug.Assert( nodes[index].type == XmlNodeType.Element );
                        if ( nodes[index].prefix.Length == 0 ) {
                            nodes[index].ns = xmlContext.defaultNamespace;
                        }
                    }
                }
            }
            else {
                // prefixed namespace declaration
                if ( Ref.Equal( attr.prefix, XmlNs ) ) {
                    OnNamespaceDecl( attr );
                    if ( !attrNeedNamespaceLookup ) {
                        // change namespace of current element and attributes
                        string pref = attr.localName;
                        Debug.Assert( nodes[index].type == XmlNodeType.Element );
                        for ( int i = index; i < index + attrCount + 1; i++ ) {
                            if ( nodes[i].prefix.Equals( pref ) ) {
                                nodes[i].ns = namespaceManager.LookupNamespace( pref );
                            }
                        }
                    }
                }
                // xml: attribute
                else {
                    if ( isXmlAttribute ) {
                        OnXmlReservedAttribute( attr );
                    }
                }
            }

            fullAttrCleanup = true;
            return attr;
        }

#if !SILVERLIGHT // Needed only for XmlTextReader (when used from XmlDocument)
        internal bool DisableUndeclaredEntityCheck {
            set { 
                disableUndeclaredEntityCheck = value; 
            }
        }
#endif

        int ReadContentAsBinary( byte[] buffer, int index, int count ) { 
            Debug.Assert( incReadDecoder != null );

            if ( incReadState == IncrementalReadState.ReadContentAsBinary_End ) {
                return 0;
            }

            incReadDecoder.SetNextOutputBuffer( buffer, index, count );

            for (;;) {
                // read what is already cached in curNode
                int charsRead = 0;
                try {
                    charsRead = curNode.CopyToBinary( incReadDecoder, readValueOffset );
                }
                // add line info to the exception
                catch ( XmlException e ) {
                    curNode.AdjustLineInfo( readValueOffset, ps.eolNormalized, ref incReadLineInfo );
                    ReThrow( e, incReadLineInfo.lineNo, incReadLineInfo.linePos );
                }
                readValueOffset += charsRead;

                if ( incReadDecoder.IsFull ) {
                    return incReadDecoder.DecodedCount;
                }

                // if on partial value, read the rest of it
                if ( incReadState == IncrementalReadState.ReadContentAsBinary_OnPartialValue ) {
                    curNode.SetValue( string.Empty );

                    // read next chunk of text
                    bool endOfValue = false;
                    int startPos = 0;
                    int endPos = 0;
                    while ( !incReadDecoder.IsFull && !endOfValue ) {
                        int orChars = 0;

                        // store current line info and parse more text
                        incReadLineInfo.Set( ps.LineNo, ps.LinePos );
                        endOfValue = ParseText( out startPos, out endPos, ref orChars );

                        try {
                            charsRead = incReadDecoder.Decode( ps.chars, startPos, endPos - startPos ); 
                        }
                        // add line info to the exception
                        catch ( XmlException e ) {
                            ReThrow( e, incReadLineInfo.lineNo, incReadLineInfo.linePos);
                        }
                        startPos += charsRead;
                    }
                    incReadState = endOfValue ? IncrementalReadState.ReadContentAsBinary_OnCachedValue : IncrementalReadState.ReadContentAsBinary_OnPartialValue;
                    readValueOffset = 0;

                    if ( incReadDecoder.IsFull ) {
                        curNode.SetValue( ps.chars, startPos, endPos - startPos );
                        // adjust line info for the chunk that has been already decoded
                        AdjustLineInfo( ps.chars, startPos - charsRead, startPos, ps.eolNormalized, ref incReadLineInfo );
                        curNode.SetLineInfo( incReadLineInfo.lineNo, incReadLineInfo.linePos );
                        return incReadDecoder.DecodedCount;
                    }
                }

                // reset to normal state so we can call Read() to move forward
                ParsingFunction tmp = parsingFunction;
                parsingFunction = nextParsingFunction;
                nextParsingFunction = nextNextParsingFunction;

                // move to next textual node in the element content; throw on sub elements
                if ( !MoveToNextContentNode( true ) ) {
                    SetupReadContentAsBinaryState( tmp );
                    incReadState = IncrementalReadState.ReadContentAsBinary_End;
                    return incReadDecoder.DecodedCount;
                }
                SetupReadContentAsBinaryState( tmp );
                incReadLineInfo.Set( curNode.LineNo, curNode.LinePos );
            }
        }

        int ReadElementContentAsBinary( byte[] buffer, int index, int count ) { 
            if ( count == 0 ) {
                return 0;
            }
            int decoded = ReadContentAsBinary( buffer, index, count );
            if ( decoded > 0 ) {
                return decoded;
            }

            // if 0 bytes returned check if we are on a closing EndElement, throw exception if not
            if ( curNode.type != XmlNodeType.EndElement ) {
                throw new XmlException( Res.Xml_InvalidNodeType, curNode.type.ToString(), this as IXmlLineInfo );
            }

            // reset state
            parsingFunction = nextParsingFunction;
            nextParsingFunction = nextNextParsingFunction;
            Debug.Assert( parsingFunction != ParsingFunction.InReadElementContentAsBinary );

            // move off the EndElement
            outerReader.Read();
            return 0;
        }

        void InitBase64Decoder() {
            if ( base64Decoder == null ) {
                base64Decoder = new Base64Decoder();
            }
            else {
                base64Decoder.Reset();
            }
            incReadDecoder = base64Decoder;
        }

        void InitBinHexDecoder() {
            if ( binHexDecoder == null ) {
                binHexDecoder = new BinHexDecoder();
            }
            else {
                binHexDecoder.Reset();
            }
            incReadDecoder = binHexDecoder;
        }

        // SxS: URIs are resolved only to be compared. No resource exposure. It's OK to suppress the SxS warning.
#if !SILVERLIGHT
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.None)]
#endif
        bool UriEqual( Uri uri1, string uri1Str, string uri2Str, XmlResolver resolver ) {
            if (resolver == null) {
                return uri1Str == uri2Str;
            }
            if (uri1 == null){
                uri1 = resolver.ResolveUri( null, uri1Str );
            }
            Uri uri2 = resolver.ResolveUri( null, uri2Str );
            return uri1.Equals(uri2);
        }

        /// <summary>
        /// This method should be called every time the reader is about to consume some number of
        ///   characters from the input. It will count it agains the security counters and
        ///   may throw if some of the security limits are exceeded.
        /// </summary>
        /// <param name="characters">Number of characters to be consumed.</param>
        /// <param name="inEntityReference">true if the characters are result of entity expansion.</param>
        void RegisterConsumedCharacters(long characters, bool inEntityReference) {
            Debug.Assert(characters >= 0);
            if (maxCharactersInDocument > 0) {
                long newCharactersInDocument = charactersInDocument + characters;
                if (newCharactersInDocument < charactersInDocument) {
                    // Integer overflow while counting
                    ThrowWithoutLineInfo(Res.Xml_LimitExceeded, "MaxCharactersInDocument");
                }
                else {
                    charactersInDocument = newCharactersInDocument;
                }
                if (charactersInDocument > maxCharactersInDocument) {
                    // The limit was exceeded for the total number of characters in the document
                    ThrowWithoutLineInfo(Res.Xml_LimitExceeded, "MaxCharactersInDocument");
                }
            }

            if (maxCharactersFromEntities > 0 && inEntityReference) {
                long newCharactersFromEntities = charactersFromEntities + characters;
                if (newCharactersFromEntities < charactersFromEntities) {
                    // Integer overflow while counting
                    ThrowWithoutLineInfo(Res.Xml_LimitExceeded, "MaxCharactersFromEntities");
                }
                else {
                    charactersFromEntities = newCharactersFromEntities;
                }
                if ( charactersFromEntities > maxCharactersFromEntities) {
                    // The limit was exceeded for the number of characters from entities
                    ThrowWithoutLineInfo(Res.Xml_LimitExceeded, "MaxCharactersFromEntities");
                }
            }
        }

#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY
        [System.Security.SecuritySafeCritical]
#endif
        static internal unsafe void AdjustLineInfo(char[] chars, int startPos, int endPos, bool isNormalized, ref LineInfo lineInfo) {
            Debug.Assert(startPos >= 0);
            Debug.Assert(endPos < chars.Length);
            Debug.Assert(startPos <= endPos);

            fixed (char* pChars = &chars[startPos]) {
                AdjustLineInfo(pChars, endPos - startPos, isNormalized, ref lineInfo);
            }
        }

#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY
        [System.Security.SecuritySafeCritical]
#endif
        static internal unsafe void AdjustLineInfo(string str, int startPos, int endPos, bool isNormalized, ref LineInfo lineInfo) {
            Debug.Assert(startPos >= 0);
            Debug.Assert(endPos < str.Length);
            Debug.Assert(startPos <= endPos);

            fixed (char* pChars = str) {
                AdjustLineInfo(pChars + startPos, endPos - startPos, isNormalized, ref lineInfo);
            }
        }

#if SILVERLIGHT
        [System.Security.SecurityCritical]
#endif
        static internal unsafe void AdjustLineInfo(char* pChars, int length, bool isNormalized, ref LineInfo lineInfo) {
            int lastNewLinePos = -1;
            for (int i = 0; i < length; i++) {
                switch (pChars[i]) {
                    case '\n':
                        lineInfo.lineNo++;
                        lastNewLinePos = i;
                        break;
                    case '\r':
                        if (isNormalized) {
                            break;
                        }
                        lineInfo.lineNo++;
                        lastNewLinePos = i;
                        if (i + 1 < length && pChars[i + 1] == '\n') {
                            i++;
                            lastNewLinePos++;
                        }
                        break;
                }
            }
            if (lastNewLinePos >= 0) {
                lineInfo.linePos = length - lastNewLinePos;
            }
        }

        // StripSpaces removes spaces at the beginning and at the end of the value and replaces sequences of spaces with a single space
        internal static string StripSpaces( string value ) {
            int len = value.Length;
            if ( len <= 0 ) {
               return string.Empty;
            }

            int startPos = 0;
            StringBuilder norValue = null;

            while ( value[startPos] == 0x20 ) {
                startPos++;
                if ( startPos == len ) {
                    return " ";
                }
            }

            int i;
            for ( i = startPos; i < len; i++ ) {
                if ( value[i] == 0x20 ) {
                    int j = i + 1;
                    while ( j < len && value[j] == 0x20 ) {
                        j++;
                    }
                    if ( j == len ) {
                        if ( norValue == null ) {
                            return value.Substring( startPos, i - startPos );
                        }
                        else {
                            norValue.Append( value, startPos, i - startPos );
                            return norValue.ToString();
                        }
                   }
                   if ( j > i + 1 ) {
                        if ( norValue == null ) {
                            norValue = new StringBuilder( len );
                        }
                        norValue.Append( value, startPos, i - startPos + 1 );
                        startPos = j;
                        i = j - 1;
                    }
                }
            }
            if ( norValue == null ) {
                return ( startPos == 0 ) ? value : value.Substring( startPos, len - startPos );
            }
            else {
                if ( i > startPos ) {
                   norValue.Append( value, startPos, i - startPos );
               }
                return norValue.ToString();
            }
        }

        // StripSpaces removes spaces at the beginning and at the end of the value and replaces sequences of spaces with a single space
        internal static void StripSpaces( char[] value, int index, ref int len ) {
            if ( len <= 0 ) {
               return;
            }

            int startPos = index;
            int endPos = index + len;

            while ( value[startPos] == 0x20 ) {
                startPos++;
                if ( startPos == endPos ) {
                    len = 1;
                    return;
                }
            }

            int offset = startPos - index;
            int i;
            for ( i = startPos; i < endPos; i++ ) {
                char ch;
                if ( ( ch = value[i] ) == 0x20 ) {
                    int j = i + 1;
                    while ( j < endPos && value[j] == 0x20 ) {
                        j++;
                    }
                    if ( j == endPos ) {
                        offset += ( j - i );
                        break;
                    }
                    if ( j > i+1 ) {
                        offset += ( j - i - 1 );
                        i = j - 1;
                    }
                }
                value[i-offset] = ch;
            }
            len -= offset;
        }

        internal static void BlockCopyChars(char[] src, int srcOffset, char[] dst, int dstOffset, int count) {
            // PERF: Buffer.BlockCopy is faster than Array.Copy
#if SILVERLIGHT
            Array.Copy( src, srcOffset, dst, dstOffset, count );
#else
            Buffer.BlockCopy( src, srcOffset * sizeof(char), dst, dstOffset * sizeof(char), count * sizeof(char) );
#endif
        }

        internal static void BlockCopy( byte[] src, int srcOffset, byte[] dst, int dstOffset, int count ) {
#if SILVERLIGHT
            Array.Copy( src, srcOffset, dst, dstOffset, count );
#else
            Buffer.BlockCopy(src, srcOffset, dst, dstOffset, count);
#endif
        }

    }