Example #1
0
        // Implements IHashCodeProvider too, due to Hashtable requirements.
        public int GetHashCode(Object key)
        {
            String s = (String)key;

            return(FastResourceComparer.GetHashCode(s));
        }
        // After calling AddResource, Generate() writes out all resources to the
        // output stream in the system default format.
        // If an exception occurs during object serialization or during IO,
        // the .resources file is closed and deleted, since it is most likely
        // invalid.
        /// <include file='doc\ResourceWriter.uex' path='docs/doc[@for="ResourceWriter.Generate"]/*' />
        public void Generate()
        {
            if (_resourceList == null)
            {
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
            }
            try {
                BinaryWriter bw       = new BinaryWriter(_output, Encoding.UTF8);
                Type[]       types    = new Type[20];
                int          numTypes = 0;

                // Write out the ResourceManager header
                // Write out magic number
                bw.Write(ResourceManager.MagicNumber);

                // Write out ResourceManager header version number
                bw.Write(ResourceManager.HeaderVersionNumber);

                MemoryStream resMgrHeaderBlob = new MemoryStream(240);
                BinaryWriter resMgrHeaderPart = new BinaryWriter(resMgrHeaderBlob);

                // Write out class name of IResourceReader capable of handling
                // this file.  Don't include the version number of mscorlib,
                // since we'd have to ignore it when reading it in.
                resMgrHeaderPart.Write(ResourceManager.ResReaderTypeNameInternal);

                // Write out class name of the ResourceSet class best suited to
                // handling this file.
                resMgrHeaderPart.Write(ResourceManager.ResSetTypeName);
                resMgrHeaderPart.Flush();

                // Write number of bytes to skip over to get past ResMgr header
                bw.Write((int)resMgrHeaderBlob.Length);

                // Write the rest of the ResMgr header
                bw.Write(resMgrHeaderBlob.GetBuffer(), 0, (int)resMgrHeaderBlob.Length);
                // End ResourceManager header


                // Write out the RuntimeResourceSet header
                // Version number
                bw.Write(RuntimeResourceSet.Version);

                // number of resources
                bw.Write(_resourceList.Count);

                // Store values in temporary streams to write at end of file.
                int[]        nameHashes    = new int[_resourceList.Count];
                int[]        namePositions = new int[_resourceList.Count];
                int          curNameNumber = 0;
                MemoryStream nameSection   = new MemoryStream(_resourceList.Count * AverageNameSize);
                BinaryWriter names         = new BinaryWriter(nameSection, Encoding.Unicode);
                MemoryStream dataSection   = new MemoryStream(_resourceList.Count * AverageValueSize);
                BinaryWriter data          = new BinaryWriter(dataSection, Encoding.UTF8);
                IFormatter   objFormatter  = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence));

#if RESOURCE_FILE_FORMAT_DEBUG
                // Write NAMES right before the names section.
                names.Write(new byte[] { (byte)'N', (byte)'A', (byte)'M', (byte)'E', (byte)'S', (byte)'-', (byte)'-', (byte)'>' });

                // Write DATA at the end of the name table section.
                data.Write(new byte[] { (byte)'D', (byte)'A', (byte)'T', (byte)'A', (byte)'-', (byte)'-', (byte)'-', (byte)'>' });
