ResourceWriter.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. //
  2. // System.Resources.ResourceWriter.cs
  3. //
  4. // Authors:
  5. // Duncan Mak <[email protected]>
  6. // Dick Porter <[email protected]>
  7. //
  8. // (C) 2001, 2002 Ximian, Inc. http://www.ximian.com
  9. //
  10. using System.IO;
  11. using System.Collections;
  12. using System.Text;
  13. using System.Runtime.Serialization;
  14. using System.Runtime.Serialization.Formatters.Binary;
  15. namespace System.Resources
  16. {
  17. public sealed class ResourceWriter : IResourceWriter, IDisposable
  18. {
  19. Hashtable resources;
  20. Stream stream;
  21. public ResourceWriter (Stream stream)
  22. {
  23. if (stream == null)
  24. throw new ArgumentNullException ("stream is null");
  25. if (stream.CanWrite == false)
  26. throw new ArgumentException ("stream is not writable.");
  27. this.stream=stream;
  28. resources=new Hashtable(CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
  29. }
  30. public ResourceWriter (String fileName)
  31. {
  32. if (fileName == null)
  33. throw new ArgumentNullException ("fileName is null.");
  34. stream=new FileStream(fileName, FileMode.Create, FileAccess.Write);
  35. resources=new Hashtable(CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
  36. }
  37. public void AddResource (string name, byte[] value)
  38. {
  39. if (name == null) {
  40. throw new ArgumentNullException ("name is null");
  41. }
  42. if (value == null) {
  43. throw new ArgumentNullException ("value is null");
  44. }
  45. if(resources==null) {
  46. throw new InvalidOperationException ("ResourceWriter has been closed");
  47. }
  48. if(resources[name]!=null) {
  49. throw new ArgumentException ("Resource already present: " + name);
  50. }
  51. resources.Add(name, value);
  52. }
  53. public void AddResource (string name, object value)
  54. {
  55. if (name == null) {
  56. throw new ArgumentNullException ("name is null");
  57. }
  58. if (value == null) {
  59. throw new ArgumentNullException ("value is null");
  60. }
  61. if(resources==null) {
  62. throw new InvalidOperationException ("ResourceWriter has been closed");
  63. }
  64. if(resources[name]!=null) {
  65. throw new ArgumentException ("Resource already present: " + name);
  66. }
  67. resources.Add(name, value);
  68. }
  69. public void AddResource (string name, string value)
  70. {
  71. if (name == null) {
  72. throw new ArgumentNullException ("name is null");
  73. }
  74. if (value == null) {
  75. throw new ArgumentNullException ("value is null");
  76. }
  77. if(resources==null) {
  78. throw new InvalidOperationException ("ResourceWriter has been closed");
  79. }
  80. if(resources[name]!=null) {
  81. throw new ArgumentException ("Resource already present: " + name);
  82. }
  83. resources.Add(name, value);
  84. }
  85. public void Close () {
  86. Dispose(true);
  87. }
  88. public void Dispose ()
  89. {
  90. Dispose(true);
  91. }
  92. private void Dispose (bool disposing)
  93. {
  94. if(disposing) {
  95. if(resources.Count>0 && generated==false) {
  96. Generate();
  97. }
  98. if(stream!=null) {
  99. stream.Close();
  100. }
  101. }
  102. resources=null;
  103. stream=null;
  104. }
  105. private bool generated=false;
  106. public void Generate () {
  107. BinaryWriter writer;
  108. IFormatter formatter;
  109. if(resources==null) {
  110. throw new InvalidOperationException ("ResourceWriter has been closed");
  111. }
  112. if(generated) {
  113. throw new InvalidOperationException ("ResourceWriter can only Generate() once");
  114. }
  115. generated=true;
  116. writer=new BinaryWriter(stream, Encoding.UTF8);
  117. formatter=new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
  118. /* The ResourceManager header */
  119. writer.Write(ResourceManager.MagicNumber);
  120. writer.Write(ResourceManager.HeaderVersionNumber);
  121. /* Build the rest of the ResourceManager
  122. * header in memory, because we need to know
  123. * how long it is in advance
  124. */
  125. MemoryStream resman_stream=new MemoryStream();
  126. BinaryWriter resman=new BinaryWriter(resman_stream,
  127. Encoding.UTF8);
  128. resman.Write(typeof(ResourceReader).AssemblyQualifiedName);
  129. resman.Write(typeof(ResourceSet).AssemblyQualifiedName);
  130. /* Only space for 32 bits of header len in the
  131. * resource file format
  132. */
  133. int resman_len=(int)resman_stream.Length;
  134. writer.Write(resman_len);
  135. writer.Write(resman_stream.GetBuffer(), 0, resman_len);
  136. /* We need to build the ResourceReader name
  137. * and data sections simultaneously
  138. */
  139. MemoryStream res_name_stream=new MemoryStream();
  140. BinaryWriter res_name=new BinaryWriter(res_name_stream, Encoding.Unicode);
  141. MemoryStream res_data_stream=new MemoryStream();
  142. BinaryWriter res_data=new BinaryWriter(res_data_stream,
  143. Encoding.UTF8);
  144. /* Not sure if this is the best collection to
  145. * use, I just want an unordered list of
  146. * objects with fast lookup, but without
  147. * needing a key. (I suppose a hashtable with
  148. * key==value would work, but I need to find
  149. * the index of each item later)
  150. */
  151. ArrayList types=new ArrayList();
  152. int[] hashes=new int[resources.Count];
  153. int[] name_offsets=new int[resources.Count];
  154. int count=0;
  155. IDictionaryEnumerator res_enum=resources.GetEnumerator();
  156. while(res_enum.MoveNext()) {
  157. Type type=res_enum.Value.GetType();
  158. /* Keep a list of unique types */
  159. if(!types.Contains(type)) {
  160. types.Add(type);
  161. }
  162. /* Hash the name */
  163. hashes[count]=GetHash((string)res_enum.Key);
  164. /* Record the offsets */
  165. name_offsets[count]=(int)res_name.BaseStream.Position;
  166. /* Write the name section */
  167. res_name.Write((string)res_enum.Key);
  168. res_name.Write((int)res_data.BaseStream.Position);
  169. /* Write the data section */
  170. Write7BitEncodedInt(res_data, types.IndexOf(type));
  171. /* Strangely, Char is serialized
  172. * rather than just written out
  173. */
  174. if(type==typeof(Byte)) {
  175. res_data.Write((Byte)res_enum.Value);
  176. } else if (type==typeof(Decimal)) {
  177. res_data.Write((Decimal)res_enum.Value);
  178. } else if (type==typeof(DateTime)) {
  179. res_data.Write(((DateTime)res_enum.Value).Ticks);
  180. } else if (type==typeof(Double)) {
  181. res_data.Write((Double)res_enum.Value);
  182. } else if (type==typeof(Int16)) {
  183. res_data.Write((Int16)res_enum.Value);
  184. } else if (type==typeof(Int32)) {
  185. res_data.Write((Int32)res_enum.Value);
  186. } else if (type==typeof(Int64)) {
  187. res_data.Write((Int64)res_enum.Value);
  188. } else if (type==typeof(SByte)) {
  189. res_data.Write((SByte)res_enum.Value);
  190. } else if (type==typeof(Single)) {
  191. res_data.Write((Single)res_enum.Value);
  192. } else if (type==typeof(String)) {
  193. res_data.Write((String)res_enum.Value);
  194. } else if (type==typeof(TimeSpan)) {
  195. res_data.Write(((TimeSpan)res_enum.Value).Ticks);
  196. } else if (type==typeof(UInt16)) {
  197. res_data.Write((UInt16)res_enum.Value);
  198. } else if (type==typeof(UInt32)) {
  199. res_data.Write((UInt32)res_enum.Value);
  200. } else if (type==typeof(UInt64)) {
  201. res_data.Write((UInt64)res_enum.Value);
  202. } else {
  203. /* non-intrinsic types are
  204. * serialized
  205. */
  206. formatter.Serialize(res_data.BaseStream, res_enum.Value);
  207. }
  208. count++;
  209. }
  210. /* Sort the hashes, keep the name offsets
  211. * matching up
  212. */
  213. Array.Sort(hashes, name_offsets);
  214. /* now do the ResourceReader header */
  215. writer.Write(1);
  216. writer.Write(resources.Count);
  217. writer.Write(types.Count);
  218. /* Write all of the unique types */
  219. foreach(Type type in types) {
  220. writer.Write(type.AssemblyQualifiedName);
  221. }
  222. /* Pad the next fields (hash values) on an 8
  223. * byte boundary, using the letters "PAD"
  224. */
  225. int pad_align=(int)(writer.BaseStream.Position & 7);
  226. int pad_chars=0;
  227. if(pad_align!=0) {
  228. pad_chars=8-pad_align;
  229. }
  230. for(int i=0; i<pad_chars; i++) {
  231. writer.Write((byte)"PAD"[i%3]);
  232. }
  233. /* Write the hashes */
  234. for(int i=0; i<resources.Count; i++) {
  235. writer.Write(hashes[i]);
  236. }
  237. /* and the name offsets */
  238. for(int i=0; i<resources.Count; i++) {
  239. writer.Write(name_offsets[i]);
  240. }
  241. /* Write the data section offset */
  242. int data_offset=(int)writer.BaseStream.Position +
  243. (int)res_name_stream.Length + 4;
  244. writer.Write(data_offset);
  245. /* The name section goes next */
  246. writer.Write(res_name_stream.GetBuffer(), 0,
  247. (int)res_name_stream.Length);
  248. /* The data section is last */
  249. writer.Write(res_data_stream.GetBuffer(), 0,
  250. (int)res_data_stream.Length);
  251. res_name.Close();
  252. res_data.Close();
  253. /* Don't close writer, according to the spec */
  254. writer.Flush();
  255. }
  256. private int GetHash(string name)
  257. {
  258. uint hash=5381;
  259. for(int i=0; i<name.Length; i++) {
  260. hash=((hash<<5)+hash)^name[i];
  261. }
  262. return((int)hash);
  263. }
  264. /* Cut and pasted from BinaryWriter, because it's
  265. * 'protected' there.
  266. */
  267. private void Write7BitEncodedInt(BinaryWriter writer,
  268. int value)
  269. {
  270. do {
  271. int high = (value >> 7) & 0x01ffffff;
  272. byte b = (byte)(value & 0x7f);
  273. if (high != 0) {
  274. b = (byte)(b | 0x80);
  275. }
  276. writer.Write(b);
  277. value = high;
  278. } while(value != 0);
  279. }
  280. }
  281. }