Update to the latest IKVM.Reflection
[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 GetCompressedUIntLength()
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                                 WriteCompressedUInt(buf.Length);
208                                 Write(buf);
209                         }
210                 }
211
212                 internal void WriteCompressedUInt(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 WriteCompressedInt(int value)
233                 {
234                         if (value >= 0)
235                         {
236                                 WriteCompressedUInt(value << 1);
237                         }
238                         else if (value >= -64)
239                         {
240                                 value = ((value << 1) & 0x7F) | 1;
241                                 Write((byte)value);
242                         }
243                         else if (value >= -8192)
244                         {
245                                 value = ((value << 1) & 0x3FFF) | 1;
246                                 Write((byte)(0x80 | (value >> 8)));
247                                 Write((byte)value);
248                         }
249                         else
250                         {
251                                 value = ((value << 1) & 0x1FFFFFFF) | 1;
252                                 Write((byte)(0xC0 | (value >> 24)));
253                                 Write((byte)(value >> 16));
254                                 Write((byte)(value >> 8));
255                                 Write((byte)value);
256                         }
257                 }
258
259                 internal void Write(ByteBuffer bb)
260                 {
261                         if (pos + bb.Length > buffer.Length)
262                                 Grow(bb.Length);
263                         Buffer.BlockCopy(bb.buffer, 0, buffer, pos, bb.Length);
264                         pos += bb.Length;
265                 }
266
267                 internal void WriteTo(System.IO.Stream stream)
268                 {
269                         stream.Write(buffer, 0, this.Length);
270                 }
271
272                 internal void Clear()
273                 {
274                         pos = 0;
275                         __length = 0;
276                 }
277
278                 internal void Align(int alignment)
279                 {
280                         if (pos + alignment > buffer.Length)
281                                 Grow(alignment);
282                         int newpos = (pos + alignment - 1) & ~(alignment - 1);
283                         while (pos < newpos)
284                                 buffer[pos++] = 0;
285                 }
286
287                 internal void WriteTypeDefOrRefEncoded(int token)
288                 {
289                         switch (token >> 24)
290                         {
291                                 case TypeDefTable.Index:
292                                         WriteCompressedUInt((token & 0xFFFFFF) << 2 | 0);
293                                         break;
294                                 case TypeRefTable.Index:
295                                         WriteCompressedUInt((token & 0xFFFFFF) << 2 | 1);
296                                         break;
297                                 case TypeSpecTable.Index:
298                                         WriteCompressedUInt((token & 0xFFFFFF) << 2 | 2);
299                                         break;
300                                 default:
301                                         throw new InvalidOperationException();
302                         }
303                 }
304
305                 internal void Write(System.IO.Stream stream)
306                 {
307                         const int chunkSize = 8192;
308                         for (; ; )
309                         {
310                                 if (pos + chunkSize > buffer.Length)
311                                         Grow(chunkSize);
312                                 int read = stream.Read(buffer, pos, chunkSize);
313                                 if (read <= 0)
314                                 {
315                                         break;
316                                 }
317                                 pos += read;
318                         }
319                 }
320
321                 internal byte[] ToArray()
322                 {
323                         int len = this.Length;
324                         byte[] buf = new byte[len];
325                         Buffer.BlockCopy(buffer, 0, buf, 0, len);
326                         return buf;
327                 }
328
329                 internal static ByteBuffer Wrap(byte[] buf)
330                 {
331                         return new ByteBuffer(buf, buf.Length);
332                 }
333
334                 internal static ByteBuffer Wrap(byte[] buf, int length)
335                 {
336                         return new ByteBuffer(buf, length);
337                 }
338
339                 internal bool Match(int pos, ByteBuffer bb2, int pos2, int len)
340                 {
341                         for (int i = 0; i < len; i++)
342                         {
343                                 if (buffer[pos + i] != bb2.buffer[pos2 + i])
344                                 {
345                                         return false;
346                                 }
347                         }
348                         return true;
349                 }
350
351                 internal int Hash()
352                 {
353                         int hash = 0;
354                         int len = this.Length;
355                         for (int i = 0; i < len; i++)
356                         {
357                                 hash *= 37;
358                                 hash ^= buffer[i];
359                         }
360                         return hash;
361                 }
362
363                 internal IKVM.Reflection.Reader.ByteReader GetBlob(int offset)
364                 {
365                         return IKVM.Reflection.Reader.ByteReader.FromBlob(buffer, offset);
366                 }
367         }
368 }