#endif

                IDictionaryEnumerator items = _resourceList.GetEnumerator();
                // Write resource name and position to the file, and the value
                // to our temporary buffer.  Save Type as well.
                while (items.MoveNext())
                {
                    nameHashes[curNameNumber]      = FastResourceComparer.GetHashCode((String)items.Key);
                    namePositions[curNameNumber++] = (int)names.Seek(0, SeekOrigin.Current);
                    names.Write((String)items.Key);                     // key
                    names.Write((int)data.Seek(0, SeekOrigin.Current)); // virtual offset of value.
#if RESOURCE_FILE_FORMAT_DEBUG
                    names.Write((byte)'*');
#endif
                    Object value = items.Value;

                    // Find type table index.  If not there, add new element.
                    // We will use -1 as the type for null objects
                    Type typeOfValue = (value == null) ? null : value.GetType();
                    if (value != null)
                    {
                        int i = 0;
                        for (; i < numTypes; i++)
                        {
                            if (types[i] == typeOfValue)
                            {
                                Write7BitEncodedInt(data, i);
                                break;
                            }
                        }
                        if (i == numTypes)
                        {
                            if (numTypes == types.Length)
                            {
                                Type[] newTypes = new Type[numTypes * 2];
                                Array.Copy(types, newTypes, numTypes);
                                types = newTypes;
                            }
                            types[numTypes] = typeOfValue;
                            Write7BitEncodedInt(data, numTypes++);
                        }
                    }
                    else
                    {
                        // -1 is the type for null.
                        Write7BitEncodedInt(data, -1);
                    }

                    // Write out value
                    WriteValue(typeOfValue, value, data, objFormatter);

#if RESOURCE_FILE_FORMAT_DEBUG
                    data.Write(new byte[] { (byte)'S', (byte)'T', (byte)'O', (byte)'P' });
#endif
                }

                // At this point, the ResourceManager header has been written.
                // Finish RuntimeResourceSet header
                //   Write size & contents of class table
                bw.Write(numTypes);
                for (int i = 0; i < numTypes; i++)
                {
                    bw.Write(types[i].AssemblyQualifiedName);
                }

                // Write out the name-related items for lookup.
                //  Note that the hash array and the namePositions array must
                //  be sorted in parallel.
                Array.Sort(nameHashes, namePositions);

                //  Prepare to write sorted name hashes (alignment fixup)
                //   Note: For 64-bit machines, these MUST be aligned on 4 byte
                //   boundaries!  Pointers on IA64 must be aligned!  And we'll
                //   run faster on X86 machines too.
                bw.Flush();
                int alignBytes = ((int)bw.BaseStream.Position) & 7;
                if (alignBytes > 0)
                {
                    for (int i = 0; i < 8 - alignBytes; i++)
                    {
                        bw.Write("PAD"[i % 3]);
                    }
                }

                //  Write out sorted name hashes.
                //   Align to 8 bytes.
                BCLDebug.Assert((bw.BaseStream.Position & 7) == 0, "ResourceWriter: Name hashes array won't be 8 byte aligned!  Ack!");
#if RESOURCE_FILE_FORMAT_DEBUG
                bw.Write(new byte[] { (byte)'H', (byte)'A', (byte)'S', (byte)'H', (byte)'E', (byte)'S', (byte)'-', (byte)'>' });
#endif
                foreach (int hash in nameHashes)
                {
                    bw.Write(hash);
                }
#if RESOURCE_FILE_FORMAT_DEBUG
                Console.Write("Name hashes: ");
                foreach (int hash in nameHashes)
                {
                    Console.Write(hash.ToString("x") + "  ");
                }
                Console.WriteLine();
#endif

                //  Write relative positions of all the names in the file.
                //   Note: this data is 4 byte aligned, occuring immediately
                //   after the 8 byte aligned name hashes (whose length may
                //   potentially be odd).
                BCLDebug.Assert((bw.BaseStream.Position & 3) == 0, "ResourceWriter: Name positions array won't be 4 byte aligned!  Ack!");
#if RESOURCE_FILE_FORMAT_DEBUG
                bw.Write(new byte[] { (byte)'P', (byte)'O', (byte)'S', (byte)'-', (byte)'-', (byte)'-', (byte)'-', (byte)'>' });
#endif
                foreach (int pos in namePositions)
                {
                    bw.Write(pos);
                }
#if RESOURCE_FILE_FORMAT_DEBUG
                Console.Write("Name positions: ");
                foreach (int pos in namePositions)
                {
                    Console.Write(pos.ToString("x") + "  ");
                }
                Console.WriteLine();
