Merge pull request #309 from i59/patch-1
[mono.git] / mcs / class / IKVM.Reflection / Writer / ByteBuffer.cs
1 /*
2   Copyright (C) 2008 Jeroen Frijters
3
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely, subject to the following restrictions:
11
12   1. The origin of this software must not be misrepresented; you must not
13      claim that you wrote the original software. If you use this software
14      in a product, an acknowledgment in the product documentation would be
15      appreciated but is not required.
16   2. Altered source versions must be plainly marked as such, and must not be
17      misrepresented as being the original software.
18   3. This notice may not be removed or altered from any source distribution.
19
20   Jeroen Frijters
21   jeroen@frijters.net
22   
23 */
24 using System;
25 using System.Collections.Generic;
26 using System.Text;
27 using IKVM.Reflection.Metadata;
28
29 namespace IKVM.Reflection.Writer
30 {
31         sealed class ByteBuffer
32         {
33                 private byte[] buffer;
34                 private int pos;
35                 private int __length;   // __length is only valid if > pos, otherwise pos is the current length
36
37                 internal ByteBuffer(int initialCapacity)
38                 {
39                         buffer = new byte[initialCapacity];
40                 }
41
42                 private ByteBuffer(byte[] wrap, int length)
43                 {
44                         this.buffer = wrap;
45                         this.pos = length;
46                 }
47
48                 internal int Position
49                 {
50                         get { return pos; }
51                         set
52                         {
53                                 if (value > this.Length || value > buffer.Length)
54                                         throw new ArgumentOutOfRangeException();
55                                 __length = Math.Max(__length, pos);
56                                 pos = value;
57                         }
58                 }
59
60                 internal int Length
61                 {
62                         get { return Math.Max(pos, __length); }
63                 }
64
65                 // insert count bytes at the current position (without advancing the current position)
66                 internal void Insert(int count)
67                 {
68                         if (count > 0)
69                         {
70                                 int len = this.Length;
71                                 int free = buffer.Length - len;
72                                 if (free < count)
73                                 {
74                                         Grow(count - free);
75                                 }
76                                 Buffer.BlockCopy(buffer, pos, buffer, pos + count, len - pos);
77                                 __length = Math.Max(__length, pos) + count;
78                         }
79                         else if (count < 0)
80                         {
81                                 throw new ArgumentOutOfRangeException("count");
82                         }
83                 }
84
85                 private void Grow(int minGrow)
86                 {
87                         byte[] newbuf = new byte[Math.Max(buffer.Length + minGrow, buffer.Length * 2)];
88                         Buffer.BlockCopy(buffer, 0, newbuf, 0, buffer.Length);
89                         buffer = newbuf;
90                 }
91
92                 // NOTE this does not advance the position
93                 internal int GetInt32AtCurrentPosition()
94                 {
95                         return buffer[pos]
96                                 + (buffer[pos + 1] << 8)
97                                 + (buffer[pos + 2] << 16)
98                                 + (buffer[pos + 3] << 24);
99                 }
100
101                 // NOTE this does not advance the position
102                 internal byte GetByteAtCurrentPosition()
103                 {
104                         return buffer[pos];
105                 }
106
107                 // return the number of bytes that the compressed int at the current position takes
108                 internal int GetCompressedIntLength()
109                 {
110                         switch (buffer[pos] & 0xC0)
111                         {
112                                 default:
113                                         return 1;
114                                 case 0x80:
115                                         return 2;
116                                 case 0xC0:
117                                         return 4;
118                         }
119                 }
120
121                 internal void Write(byte[] value)
122                 {
123                         if (pos + value.Length > buffer.Length)
124                                 Grow(value.Length);
125                         Buffer.BlockCopy(value, 0, buffer, pos, value.Length);
126                         pos += value.Length;
127                 }
128
129                 internal void Write(byte value)
130                 {
131                         if (pos == buffer.Length)
132                                 Grow(1);
133                         buffer[pos++] = value;
134                 }
135
136                 internal void Write(sbyte value)
137                 {
138                         Write((byte)value);
139                 }
140
141                 internal void Write(ushort value)
142                 {
143                         Write((short)value);
144                 }
145
146                 internal void Write(short value)
147                 {
148                         if (pos + 2 > buffer.Length)
149                                 Grow(2);
150                         buffer[pos++] = (byte)value;
151                         buffer[pos++] = (byte)(value >> 8);
152                 }
153
154                 internal void Write(uint value)
155                 {
156                         Write((int)value);
157                 }
158         
159                 internal void Write(int value)
160                 {
161                         if (pos + 4 > buffer.Length)
162                                 Grow(4);
163                         buffer[pos++] = (byte)value;
164                         buffer[pos++] = (byte)(value >> 8);
165                         buffer[pos++] = (byte)(value >> 16);
166                         buffer[pos++] = (byte)(value >> 24);
167                 }
168
169                 internal void Write(ulong value)
170                 {
171                         Write((long)value);
172                 }
173
174                 internal void Write(long value)
175                 {
176                         if (pos + 8 > buffer.Length)
177                                 Grow(8);
178                         buffer[pos++] = (byte)value;
179                         buffer[pos++] = (byte)(value >> 8);
180                         buffer[pos++] = (byte)(value >> 16);
181                         buffer[pos++] = (byte)(value >> 24);
182                         buffer[pos++] = (byte)(value >> 32);
183                         buffer[pos++] = (byte)(value >> 40);
184                         buffer[pos++] = (byte)(value >> 48);
185                         buffer[pos++] = (byte)(value >> 56);
186                 }
187
188                 internal void Write(float value)
189                 {
190                         Write(SingleConverter.SingleToInt32Bits(value));
191                 }
192
193                 internal void Write(double value)
194                 {
195                         Write(BitConverter.DoubleToInt64Bits(value));
196                 }
197
198                 internal void Write(string str)
199                 {
200                         if (str == null)
201                         {
202                                 Write((byte)0xFF);
203                         }
204                         else
205                         {
206                                 byte[] buf = Encoding.UTF8.GetBytes(str);
207                                 WriteCompressedInt(buf.Length);
208                                 Write(buf);
209                         }
210                 }
211
212                 internal void WriteCompressedInt(int value)
213                 {
214                         if (value <= 0x7F)
215                         {
216                                 Write((byte)value);
217                         }
218                         else if (value <= 0x3FFF)
219                         {
220                                 Write((byte)(0x80 | (value >> 8)));
221                                 Write((byte)value);
222                         }
223                         else
224                         {
225                                 Write((byte)(0xC0 | (value >> 24)));
226                                 Write((byte)(value >> 16));
227                                 Write((byte)(value >> 8));
228                                 Write((byte)value);
229                         }
230                 }
231
232                 internal void Write(ByteBuffer bb)
233                 {
234                         if (pos + bb.Length > buffer.Length)
235                                 Grow(bb.Length);
236                         Buffer.BlockCopy(bb.buffer, 0, buffer, pos, bb.Length);
237                         pos += bb.Length;
238                 }
239
240                 internal void WriteTo(System.IO.Stream stream)
241                 {
242                         stream.Write(buffer, 0, this.Length);
243                 }
244
245                 internal void Clear()
246                 {
247                         pos = 0;
248                         __length = 0;
249                 }
250
251                 internal void Align(int alignment)
252                 {
253                         if (pos + alignment > buffer.Length)
254                                 Grow(alignment);
255                         int newpos = (pos + alignment - 1) & ~(alignment - 1);
256                         while (pos < newpos)
257                                 buffer[pos++] = 0;
258                 }
259
260                 internal void WriteTypeDefOrRefEncoded(int token)
261                 {
262                         switch (token >> 24)
263                         {
264                                 case TypeDefTable.Index:
265                                         WriteCompressedInt((token & 0xFFFFFF) << 2 | 0);
266                                         break;
267                                 case TypeRefTable.Index:
268                                         WriteCompressedInt((token & 0xFFFFFF) << 2 | 1);
269                                         break;
270                                 case TypeSpecTable.Index:
271                                         WriteCompressedInt((token & 0xFFFFFF) << 2 | 2);
272                                         break;
273                                 default:
274                                         throw new InvalidOperationException();
275                         }
276                 }
277
278                 internal void Write(System.IO.Stream stream)
279                 {
280                         const int chunkSize = 8192;
281                         for (; ; )
282                         {
283                                 if (pos + chunkSize > buffer.Length)
284                                         Grow(chunkSize);
285                                 int read = stream.Read(buffer, pos, chunkSize);
286                                 if (read <= 0)
287                                 {
288                                         break;
289                                 }
290                                 pos += read;
291                         }
292                 }
293
294                 internal byte[] ToArray()
295                 {
296                         int len = this.Length;
297                         byte[] buf = new byte[len];
298                         Buffer.BlockCopy(buffer, 0, buf, 0, len);
299                         return buf;
300                 }
301
302                 internal static ByteBuffer Wrap(byte[] buf)
303                 {
304                         return new ByteBuffer(buf, buf.Length);
305                 }
306
307                 internal static ByteBuffer Wrap(byte[] buf, int length)
308                 {
309                         return new ByteBuffer(buf, length);
310                 }
311
312                 internal bool Match(int pos, ByteBuffer bb2, int pos2, int len)
313                 {
314                         for (int i = 0; i < len; i++)
315                         {
316                                 if (buffer[pos + i] != bb2.buffer[pos2 + i])
317                                 {
318                                         return false;
319                                 }
320                         }
321                         return true;
322                 }
323
324                 internal int Hash()
325                 {
326                         int hash = 0;
327                         int len = this.Length;
328                         for (int i = 0; i < len; i++)
329                         {
330                                 hash *= 37;
331                                 hash ^= buffer[i];
332                         }
333                         return hash;
334                 }
335
336                 internal IKVM.Reflection.Reader.ByteReader GetBlob(int offset)
337                 {
338                         return IKVM.Reflection.Reader.ByteReader.FromBlob(buffer, offset);
339                 }
340         }
341 }