Merge pull request #205 from m3rlinez/master
[mono.git] / mcs / class / I18N / Common / ByteSafeEncoding.cs
1 /*
2  * ByteSafeEncoding.cs - Implementation of the "I18N.Common.ByteSafeEncoding" class.
3  *
4  * Copyright (c) 2002  Southern Storm Software, Pty Ltd
5  * Copytight (c) 2011  Pablo Ruiz GarcĂ­a
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23  * OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 namespace I18N.Common
27 {
28
29 using System;
30 using System.Runtime.InteropServices;
31 using System.Text;
32
33 // This class provides an abstract base for encodings that use a single
34 // byte per character.  The bulk of the work is done in this class, with
35 // subclasses providing implementations of the "ToBytes" methods to perform
36 // the char->byte conversion.
37
38 #if DISABLE_UNSAFE
39
40 [Serializable]
41 public abstract class ByteSafeEncoding : MonoSafeEncoding
42 {
43         // Internal state.
44         protected char[] toChars;
45         protected String encodingName;
46         protected String bodyName;
47         protected String headerName;
48         protected String webName;
49         protected bool isBrowserDisplay;
50         protected bool isBrowserSave;
51         protected bool isMailNewsDisplay;
52         protected bool isMailNewsSave;
53         protected int windowsCodePage;
54 #if NET_2_0
55         static byte [] isNormalized;
56         static byte [] isNormalizedComputed;
57         static byte [] normalization_bytes;
58 #endif
59
60         // Constructor.
61         protected ByteSafeEncoding(int codePage, char[] toChars,
62                                                    String encodingName, String bodyName,
63                                                    String headerName, String webName,
64                                                    bool isBrowserDisplay, bool isBrowserSave,
65                                                    bool isMailNewsDisplay, bool isMailNewsSave,
66                                                    int windowsCodePage)
67                         : base(codePage)
68                         {
69                                 if (toChars.Length != byte.MaxValue + 1)
70                                         throw new ArgumentException("toChars");
71
72                                 this.toChars = toChars;
73                                 this.encodingName = encodingName;
74                                 this.bodyName = bodyName;
75                                 this.headerName = headerName;
76                                 this.webName = webName;
77                                 this.isBrowserDisplay = isBrowserDisplay;
78                                 this.isBrowserSave = isBrowserSave;
79                                 this.isMailNewsDisplay = isMailNewsDisplay;
80                                 this.isMailNewsSave = isMailNewsSave;
81                                 this.windowsCodePage = windowsCodePage;
82                         }
83
84 #if NET_2_0
85         public override bool IsAlwaysNormalized (NormalizationForm form)
86         {
87                 if (form != NormalizationForm.FormC)
88                         return false;
89
90                 if (isNormalized == null)
91                         isNormalized = new byte [0x10000 / 8];
92                 if (isNormalizedComputed == null)
93                         isNormalizedComputed = new byte [0x10000 / 8];
94
95                 if (normalization_bytes == null) {
96                         normalization_bytes = new byte [0x100];
97                         lock (normalization_bytes) {
98                                 for (int i = 0; i < 0x100; i++)
99                                         normalization_bytes [i] = (byte) i;
100                         }
101                 }
102
103                 byte offset = (byte) (1 << (CodePage % 8));
104                 if ((isNormalizedComputed [CodePage / 8] & offset) == 0) {
105                         Encoding e = Clone () as Encoding;
106                         e.DecoderFallback = new DecoderReplacementFallback ("");
107                         string s = e.GetString (normalization_bytes);
108                         // note that the flag only stores FormC information.
109                         if (s != s.Normalize (form))
110                                 isNormalized [CodePage / 8] |= offset;
111                         isNormalizedComputed [CodePage / 8] |= offset;
112                 }
113
114                 return (isNormalized [CodePage / 8] & offset) == 0;
115         }
116
117         public override bool IsSingleByte {
118                 get { return true; }
119         }
120 #endif
121
122         public override int GetByteCount(String s)
123         {
124                 if(s == null)
125                         throw new ArgumentNullException("s");
126                 
127                 return s.Length;
128         }
129
130         // Get the number of bytes needed to encode a character buffer.
131         public override int GetByteCount(char[] chars, int index, int count)
132         {
133                 return count - index;
134         }
135
136         public override unsafe int GetByteCount(char* chars, int count)
137         {
138                 return count;
139         }
140
141         // Convert an array of characters into a byte buffer,
142         // once the parameters have been validated.
143         protected abstract void ToBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex);
144
145         // Convert a string into a byte buffer, once the parameters
146         // have been validated.
147         protected virtual void ToBytes(String s, int charIndex, int charCount, byte[] bytes, int byteIndex)
148         {
149                 // When it is not overriden, use ToBytes() with pointers
150                 // (Ideal solution)
151                 if (s.Length == 0 || bytes.Length == byteIndex)
152                         return;
153
154                 ToBytes(s.ToCharArray(), charIndex, charCount, bytes, byteIndex);
155         }
156
157         // Get the bytes that result from encoding a character buffer.
158         public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
159         {
160                 if(chars == null)
161                 {
162                         throw new ArgumentNullException("chars");
163                 }
164                 if(bytes == null)
165                 {
166                         throw new ArgumentNullException("bytes");
167                 }
168                 if(charIndex < 0 || charIndex > chars.Length)
169                 {
170                         throw new ArgumentOutOfRangeException
171                                 ("charIndex", Strings.GetString("ArgRange_Array"));
172                 }
173                 if(charCount < 0 || charCount > (chars.Length - charIndex))
174                 {
175                         throw new ArgumentOutOfRangeException
176                                 ("charCount", Strings.GetString("ArgRange_Array"));
177                 }
178                 if(byteIndex < 0 || byteIndex > bytes.Length)
179                 {
180                         throw new ArgumentOutOfRangeException
181                                 ("byteIndex", Strings.GetString("ArgRange_Array"));
182                 }
183                 if((bytes.Length - byteIndex) < charCount)
184                 {
185                         throw new ArgumentException
186                                 (Strings.GetString("Arg_InsufficientSpace"));
187                 }
188
189                 ToBytes(chars, charIndex, charCount, bytes, byteIndex);
190                 return charCount;
191         }
192
193         // Convenience wrappers for "GetBytes".
194         public override int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex)
195         {
196                 if(s == null)
197                 {
198                         throw new ArgumentNullException("s");
199                 }
200                 if(bytes == null)
201                 {
202                         throw new ArgumentNullException("bytes");
203                 }
204                 if(charIndex < 0 || charIndex > s.Length)
205                 {
206                         throw new ArgumentOutOfRangeException
207                                 ("charIndex",
208                                         Strings.GetString("ArgRange_StringIndex"));
209                 }
210                 if(charCount < 0 || charCount > (s.Length - charIndex))
211                 {
212                         throw new ArgumentOutOfRangeException
213                                 ("charCount",
214                                         Strings.GetString("ArgRange_StringRange"));
215                 }
216                 if(byteIndex < 0 || byteIndex > bytes.Length)
217                 {
218                         throw new ArgumentOutOfRangeException
219                                 ("byteIndex", Strings.GetString("ArgRange_Array"));
220                 }
221                 if((bytes.Length - byteIndex) < charCount)
222                 {
223                         throw new ArgumentException
224                                 (Strings.GetString("Arg_InsufficientSpace"));
225                 }
226
227                 ToBytes(s, charIndex, charCount, bytes, byteIndex);
228                 return charCount;
229         }
230
231         public override byte[] GetBytes(string s)
232         {
233                 if (s == null)
234                         throw new ArgumentNullException("s");
235
236                 char[] data = s.ToCharArray();
237
238                 return GetBytes(data, 0, data.Length);
239         }
240
241         // Get the number of characters needed to decode a byte buffer.
242         public override int GetCharCount(byte[] bytes, int index, int count)
243                         {
244                                 if(bytes == null)
245                                 {
246                                         throw new ArgumentNullException("bytes");
247                                 }
248                                 if(index < 0 || index > bytes.Length)
249                                 {
250                                         throw new ArgumentOutOfRangeException
251                                                 ("index", Strings.GetString("ArgRange_Array"));
252                                 }
253                                 if(count < 0 || count > (bytes.Length - index))
254                                 {
255                                         throw new ArgumentOutOfRangeException
256                                                 ("count", Strings.GetString("ArgRange_Array"));
257                                 }
258                                 return count;
259                         }
260
261         // Get the characters that result from decoding a byte buffer.
262         public override int GetChars(byte[] bytes, int byteIndex, int byteCount,
263                                                                  char[] chars, int charIndex)
264                         {
265                                 if(bytes == null)
266                                 {
267                                         throw new ArgumentNullException("bytes");
268                                 }
269                                 if(chars == null)
270                                 {
271                                         throw new ArgumentNullException("chars");
272                                 }
273                                 if(byteIndex < 0 || byteIndex > bytes.Length)
274                                 {
275                                         throw new ArgumentOutOfRangeException
276                                                 ("byteIndex", Strings.GetString("ArgRange_Array"));
277                                 }
278                                 if(byteCount < 0 || byteCount > (bytes.Length - byteIndex))
279                                 {
280                                         throw new ArgumentOutOfRangeException
281                                                 ("byteCount", Strings.GetString("ArgRange_Array"));
282                                 }
283                                 if(charIndex < 0 || charIndex > chars.Length)
284                                 {
285                                         throw new ArgumentOutOfRangeException
286                                                 ("charIndex", Strings.GetString("ArgRange_Array"));
287                                 }
288                                 if((chars.Length - charIndex) < byteCount)
289                                 {
290                                         throw new ArgumentException
291                                                 (Strings.GetString("Arg_InsufficientSpace"));
292                                 }
293                                 int count = byteCount;
294                                 char[] cvt = toChars;
295                                 while(count-- > 0)
296                                 {
297                                         chars[charIndex++] = cvt[(int)(bytes[byteIndex++])];
298                                 }
299                                 return byteCount;
300                         }
301
302         // Get the maximum number of bytes needed to encode a
303         // specified number of characters.
304         public override int GetMaxByteCount(int charCount)
305                         {
306                                 if(charCount < 0)
307                                 {
308                                         throw new ArgumentOutOfRangeException
309                                                 ("charCount",
310                                                  Strings.GetString("ArgRange_NonNegative"));
311                                 }
312                                 return charCount;
313                         }
314
315         // Get the maximum number of characters needed to decode a
316         // specified number of bytes.
317         public override int GetMaxCharCount(int byteCount)
318                         {
319                                 if(byteCount < 0)
320                                 {
321                                         throw new ArgumentOutOfRangeException
322                                                 ("byteCount",
323                                                  Strings.GetString("ArgRange_NonNegative"));
324                                 }
325                                 return byteCount;
326                         }
327
328         // Decode a buffer of bytes into a string.
329         public unsafe override String GetString(byte[] bytes, int index, int count)
330                         {
331                                 if(bytes == null)
332                                 {
333                                         throw new ArgumentNullException("bytes");
334                                 }
335                                 if(index < 0 || index > bytes.Length)
336                                 {
337                                         throw new ArgumentOutOfRangeException
338                                                 ("index", Strings.GetString("ArgRange_Array"));
339                                 }
340                                 if(count < 0 || count > (bytes.Length - index))
341                                 {
342                                         throw new ArgumentOutOfRangeException
343                                                 ("count", Strings.GetString("ArgRange_Array"));
344                                 }
345
346                                 if (count == 0)
347                                         return string.Empty;
348
349                                 string s = new string ((char) 0, count);
350
351                                 fixed (byte* bytePtr = bytes)
352                                         fixed (char* charPtr = s)
353                                                 fixed (char* cvt = toChars) {
354                                                         byte* b = bytePtr + index;
355                                                         char* c = charPtr;
356                                                         while(count-- != 0)
357                                                                 *(c++) = cvt[*(b++)];
358                                                 }
359
360                                 return s;
361                         }
362         public override String GetString(byte[] bytes)
363                         {
364                                 if(bytes == null)
365                                 {
366                                         throw new ArgumentNullException("bytes");
367                                 }
368
369                                 return GetString (bytes, 0, bytes.Length);
370                         }
371
372 #if !ECMA_COMPAT
373
374         // Get the mail body name for this encoding.
375         public override String BodyName
376                         {
377                                 get
378                                 {
379                                         return bodyName;
380                                 }
381                         }
382
383         // Get the human-readable name for this encoding.
384         public override String EncodingName
385                         {
386                                 get
387                                 {
388                                         return encodingName;
389                                 }
390                         }
391
392         // Get the mail agent header name for this encoding.
393         public override String HeaderName
394                         {
395                                 get
396                                 {
397                                         return headerName;
398                                 }
399                         }
400
401         // Determine if this encoding can be displayed in a Web browser.
402         public override bool IsBrowserDisplay
403                         {
404                                 get
405                                 {
406                                         return isBrowserDisplay;
407                                 }
408                         }
409
410         // Determine if this encoding can be saved from a Web browser.
411         public override bool IsBrowserSave
412                         {
413                                 get
414                                 {
415                                         return isBrowserSave;
416                                 }
417                         }
418
419         // Determine if this encoding can be displayed in a mail/news agent.
420         public override bool IsMailNewsDisplay
421                         {
422                                 get
423                                 {
424                                         return isMailNewsDisplay;
425                                 }
426                         }
427
428         // Determine if this encoding can be saved from a mail/news agent.
429         public override bool IsMailNewsSave
430                         {
431                                 get
432                                 {
433                                         return isMailNewsSave;
434                                 }
435                         }
436
437         // Get the IANA-preferred Web name for this encoding.
438         public override String WebName
439                         {
440                                 get
441                                 {
442                                         return webName;
443                                 }
444                         }
445
446         // Get the Windows code page represented by this object.
447         public override int WindowsCodePage
448                         {
449                                 get
450                                 {
451                                         return windowsCodePage;
452                                 }
453                         }
454
455 #endif // !ECMA_COMPAT
456
457 }; // class ByteEncoding
458
459 #endif
460 }; // namespace I18N.Encoding