#endif

                // Flush all BinaryWriters to MemoryStreams.
                bw.Flush();
                names.Flush();
                data.Flush();

                // Write offset to data section
                int startOfDataSection = (int)(bw.Seek(0, SeekOrigin.Current) + nameSection.Length);
                startOfDataSection += 4;  // We're writing an int to store this data, adding more bytes to the header
                BCLDebug.Log("RESMGRFILEFORMAT", "Generate: start of DataSection: 0x" + startOfDataSection.ToString("x") + "  nameSection length: " + nameSection.Length);
                bw.Write(startOfDataSection);

                // Write name section.
                bw.Write(nameSection.GetBuffer(), 0, (int)nameSection.Length);
                names.Close();

                // Write data section.
                BCLDebug.Assert(startOfDataSection == bw.Seek(0, SeekOrigin.Current), "ResourceWriter::Generate - start of data section is wrong!");
                bw.Write(dataSection.GetBuffer(), 0, (int)dataSection.Length);
                data.Close();
                bw.Flush();
    #if DEBUG
                if (ResourceManager.DEBUG >= 1)
                {
                    Console.WriteLine("Wrote out " + (resources.Length) + " resources.");
                }
    #endif
            }
            catch (Exception) {
                _output.Close();
                throw;
            }
            // Indicate we've called Generate
            _resourceList = null;
        }
Example #3
0
        // From a name, finds the associated virtual offset for the data.
        // This does a binary search through the names.
        internal int FindPosForResource(String name)
        {
            BCLDebug.Assert(_store != null, "ResourceReader is closed!");
            int hash = FastResourceComparer.GetHashCode(name);

            BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for " + name + "  hash: " + hash.ToString("x"));
            // Binary search over the hashes.  Use the _namePositions array to
            // determine where they exist in the underlying stream.
            int  lo      = 0;
            int  hi      = _numResources - 1;
            int  index   = -1;
            bool success = false;

            while (lo <= hi)
            {
                index = (lo + hi) >> 1;
                // Do NOT use subtraction here, since it will wrap for large
                // negative numbers.
                //int c = _nameHashes[index].CompareTo(hash);
                int currentHash = GetNameHash(index);
                int c;
                if (currentHash == hash)
                {
                    c = 0;
                }
                else if (currentHash < hash)
                {
                    c = -1;
                }
                else
                {
                    c = 1;
                }
                //BCLDebug.Log("RESMGRFILEFORMAT", "  Probing index "+index+"  lo: "+lo+"  hi: "+hi+"  c: "+c);
                if (c == 0)
                {
                    success = true;
                    break;
                }
                if (c < 0)
                {
                    lo = index + 1;
                }
                else
                {
                    hi = index - 1;
                }
            }
            if (!success)
            {
#if RESOURCE_FILE_FORMAT_DEBUG
                lock (this) {
                    _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(index), SeekOrigin.Begin);
                    String lastReadString = _store.ReadString();
                }
                BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for " + name + " failed.  i: " + index + "  lo: " + lo + "  hi: " + hi + "  last read string: \"" + lastReadString + "\"");
#endif
                return(-1);
            }

            // index is the location in our hash array that corresponds with a
            // value in the namePositions array.
            // There could be collisions in our hash function.  Check on both sides
            // of index to find the range of hash values that are equal to the
            // target hash value.
            if (lo != index)
            {
                lo = index;
                while (lo > 0 && GetNameHash(lo - 1) == hash)
                {
                    lo--;
                }
            }
            if (hi != index)
            {
                hi = index;
                while (hi < _numResources && GetNameHash(hi + 1) == hash)
                {
                    hi++;
                }
            }

            lock (this) {
                for (int i = lo; i <= hi; i++)
                {
                    _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(i), SeekOrigin.Begin);
                    if (CompareStringEqualsName(name))
                    {
                        int dataPos = _store.ReadInt32();
                        BCLDebug.Assert(dataPos >= 0 || dataPos < _store.BaseStream.Length - _dataSectionOffset, "Data section relative offset is out of the bounds of the data section!  dataPos: " + dataPos);
                        return(dataPos);
                    }
                }
            }
            BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for " + name + ": Found a hash collision, HOWEVER, neither of these collided values equaled the given string.");
            return(-1);
        }