| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- /*============================================================
- **
- **
- **
- **
- **
- ** Purpose: Default way to read streams of resources on
- ** demand.
- **
- ** Version 2 support on October 6, 2003
- **
- ===========================================================*/
- namespace System.Resources
- {
- using System;
- using System.IO;
- using System.Text;
- using System.Collections;
- using System.Collections.Generic;
- using System.Reflection;
- using System.Security;
- using System.Globalization;
- using System.Configuration.Assemblies;
- using System.Runtime.Versioning;
- using System.Diagnostics;
- using System.Diagnostics.Contracts;
- using System.Threading;
- // Provides the default implementation of IResourceReader, reading
- // .resources file from the system default binary format. This class
- // can be treated as an enumerator once.
- //
- // See the RuntimeResourceSet overview for details on the system
- // default file format.
- //
- internal struct ResourceLocator
- {
- internal object _value; // Can be null. Consider WeakReference instead?
- internal int _dataPos;
- internal ResourceLocator(int dataPos, object value)
- {
- _dataPos = dataPos;
- _value = value;
- }
- internal int DataPosition
- {
- get { return _dataPos; }
- //set { _dataPos = value; }
- }
- // Allows adding in profiling data in a future version, or a special
- // resource profiling build. We could also use WeakReference.
- internal object Value
- {
- get { return _value; }
- set { _value = value; }
- }
- internal static bool CanCache(ResourceTypeCode value)
- {
- Debug.Assert(value >= 0, "negative ResourceTypeCode. What?");
- return value <= ResourceTypeCode.LastPrimitive;
- }
- }
- public sealed class ResourceReader : IResourceReader
- {
- // A reasonable default buffer size for reading from files, especially
- // when we will likely be seeking frequently. Could be smaller, but does
- // it make sense to use anything less than one page?
- private const int DefaultFileStreamBufferSize = 4096;
- private BinaryReader _store; // backing store we're reading from.
- // Used by RuntimeResourceSet and this class's enumerator. Maps
- // resource name to a value, a ResourceLocator, or a
- // LooselyLinkedManifestResource.
- internal Dictionary<string, ResourceLocator> _resCache;
- private long _nameSectionOffset; // Offset to name section of file.
- private long _dataSectionOffset; // Offset to Data section of file.
- // Note this class is tightly coupled with UnmanagedMemoryStream.
- // At runtime when getting an embedded resource from an assembly,
- // we're given an UnmanagedMemoryStream referring to the mmap'ed portion
- // of the assembly. The pointers here are pointers into that block of
- // memory controlled by the OS's loader.
- private int[] _nameHashes; // hash values for all names.
- private unsafe int* _nameHashesPtr; // In case we're using UnmanagedMemoryStream
- private int[] _namePositions; // relative locations of names
- private unsafe int* _namePositionsPtr; // If we're using UnmanagedMemoryStream
- private Type[] _typeTable; // Lazy array of Types for resource values.
- private int[] _typeNamePositions; // To delay initialize type table
- private int _numResources; // Num of resources files, in case arrays aren't allocated.
- private readonly bool _permitDeserialization; // can deserialize BinaryFormatted resources
- private object _binaryFormatter; // binary formatter instance to use for deserializing
- // statics used to dynamically call into BinaryFormatter
- // When successfully located s_binaryFormatterType will point to the BinaryFormatter type
- // and s_deserializeMethod will point to an unbound delegate to the deserialize method.
- private static Type s_binaryFormatterType;
- private static Func<object, Stream, object> s_deserializeMethod;
- // We'll include a separate code path that uses UnmanagedMemoryStream to
- // avoid allocating String objects and the like.
- private UnmanagedMemoryStream _ums;
- // Version number of .resources file, for compatibility
- private int _version;
- public ResourceReader(string fileName)
- {
- _resCache = new Dictionary<string, ResourceLocator>(FastResourceComparer.Default);
- _store = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.RandomAccess), Encoding.UTF8);
- try
- {
- ReadResources();
- }
- catch
- {
- _store.Close(); // If we threw an exception, close the file.
- throw;
- }
- }
- public ResourceReader(Stream stream)
- {
- if (stream == null)
- throw new ArgumentNullException(nameof(stream));
- if (!stream.CanRead)
- throw new ArgumentException(SR.Argument_StreamNotReadable);
- _resCache = new Dictionary<string, ResourceLocator>(FastResourceComparer.Default);
- _store = new BinaryReader(stream, Encoding.UTF8);
- // We have a faster code path for reading resource files from an assembly.
- _ums = stream as UnmanagedMemoryStream;
- ReadResources();
- }
- // This is the constructor the RuntimeResourceSet calls,
- // passing in the stream to read from and the RuntimeResourceSet's
- // internal hash table (hash table of names with file offsets
- // and values, coupled to this ResourceReader).
- internal ResourceReader(Stream stream, Dictionary<string, ResourceLocator> resCache, bool permitDeserialization)
- {
- Debug.Assert(stream != null, "Need a stream!");
- Debug.Assert(stream.CanRead, "Stream should be readable!");
- Debug.Assert(resCache != null, "Need a Dictionary!");
- _resCache = resCache;
- _store = new BinaryReader(stream, Encoding.UTF8);
- _ums = stream as UnmanagedMemoryStream;
- _permitDeserialization = permitDeserialization;
- ReadResources();
- }
- public void Close()
- {
- Dispose(true);
- }
- public void Dispose()
- {
- Close();
- }
- private unsafe void Dispose(bool disposing)
- {
- if (_store != null)
- {
- _resCache = null;
- if (disposing)
- {
- // Close the stream in a thread-safe way. This fix means
- // that we may call Close n times, but that's safe.
- BinaryReader copyOfStore = _store;
- _store = null;
- if (copyOfStore != null)
- copyOfStore.Close();
- }
- _store = null;
- _namePositions = null;
- _nameHashes = null;
- _ums = null;
- _namePositionsPtr = null;
- _nameHashesPtr = null;
- }
- }
- internal static unsafe int ReadUnalignedI4(int* p)
- {
- byte* buffer = (byte*)p;
- // Unaligned, little endian format
- return buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
- }
- private void SkipString()
- {
- int stringLength = _store.Read7BitEncodedInt();
- if (stringLength < 0)
- {
- throw new BadImageFormatException(SR.BadImageFormat_NegativeStringLength);
- }
- _store.BaseStream.Seek(stringLength, SeekOrigin.Current);
- }
- private unsafe int GetNameHash(int index)
- {
- Debug.Assert(index >= 0 && index < _numResources, "Bad index into hash array. index: " + index);
- Debug.Assert((_ums == null && _nameHashes != null && _nameHashesPtr == null) ||
- (_ums != null && _nameHashes == null && _nameHashesPtr != null), "Internal state mangled.");
- if (_ums == null)
- return _nameHashes[index];
- else
- return ReadUnalignedI4(&_nameHashesPtr[index]);
- }
- private unsafe int GetNamePosition(int index)
- {
- Debug.Assert(index >= 0 && index < _numResources, "Bad index into name position array. index: " + index);
- Debug.Assert((_ums == null && _namePositions != null && _namePositionsPtr == null) ||
- (_ums != null && _namePositions == null && _namePositionsPtr != null), "Internal state mangled.");
- int r;
- if (_ums == null)
- r = _namePositions[index];
- else
- r = ReadUnalignedI4(&_namePositionsPtr[index]);
- if (r < 0 || r > _dataSectionOffset - _nameSectionOffset)
- {
- throw new FormatException(SR.Format(SR.BadImageFormat_ResourcesNameInvalidOffset, r));
- }
- return r;
- }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- public IDictionaryEnumerator GetEnumerator()
- {
- if (_resCache == null)
- throw new InvalidOperationException(SR.ResourceReaderIsClosed);
- return new ResourceEnumerator(this);
- }
- internal ResourceEnumerator GetEnumeratorInternal()
- {
- return new ResourceEnumerator(this);
- }
- // From a name, finds the associated virtual offset for the data.
- // To read the data, seek to _dataSectionOffset + dataPos, then
- // read the resource type & data.
- // This does a binary search through the names.
- internal int FindPosForResource(string name)
- {
- Debug.Assert(_store != null, "ResourceReader is closed!");
- int hash = FastResourceComparer.HashFunction(name);
- // 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 currentHash = GetNameHash(index);
- int c;
- if (currentHash == hash)
- c = 0;
- else if (currentHash < hash)
- c = -1;
- else
- c = 1;
- if (c == 0)
- {
- success = true;
- break;
- }
- if (c < 0)
- lo = index + 1;
- else
- hi = index - 1;
- }
- if (!success)
- {
- 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 - 1 && 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();
- if (dataPos < 0 || dataPos >= _store.BaseStream.Length - _dataSectionOffset)
- {
- throw new FormatException(SR.Format(SR.BadImageFormat_ResourcesDataInvalidOffset, dataPos));
- }
- return dataPos;
- }
- }
- }
- return -1;
- }
- // This compares the String in the .resources file at the current position
- // with the string you pass in.
- // Whoever calls this method should make sure that they take a lock
- // so no one else can cause us to seek in the stream.
- private unsafe bool CompareStringEqualsName(string name)
- {
- Debug.Assert(_store != null, "ResourceReader is closed!");
- int byteLen = _store.Read7BitEncodedInt();
- if (byteLen < 0)
- {
- throw new BadImageFormatException(SR.BadImageFormat_NegativeStringLength);
- }
- if (_ums != null)
- {
- byte* bytes = _ums.PositionPointer;
- // Skip over the data in the Stream, positioning ourselves right after it.
- _ums.Seek(byteLen, SeekOrigin.Current);
- if (_ums.Position > _ums.Length)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesNameTooLong);
- }
- // On 64-bit machines, these char*'s may be misaligned. Use a
- // byte-by-byte comparison instead.
- //return FastResourceComparer.CompareOrdinal((char*)bytes, byteLen/2, name) == 0;
- return FastResourceComparer.CompareOrdinal(bytes, byteLen, name) == 0;
- }
- else
- {
- // This code needs to be fast
- byte[] bytes = new byte[byteLen];
- int numBytesToRead = byteLen;
- while (numBytesToRead > 0)
- {
- int n = _store.Read(bytes, byteLen - numBytesToRead, numBytesToRead);
- if (n == 0)
- throw new BadImageFormatException(SR.BadImageFormat_ResourceNameCorrupted);
- numBytesToRead -= n;
- }
- return FastResourceComparer.CompareOrdinal(bytes, byteLen / 2, name) == 0;
- }
- }
- // This is used in the enumerator. The enumerator iterates from 0 to n
- // of our resources and this returns the resource name for a particular
- // index. The parameter is NOT a virtual offset.
- private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
- {
- Debug.Assert(_store != null, "ResourceReader is closed!");
- byte[] bytes;
- int byteLen;
- long nameVA = GetNamePosition(index);
- lock (this)
- {
- _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
- // Can't use _store.ReadString, since it's using UTF-8!
- byteLen = _store.Read7BitEncodedInt();
- if (byteLen < 0)
- {
- throw new BadImageFormatException(SR.BadImageFormat_NegativeStringLength);
- }
- if (_ums != null)
- {
- if (_ums.Position > _ums.Length - byteLen)
- throw new BadImageFormatException(SR.Format(SR.BadImageFormat_ResourcesIndexTooLong, index));
- string s = null;
- char* charPtr = (char*)_ums.PositionPointer;
- s = new string(charPtr, 0, byteLen / 2);
- _ums.Position += byteLen;
- dataOffset = _store.ReadInt32();
- if (dataOffset < 0 || dataOffset >= _store.BaseStream.Length - _dataSectionOffset)
- {
- throw new FormatException(SR.Format(SR.BadImageFormat_ResourcesDataInvalidOffset, dataOffset));
- }
- return s;
- }
- bytes = new byte[byteLen];
- // We must read byteLen bytes, or we have a corrupted file.
- // Use a blocking read in case the stream doesn't give us back
- // everything immediately.
- int count = byteLen;
- while (count > 0)
- {
- int n = _store.Read(bytes, byteLen - count, count);
- if (n == 0)
- throw new EndOfStreamException(SR.Format(SR.BadImageFormat_ResourceNameCorrupted_NameIndex, index));
- count -= n;
- }
- dataOffset = _store.ReadInt32();
- if (dataOffset < 0 || dataOffset >= _store.BaseStream.Length - _dataSectionOffset)
- {
- throw new FormatException(SR.Format(SR.BadImageFormat_ResourcesDataInvalidOffset, dataOffset));
- }
- }
- return Encoding.Unicode.GetString(bytes, 0, byteLen);
- }
- // This is used in the enumerator. The enumerator iterates from 0 to n
- // of our resources and this returns the resource value for a particular
- // index. The parameter is NOT a virtual offset.
- private object GetValueForNameIndex(int index)
- {
- Debug.Assert(_store != null, "ResourceReader is closed!");
- long nameVA = GetNamePosition(index);
- lock (this)
- {
- _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
- SkipString();
- int dataPos = _store.ReadInt32();
- if (dataPos < 0 || dataPos >= _store.BaseStream.Length - _dataSectionOffset)
- {
- throw new FormatException(SR.Format(SR.BadImageFormat_ResourcesDataInvalidOffset, dataPos));
- }
- ResourceTypeCode junk;
- if (_version == 1)
- return LoadObjectV1(dataPos);
- else
- return LoadObjectV2(dataPos, out junk);
- }
- }
- // This takes a virtual offset into the data section and reads a String
- // from that location.
- // Anyone who calls LoadObject should make sure they take a lock so
- // no one can cause us to do a seek in here.
- internal string LoadString(int pos)
- {
- Debug.Assert(_store != null, "ResourceReader is closed!");
- _store.BaseStream.Seek(_dataSectionOffset + pos, SeekOrigin.Begin);
- string s = null;
- int typeIndex = _store.Read7BitEncodedInt();
- if (_version == 1)
- {
- if (typeIndex == -1)
- return null;
- if (FindType(typeIndex) != typeof(string))
- throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ResourceNotString_Type, FindType(typeIndex).FullName));
- s = _store.ReadString();
- }
- else
- {
- ResourceTypeCode typeCode = (ResourceTypeCode)typeIndex;
- if (typeCode != ResourceTypeCode.String && typeCode != ResourceTypeCode.Null)
- {
- string typeString;
- if (typeCode < ResourceTypeCode.StartOfUserTypes)
- typeString = typeCode.ToString();
- else
- typeString = FindType(typeCode - ResourceTypeCode.StartOfUserTypes).FullName;
- throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ResourceNotString_Type, typeString));
- }
- if (typeCode == ResourceTypeCode.String) // ignore Null
- s = _store.ReadString();
- }
- return s;
- }
- // Called from RuntimeResourceSet
- internal object LoadObject(int pos)
- {
- if (_version == 1)
- return LoadObjectV1(pos);
- ResourceTypeCode typeCode;
- return LoadObjectV2(pos, out typeCode);
- }
- internal object LoadObject(int pos, out ResourceTypeCode typeCode)
- {
- if (_version == 1)
- {
- object o = LoadObjectV1(pos);
- typeCode = (o is string) ? ResourceTypeCode.String : ResourceTypeCode.StartOfUserTypes;
- return o;
- }
- return LoadObjectV2(pos, out typeCode);
- }
- // This takes a virtual offset into the data section and reads an Object
- // from that location.
- // Anyone who calls LoadObject should make sure they take a lock so
- // no one can cause us to do a seek in here.
- internal object LoadObjectV1(int pos)
- {
- Debug.Assert(_store != null, "ResourceReader is closed!");
- Debug.Assert(_version == 1, ".resources file was not a V1 .resources file!");
- try
- {
- // mega try-catch performs exceptionally bad on x64; factored out body into
- // _LoadObjectV1 and wrap here.
- return _LoadObjectV1(pos);
- }
- catch (EndOfStreamException eof)
- {
- throw new BadImageFormatException(SR.BadImageFormat_TypeMismatch, eof);
- }
- catch (ArgumentOutOfRangeException e)
- {
- throw new BadImageFormatException(SR.BadImageFormat_TypeMismatch, e);
- }
- }
- private object _LoadObjectV1(int pos)
- {
- _store.BaseStream.Seek(_dataSectionOffset + pos, SeekOrigin.Begin);
- int typeIndex = _store.Read7BitEncodedInt();
- if (typeIndex == -1)
- return null;
- Type type = FindType(typeIndex);
- // Consider putting in logic to see if this type is a
- // primitive or a value type first, so we can reach the
- // deserialization code faster for arbitrary objects.
- if (type == typeof(string))
- return _store.ReadString();
- else if (type == typeof(int))
- return _store.ReadInt32();
- else if (type == typeof(byte))
- return _store.ReadByte();
- else if (type == typeof(sbyte))
- return _store.ReadSByte();
- else if (type == typeof(short))
- return _store.ReadInt16();
- else if (type == typeof(long))
- return _store.ReadInt64();
- else if (type == typeof(ushort))
- return _store.ReadUInt16();
- else if (type == typeof(uint))
- return _store.ReadUInt32();
- else if (type == typeof(ulong))
- return _store.ReadUInt64();
- else if (type == typeof(float))
- return _store.ReadSingle();
- else if (type == typeof(double))
- return _store.ReadDouble();
- else if (type == typeof(DateTime))
- {
- // Ideally we should use DateTime's ToBinary & FromBinary,
- // but we can't for compatibility reasons.
- return new DateTime(_store.ReadInt64());
- }
- else if (type == typeof(TimeSpan))
- return new TimeSpan(_store.ReadInt64());
- else if (type == typeof(decimal))
- {
- int[] bits = new int[4];
- for (int i = 0; i < bits.Length; i++)
- bits[i] = _store.ReadInt32();
- return new decimal(bits);
- }
- else
- {
- return DeserializeObject(typeIndex);
- }
- }
- internal object LoadObjectV2(int pos, out ResourceTypeCode typeCode)
- {
- Debug.Assert(_store != null, "ResourceReader is closed!");
- Debug.Assert(_version >= 2, ".resources file was not a V2 (or higher) .resources file!");
- try
- {
- // mega try-catch performs exceptionally bad on x64; factored out body into
- // _LoadObjectV2 and wrap here.
- return _LoadObjectV2(pos, out typeCode);
- }
- catch (EndOfStreamException eof)
- {
- throw new BadImageFormatException(SR.BadImageFormat_TypeMismatch, eof);
- }
- catch (ArgumentOutOfRangeException e)
- {
- throw new BadImageFormatException(SR.BadImageFormat_TypeMismatch, e);
- }
- }
- private object _LoadObjectV2(int pos, out ResourceTypeCode typeCode)
- {
- _store.BaseStream.Seek(_dataSectionOffset + pos, SeekOrigin.Begin);
- typeCode = (ResourceTypeCode)_store.Read7BitEncodedInt();
- switch (typeCode)
- {
- case ResourceTypeCode.Null:
- return null;
- case ResourceTypeCode.String:
- return _store.ReadString();
- case ResourceTypeCode.Boolean:
- return _store.ReadBoolean();
- case ResourceTypeCode.Char:
- return (char)_store.ReadUInt16();
- case ResourceTypeCode.Byte:
- return _store.ReadByte();
- case ResourceTypeCode.SByte:
- return _store.ReadSByte();
- case ResourceTypeCode.Int16:
- return _store.ReadInt16();
- case ResourceTypeCode.UInt16:
- return _store.ReadUInt16();
- case ResourceTypeCode.Int32:
- return _store.ReadInt32();
- case ResourceTypeCode.UInt32:
- return _store.ReadUInt32();
- case ResourceTypeCode.Int64:
- return _store.ReadInt64();
- case ResourceTypeCode.UInt64:
- return _store.ReadUInt64();
- case ResourceTypeCode.Single:
- return _store.ReadSingle();
- case ResourceTypeCode.Double:
- return _store.ReadDouble();
- case ResourceTypeCode.Decimal:
- return _store.ReadDecimal();
- case ResourceTypeCode.DateTime:
- // Use DateTime's ToBinary & FromBinary.
- long data = _store.ReadInt64();
- return DateTime.FromBinary(data);
- case ResourceTypeCode.TimeSpan:
- long ticks = _store.ReadInt64();
- return new TimeSpan(ticks);
- // Special types
- case ResourceTypeCode.ByteArray:
- {
- int len = _store.ReadInt32();
- if (len < 0)
- {
- throw new BadImageFormatException(SR.Format(SR.BadImageFormat_ResourceDataLengthInvalid, len));
- }
- if (_ums == null)
- {
- if (len > _store.BaseStream.Length)
- {
- throw new BadImageFormatException(SR.Format(SR.BadImageFormat_ResourceDataLengthInvalid, len));
- }
- return _store.ReadBytes(len);
- }
- if (len > _ums.Length - _ums.Position)
- {
- throw new BadImageFormatException(SR.Format(SR.BadImageFormat_ResourceDataLengthInvalid, len));
- }
- byte[] bytes = new byte[len];
- int r = _ums.Read(bytes, 0, len);
- Debug.Assert(r == len, "ResourceReader needs to use a blocking read here. (Call _store.ReadBytes(len)?)");
- return bytes;
- }
- case ResourceTypeCode.Stream:
- {
- int len = _store.ReadInt32();
- if (len < 0)
- {
- throw new BadImageFormatException(SR.Format(SR.BadImageFormat_ResourceDataLengthInvalid, len));
- }
- if (_ums == null)
- {
- byte[] bytes = _store.ReadBytes(len);
- // Lifetime of memory == lifetime of this stream.
- return new PinnedBufferMemoryStream(bytes);
- }
- // make sure we don't create an UnmanagedMemoryStream that is longer than the resource stream.
- if (len > _ums.Length - _ums.Position)
- {
- throw new BadImageFormatException(SR.Format(SR.BadImageFormat_ResourceDataLengthInvalid, len));
- }
- // For the case that we've memory mapped in the .resources
- // file, just return a Stream pointing to that block of memory.
- unsafe
- {
- return new UnmanagedMemoryStream(_ums.PositionPointer, len, len, FileAccess.Read);
- }
- }
- default:
- if (typeCode < ResourceTypeCode.StartOfUserTypes)
- {
- throw new BadImageFormatException(SR.BadImageFormat_TypeMismatch);
- }
- break;
- }
- // Normal serialized objects
- int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
- return DeserializeObject(typeIndex);
- }
- private object DeserializeObject(int typeIndex)
- {
- if (!_permitDeserialization)
- {
- throw new NotSupportedException(SR.NotSupported_ResourceObjectSerialization);
- }
- if (_binaryFormatter == null)
- {
- InitializeBinaryFormatter();
- }
- Type type = FindType(typeIndex);
-
- object graph = s_deserializeMethod(_binaryFormatter, _store.BaseStream);
-
- // guard against corrupted resources
- if (graph.GetType() != type)
- throw new BadImageFormatException(SR.Format(SR.BadImageFormat_ResType_SerBlobMismatch, type.FullName, graph.GetType().FullName));
-
- return graph;
- }
- private void InitializeBinaryFormatter()
- {
- LazyInitializer.EnsureInitialized(ref s_binaryFormatterType, () =>
- Type.GetType("System.Runtime.Serialization.Formatters.Binary.BinaryFormatter, System.Runtime.Serialization.Formatters, Version=0.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
- throwOnError: true));
- LazyInitializer.EnsureInitialized(ref s_deserializeMethod, () =>
- {
- MethodInfo binaryFormatterDeserialize = s_binaryFormatterType.GetMethod("Deserialize", new Type[] { typeof(Stream) });
- // create an unbound delegate that can accept a BinaryFormatter instance as object
- return (Func<object, Stream, object>)typeof(ResourceReader)
- .GetMethod(nameof(CreateUntypedDelegate), BindingFlags.NonPublic | BindingFlags.Static)
- .MakeGenericMethod(s_binaryFormatterType)
- .Invoke(null, new object[] { binaryFormatterDeserialize });
- });
- _binaryFormatter = Activator.CreateInstance(s_binaryFormatterType);
- }
- // generic method that we specialize at runtime once we've loaded the BinaryFormatter type
- // permits creating an unbound delegate so that we can avoid reflection after the initial
- // lightup code completes.
- private static Func<object, Stream, object> CreateUntypedDelegate<TInstance>(MethodInfo method)
- {
- Func<TInstance, Stream, object> typedDelegate = (Func<TInstance, Stream, object>)Delegate.CreateDelegate(typeof(Func<TInstance, Stream, object>), null, method);
- return (obj, stream) => typedDelegate((TInstance)obj, stream);
- }
- // Reads in the header information for a .resources file. Verifies some
- // of the assumptions about this resource set, and builds the class table
- // for the default resource file format.
- private void ReadResources()
- {
- Debug.Assert(_store != null, "ResourceReader is closed!");
- try
- {
- // mega try-catch performs exceptionally bad on x64; factored out body into
- // _ReadResources and wrap here.
- _ReadResources();
- }
- catch (EndOfStreamException eof)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesHeaderCorrupted, eof);
- }
- catch (IndexOutOfRangeException e)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesHeaderCorrupted, e);
- }
- }
- private void _ReadResources()
- {
- // Read ResourceManager header
- // Check for magic number
- int magicNum = _store.ReadInt32();
- if (magicNum != ResourceManager.MagicNumber)
- throw new ArgumentException(SR.Resources_StreamNotValid);
- // Assuming this is ResourceManager header V1 or greater, hopefully
- // after the version number there is a number of bytes to skip
- // to bypass the rest of the ResMgr header. For V2 or greater, we
- // use this to skip to the end of the header
- int resMgrHeaderVersion = _store.ReadInt32();
- int numBytesToSkip = _store.ReadInt32();
- if (numBytesToSkip < 0 || resMgrHeaderVersion < 0)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesHeaderCorrupted);
- }
- if (resMgrHeaderVersion > 1)
- {
- _store.BaseStream.Seek(numBytesToSkip, SeekOrigin.Current);
- }
- else
- {
- // We don't care about numBytesToSkip; read the rest of the header
- // Read in type name for a suitable ResourceReader
- // Note ResourceWriter & InternalResGen use different Strings.
- string readerType = _store.ReadString();
- if (!ResourceManager.IsDefaultType(readerType, ResourceManager.ResReaderTypeName))
- throw new NotSupportedException(SR.Format(SR.NotSupported_WrongResourceReader_Type, readerType));
- // Skip over type name for a suitable ResourceSet
- SkipString();
- }
- // Read RuntimeResourceSet header
- // Do file version check
- int version = _store.ReadInt32();
- if (version != RuntimeResourceSet.Version && version != 1)
- throw new ArgumentException(SR.Format(SR.Arg_ResourceFileUnsupportedVersion, RuntimeResourceSet.Version, version));
- _version = version;
- _numResources = _store.ReadInt32();
- if (_numResources < 0)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesHeaderCorrupted);
- }
- // Read type positions into type positions array.
- // But delay initialize the type table.
- int numTypes = _store.ReadInt32();
- if (numTypes < 0)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesHeaderCorrupted);
- }
- _typeTable = new Type[numTypes];
- _typeNamePositions = new int[numTypes];
- for (int i = 0; i < numTypes; i++)
- {
- _typeNamePositions[i] = (int)_store.BaseStream.Position;
- // Skip over the Strings in the file. Don't create types.
- SkipString();
- }
- // Prepare to read in the array of name hashes
- // Note that the name hashes array is aligned to 8 bytes so
- // we can use pointers into it on 64 bit machines. (4 bytes
- // may be sufficient, but let's plan for the future)
- // Skip over alignment stuff. All public .resources files
- // should be aligned No need to verify the byte values.
- long pos = _store.BaseStream.Position;
- int alignBytes = ((int)pos) & 7;
- if (alignBytes != 0)
- {
- for (int i = 0; i < 8 - alignBytes; i++)
- {
- _store.ReadByte();
- }
- }
- // Read in the array of name hashes
- if (_ums == null)
- {
- _nameHashes = new int[_numResources];
- for (int i = 0; i < _numResources; i++)
- {
- _nameHashes[i] = _store.ReadInt32();
- }
- }
- else
- {
- int seekPos = unchecked(4 * _numResources);
- if (seekPos < 0)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesHeaderCorrupted);
- }
- unsafe
- {
- _nameHashesPtr = (int*)_ums.PositionPointer;
- // Skip over the array of nameHashes.
- _ums.Seek(seekPos, SeekOrigin.Current);
- // get the position pointer once more to check that the whole table is within the stream
- byte* junk = _ums.PositionPointer;
- }
- }
- // Read in the array of relative positions for all the names.
- if (_ums == null)
- {
- _namePositions = new int[_numResources];
- for (int i = 0; i < _numResources; i++)
- {
- int namePosition = _store.ReadInt32();
- if (namePosition < 0)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesHeaderCorrupted);
- }
- _namePositions[i] = namePosition;
- }
- }
- else
- {
- int seekPos = unchecked(4 * _numResources);
- if (seekPos < 0)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesHeaderCorrupted);
- }
- unsafe
- {
- _namePositionsPtr = (int*)_ums.PositionPointer;
- // Skip over the array of namePositions.
- _ums.Seek(seekPos, SeekOrigin.Current);
- // get the position pointer once more to check that the whole table is within the stream
- byte* junk = _ums.PositionPointer;
- }
- }
- // Read location of data section.
- _dataSectionOffset = _store.ReadInt32();
- if (_dataSectionOffset < 0)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesHeaderCorrupted);
- }
- // Store current location as start of name section
- _nameSectionOffset = _store.BaseStream.Position;
- // _nameSectionOffset should be <= _dataSectionOffset; if not, it's corrupt
- if (_dataSectionOffset < _nameSectionOffset)
- {
- throw new BadImageFormatException(SR.BadImageFormat_ResourcesHeaderCorrupted);
- }
- }
- // This allows us to delay-initialize the Type[]. This might be a
- // good startup time savings, since we might have to load assemblies
- // and initialize Reflection.
- private Type FindType(int typeIndex)
- {
- if (typeIndex < 0 || typeIndex >= _typeTable.Length)
- {
- throw new BadImageFormatException(SR.BadImageFormat_InvalidType);
- }
- if (_typeTable[typeIndex] == null)
- {
- long oldPos = _store.BaseStream.Position;
- try
- {
- _store.BaseStream.Position = _typeNamePositions[typeIndex];
- string typeName = _store.ReadString();
- _typeTable[typeIndex] = Type.GetType(typeName, true);
- }
- // If serialization isn't supported, we convert FileNotFoundException to
- // NotSupportedException for consistency with v2. This is a corner-case, but the
- // idea is that we want to give the user a more accurate error message. Even if
- // the dependency were found, we know it will require serialization since it
- // can't be one of the types we special case. So if the dependency were found,
- // it would go down the serialization code path, resulting in NotSupported for
- // SKUs without serialization.
- //
- // We don't want to regress the expected case by checking the type info before
- // getting to Type.GetType -- this is costly with v1 resource formats.
- catch (FileNotFoundException)
- {
- throw new NotSupportedException(SR.NotSupported_ResourceObjectSerialization);
- }
- finally
- {
- _store.BaseStream.Position = oldPos;
- }
- }
- Debug.Assert(_typeTable[typeIndex] != null, "Should have found a type!");
- return _typeTable[typeIndex];
- }
- public void GetResourceData(string resourceName, out string resourceType, out byte[] resourceData)
- {
- if (resourceName == null)
- throw new ArgumentNullException(nameof(resourceName));
- if (_resCache == null)
- throw new InvalidOperationException(SR.ResourceReaderIsClosed);
- // Get the type information from the data section. Also,
- // sort all of the data section's indexes to compute length of
- // the serialized data for this type (making sure to subtract
- // off the length of the type code).
- int[] sortedDataPositions = new int[_numResources];
- int dataPos = FindPosForResource(resourceName);
- if (dataPos == -1)
- {
- throw new ArgumentException(SR.Format(SR.Arg_ResourceNameNotExist, resourceName));
- }
- lock (this)
- {
- // Read all the positions of data within the data section.
- for (int i = 0; i < _numResources; i++)
- {
- _store.BaseStream.Position = _nameSectionOffset + GetNamePosition(i);
- // Skip over name of resource
- int numBytesToSkip = _store.Read7BitEncodedInt();
- if (numBytesToSkip < 0)
- {
- throw new FormatException(SR.Format(SR.BadImageFormat_ResourcesNameInvalidOffset, numBytesToSkip));
- }
- _store.BaseStream.Position += numBytesToSkip;
- int dPos = _store.ReadInt32();
- if (dPos < 0 || dPos >= _store.BaseStream.Length - _dataSectionOffset)
- {
- throw new FormatException(SR.Format(SR.BadImageFormat_ResourcesDataInvalidOffset, dPos));
- }
- sortedDataPositions[i] = dPos;
- }
- Array.Sort(sortedDataPositions);
- int index = Array.BinarySearch(sortedDataPositions, dataPos);
- Debug.Assert(index >= 0 && index < _numResources, "Couldn't find data position within sorted data positions array!");
- long nextData = (index < _numResources - 1) ? sortedDataPositions[index + 1] + _dataSectionOffset : _store.BaseStream.Length;
- int len = (int)(nextData - (dataPos + _dataSectionOffset));
- Debug.Assert(len >= 0 && len <= (int)_store.BaseStream.Length - dataPos + _dataSectionOffset, "Length was negative or outside the bounds of the file!");
- // Read type code then byte[]
- _store.BaseStream.Position = _dataSectionOffset + dataPos;
- ResourceTypeCode typeCode = (ResourceTypeCode)_store.Read7BitEncodedInt();
- if (typeCode < 0 || typeCode >= ResourceTypeCode.StartOfUserTypes + _typeTable.Length)
- {
- throw new BadImageFormatException(SR.BadImageFormat_InvalidType);
- }
- resourceType = TypeNameFromTypeCode(typeCode);
- // The length must be adjusted to subtract off the number
- // of bytes in the 7 bit encoded type code.
- len -= (int)(_store.BaseStream.Position - (_dataSectionOffset + dataPos));
- byte[] bytes = _store.ReadBytes(len);
- if (bytes.Length != len)
- throw new FormatException(SR.BadImageFormat_ResourceNameCorrupted);
- resourceData = bytes;
- }
- }
- private string TypeNameFromTypeCode(ResourceTypeCode typeCode)
- {
- Debug.Assert(typeCode >= 0, "can't be negative");
- if (typeCode < ResourceTypeCode.StartOfUserTypes)
- {
- Debug.Assert(!string.Equals(typeCode.ToString(), "LastPrimitive"), "Change ResourceTypeCode metadata order so LastPrimitive isn't what Enum.ToString prefers.");
- return "ResourceTypeCode." + typeCode.ToString();
- }
- else
- {
- int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
- Debug.Assert(typeIndex >= 0 && typeIndex < _typeTable.Length, "TypeCode is broken or corrupted!");
- long oldPos = _store.BaseStream.Position;
- try
- {
- _store.BaseStream.Position = _typeNamePositions[typeIndex];
- return _store.ReadString();
- }
- finally
- {
- _store.BaseStream.Position = oldPos;
- }
- }
- }
- internal sealed class ResourceEnumerator : IDictionaryEnumerator
- {
- private const int ENUM_DONE = int.MinValue;
- private const int ENUM_NOT_STARTED = -1;
- private ResourceReader _reader;
- private bool _currentIsValid;
- private int _currentName;
- private int _dataPosition; // cached for case-insensitive table
- internal ResourceEnumerator(ResourceReader reader)
- {
- _currentName = ENUM_NOT_STARTED;
- _reader = reader;
- _dataPosition = -2;
- }
- public bool MoveNext()
- {
- if (_currentName == _reader._numResources - 1 || _currentName == ENUM_DONE)
- {
- _currentIsValid = false;
- _currentName = ENUM_DONE;
- return false;
- }
- _currentIsValid = true;
- _currentName++;
- return true;
- }
- public object Key
- {
- get
- {
- if (_currentName == ENUM_DONE) throw new InvalidOperationException(SR.InvalidOperation_EnumEnded);
- if (!_currentIsValid) throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted);
- if (_reader._resCache == null) throw new InvalidOperationException(SR.ResourceReaderIsClosed);
- return _reader.AllocateStringForNameIndex(_currentName, out _dataPosition);
- }
- }
- public object Current
- {
- get
- {
- return Entry;
- }
- }
- // Warning: This requires that you call the Key or Entry property FIRST before calling it!
- internal int DataPosition
- {
- get
- {
- return _dataPosition;
- }
- }
- public DictionaryEntry Entry
- {
- get
- {
- if (_currentName == ENUM_DONE) throw new InvalidOperationException(SR.InvalidOperation_EnumEnded);
- if (!_currentIsValid) throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted);
- if (_reader._resCache == null) throw new InvalidOperationException(SR.ResourceReaderIsClosed);
- string key;
- object value = null;
- lock (_reader)
- { // locks should be taken in the same order as in RuntimeResourceSet.GetObject to avoid deadlock
- lock (_reader._resCache)
- {
- key = _reader.AllocateStringForNameIndex(_currentName, out _dataPosition); // AllocateStringForNameIndex could lock on _reader
- ResourceLocator locator;
- if (_reader._resCache.TryGetValue(key, out locator))
- {
- value = locator.Value;
- }
- if (value == null)
- {
- if (_dataPosition == -1)
- value = _reader.GetValueForNameIndex(_currentName);
- else
- value = _reader.LoadObject(_dataPosition);
- // If enumeration and subsequent lookups happen very
- // frequently in the same process, add a ResourceLocator
- // to _resCache here. But WinForms enumerates and
- // just about everyone else does lookups. So caching
- // here may bloat working set.
- }
- }
- }
- return new DictionaryEntry(key, value);
- }
- }
- public object Value
- {
- get
- {
- if (_currentName == ENUM_DONE) throw new InvalidOperationException(SR.InvalidOperation_EnumEnded);
- if (!_currentIsValid) throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted);
- if (_reader._resCache == null) throw new InvalidOperationException(SR.ResourceReaderIsClosed);
- // Consider using _resCache here, eventually, if
- // this proves to be an interesting perf scenario.
- // But mixing lookups and enumerators shouldn't be
- // particularly compelling.
- return _reader.GetValueForNameIndex(_currentName);
- }
- }
- public void Reset()
- {
- if (_reader._resCache == null) throw new InvalidOperationException(SR.ResourceReaderIsClosed);
- _currentIsValid = false;
- _currentName = ENUM_NOT_STARTED;
- }
- }
- }
- }
|