Merge pull request #2803 from BrzVlad/feature-conc-pinned-scan
[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         static byte [] isNormalized;
55         static byte [] isNormalizedComputed;
56         static byte [] normalization_bytes;
57
58         // Constructor.
59         protected ByteSafeEncoding(int codePage, char[] toChars,
60                                                    String encodingName, String bodyName,
61                                                    String headerName, String webName,
62                                                    bool isBrowserDisplay, bool isBrowserSave,
63                                                    bool isMailNewsDisplay, bool isMailNewsSave,
64                                                    int windowsCodePage)
65                         : base(codePage)
66                         {
67                                 if (toChars.Length != byte.MaxValue + 1)
68                                         throw new ArgumentException("toChars");
69
70                                 this.toChars = toChars;
71                                 this.encodingName = encodingName;
72                                 this.bodyName = bodyName;
73                                 this.headerName = headerName;
74                                 this.webName = webName;
75                                 this.isBrowserDisplay = isBrowserDisplay;
76                                 this.isBrowserSave = isBrowserSave;
77                                 this.isMailNewsDisplay = isMailNewsDisplay;
78                                 this.isMailNewsSave = isMailNewsSave;
79                                 this.windowsCodePage = windowsCodePage;
80                         }
81
82         public override bool IsAlwaysNormalized (NormalizationForm form)
83         {
84                 if (form != NormalizationForm.FormC)
85                         return false;
86
87                 if (isNormalized == null)
88                         isNormalized = new byte [0x10000 / 8];
89                 if (isNormalizedComputed == null)
90                         isNormalizedComputed = new byte [0x10000 / 8];
91
92                 if (normalization_bytes == null) {
93                         normalization_bytes = new byte [0x100];
94                         lock (normalization_bytes) {
95                                 for (int i = 0; i < 0x100; i++)
96                                         normalization_bytes [i] = (byte) i;
97                         }
98                 }
99
100                 byte offset = (byte) (1 << (CodePage % 8));
101                 if ((isNormalizedComputed [CodePage / 8] & offset) == 0) {
102                         Encoding e = Clone () as Encoding;
103                         e.DecoderFallback = new DecoderReplacementFallback ("");
104                         string s = e.GetString (normalization_bytes);
105                         // note that the flag only stores FormC information.
106                         if (s != s.Normalize (form))
107                                 isNormalized [CodePage / 8] |= offset;
108                         isNormalizedComputed [CodePage / 8] |= offset;
109                 }
110
111                 return (isNormalized [CodePage / 8] & offset) == 0;
112         }
113
114         public override bool IsSingleByte {
115                 get { return true; }
116         }
117
118         public override int GetByteCount(String s)
119         {
120                 if(s == null)
121                         throw new ArgumentNullException("s");
122                 
123                 return s.Length;
124         }
125
126         // Get the number of bytes needed to encode a character buffer.
127         public override int GetByteCount(char[] chars, int index, int count)
128         {
129                 return count - index;
130         }
131
132         public override unsafe int GetByteCount(char* chars, int count)
133         {
134                 return count;
135         }
136
137         // Convert an array of characters into a byte buffer,
138         // once the parameters have been validated.
139         protected abstract void ToBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex);
140
141         // Convert a string into a byte buffer, once the parameters
142         // have been validated.
143         protected virtual void ToBytes(String s, int charIndex, int charCount, byte[] bytes, int byteIndex)
144         {
145                 // When it is not overriden, use ToBytes() with pointers
146                 // (Ideal solution)
147                 if (s.Length == 0 || bytes.Length == byteIndex)
148                         return;
149
150                 ToBytes(s.ToCharArray(), charIndex, charCount, bytes, byteIndex);
151         }
152
153         // Get the bytes that result from encoding a character buffer.
154         public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
155         {
156                 if(chars == null)
157                 {
158                         throw new ArgumentNullException("chars");
159                 }
160                 if(bytes == null)
161                 {
162                         throw new ArgumentNullException("bytes");
163                 }
164                 if(charIndex < 0 || charIndex > chars.Length)
165                 {
166                         throw new ArgumentOutOfRangeException
167                                 ("charIndex", Strings.GetString("ArgRange_Array"));
168                 }
169                 if(charCount < 0 || charCount > (chars.Length - charIndex))
170                 {
171                         throw new ArgumentOutOfRangeException
172                                 ("charCount", Strings.GetString("ArgRange_Array"));
173                 }
174                 if(byteIndex < 0 || byteIndex > bytes.Length)
175                 {
176                         throw new ArgumentOutOfRangeException
177                                 ("byteIndex", Strings.GetString("ArgRange_Array"));
178                 }
179                 if((bytes.Length - byteIndex) < charCount)
180                 {
181                         throw new ArgumentException
182                                 (Strings.GetString("Arg_InsufficientSpace"));
183                 }
184
185                 ToBytes(chars, charIndex, charCount, bytes, byteIndex);
186                 return charCount;
187         }
188
189         // Convenience wrappers for "GetBytes".
190         public override int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex)
191         {
192                 if(s == null)
193                 {
194                         throw new ArgumentNullException("s");
195                 }
196                 if(bytes == null)
197                 {
198                         throw new ArgumentNullException("bytes");
199                 }
200                 if(charIndex < 0 || charIndex > s.Length)
201                 {
202                         throw new ArgumentOutOfRangeException
203                                 ("charIndex",
204                                         Strings.GetString("ArgRange_StringIndex"));
205                 }
206                 if(charCount < 0 || charCount > (s.Length - charIndex))
207                 {
208                         throw new ArgumentOutOfRangeException
209                                 ("charCount",
210                                         Strings.GetString("ArgRange_StringRange"));
211                 }
212                 if(byteIndex < 0 || byteIndex > bytes.Length)
213                 {
214                         throw new ArgumentOutOfRangeException
215                                 ("byteIndex", Strings.GetString("ArgRange_Array"));
216                 }
217                 if((bytes.Length - byteIndex) < charCount)
218                 {
219                         throw new ArgumentException
220                                 (Strings.GetString("Arg_InsufficientSpace"));
221                 }
222
223                 ToBytes(s, charIndex, charCount, bytes, byteIndex);
224                 return charCount;
225         }
226
227         public override byte[] GetBytes(string s)
228         {
229                 if (s == null)
230                         throw new ArgumentNullException("s");
231
232                 char[] data = s.ToCharArray();
233
234                 return GetBytes(data, 0, data.Length);
235         }
236
237         // Get the number of characters needed to decode a byte buffer.
238         public override int GetCharCount(byte[] bytes, int index, int count)
239                         {
240                                 if(bytes == null)
241                                 {
242                                         throw new ArgumentNullException("bytes");
243                                 }
244                                 if(index < 0 || index > bytes.Length)
245                                 {
246                                         throw new ArgumentOutOfRangeException
247                                                 ("index", Strings.GetString("ArgRange_Array"));
248                                 }
249                                 if(count < 0 || count > (bytes.Length - index))
250                                 {
251                                         throw new ArgumentOutOfRangeException
252                                                 ("count", Strings.GetString("ArgRange_Array"));
253                                 }
254                                 return count;
255                         }
256
257         // Get the characters that result from decoding a byte buffer.
258         public override int GetChars(byte[] bytes, int byteIndex, int byteCount,
259                                                                  char[] chars, int charIndex)
260                         {
261                                 if(bytes == null)
262                                 {
263                                         throw new ArgumentNullException("bytes");
264                                 }
265                                 if(chars == null)
266                                 {
267                                         throw new ArgumentNullException("chars");
268                                 }
269                                 if(byteIndex < 0 || byteIndex > bytes.Length)
270                                 {
271                                         throw new ArgumentOutOfRangeException
272                                                 ("byteIndex", Strings.GetString("ArgRange_Array"));
273                                 }
274                                 if(byteCount < 0 || byteCount > (bytes.Length - byteIndex))
275                                 {
276                                         throw new ArgumentOutOfRangeException
277                                                 ("byteCount", Strings.GetString("ArgRange_Array"));
278                                 }
279                                 if(charIndex < 0 || charIndex > chars.Length)
280                                 {
281                                         throw new ArgumentOutOfRangeException
282                                                 ("charIndex", Strings.GetString("ArgRange_Array"));
283                                 }
284                                 if((chars.Length - charIndex) < byteCount)
285                                 {
286                                         throw new ArgumentException
287                                                 (Strings.GetString("Arg_InsufficientSpace"));
288                                 }
289                                 int count = byteCount;
290                                 char[] cvt = toChars;
291                                 while(count-- > 0)
292                                 {
293                                         chars[charIndex++] = cvt[(int)(bytes[byteIndex++])];
294                                 }
295                                 return byteCount;
296                         }
297
298         // Get the maximum number of bytes needed to encode a
299         // specified number of characters.
300         public override int GetMaxByteCount(int charCount)
301                         {
302                                 if(charCount < 0)
303                                 {
304                                         throw new ArgumentOutOfRangeException
305                                                 ("charCount",
306                                                  Strings.GetString("ArgRange_NonNegative"));
307                                 }
308                                 return charCount;
309                         }
310
311         // Get the maximum number of characters needed to decode a
312         // specified number of bytes.
313         public override int GetMaxCharCount(int byteCount)
314                         {
315                                 if(byteCount < 0)
316                                 {
317                                         throw new ArgumentOutOfRangeException
318                                                 ("byteCount",
319                                                  Strings.GetString("ArgRange_NonNegative"));
320                                 }
321                                 return byteCount;
322                         }
323
324         // Decode a buffer of bytes into a string.
325         public unsafe override String GetString(byte[] bytes, int index, int count)
326                         {
327                                 if(bytes == null)
328                                 {
329                                         throw new ArgumentNullException("bytes");
330                                 }
331                                 if(index < 0 || index > bytes.Length)
332                                 {
333                                         throw new ArgumentOutOfRangeException
334                                                 ("index", Strings.GetString("ArgRange_Array"));
335                                 }
336                                 if(count < 0 || count > (bytes.Length - index))
337                                 {
338                                         throw new ArgumentOutOfRangeException
339                                                 ("count", Strings.GetString("ArgRange_Array"));
340                                 }
341
342                                 if (count == 0)
343                                         return string.Empty;
344
345                                 string s = new string ((char) 0, count);
346
347                                 fixed (byte* bytePtr = bytes)
348                                         fixed (char* charPtr = s)
349                                                 fixed (char* cvt = toChars) {
350                                                         byte* b = bytePtr + index;
351                                                         char* c = charPtr;
352                                                         while(count-- != 0)
353                                                                 *(c++) = cvt[*(b++)];
354                                                 }
355
356                                 return s;
357                         }
358         public override String GetString(byte[] bytes)
359                         {
360                                 if(bytes == null)
361                                 {
362                                         throw new ArgumentNullException("bytes");
363                                 }
364
365                                 return GetString (bytes, 0, bytes.Length);
366                         }
367
368 #if !ECMA_COMPAT
369
370         // Get the mail body name for this encoding.
371         public override String BodyName
372                         {
373                                 get
374                                 {
375                                         return bodyName;
376                                 }
377                         }
378
379         // Get the human-readable name for this encoding.
380         public override String EncodingName
381                         {
382                                 get
383                                 {
384                                         return encodingName;
385                                 }
386                         }
387
388         // Get the mail agent header name for this encoding.
389         public override String HeaderName
390                         {
391                                 get
392                                 {
393                                         return headerName;
394                                 }
395                         }
396
397         // Determine if this encoding can be displayed in a Web browser.
398         public override bool IsBrowserDisplay
399                         {
400                                 get
401                                 {
402                                         return isBrowserDisplay;
403                                 }
404                         }
405
406         // Determine if this encoding can be saved from a Web browser.
407         public override bool IsBrowserSave
408                         {
409                                 get
410                                 {
411                                         return isBrowserSave;
412                                 }
413                         }
414
415         // Determine if this encoding can be displayed in a mail/news agent.
416         public override bool IsMailNewsDisplay
417                         {
418                                 get
419                                 {
420                                         return isMailNewsDisplay;
421                                 }
422                         }
423
424         // Determine if this encoding can be saved from a mail/news agent.
425         public override bool IsMailNewsSave
426                         {
427                                 get
428                                 {
429                                         return isMailNewsSave;
430                                 }
431                         }
432
433         // Get the IANA-preferred Web name for this encoding.
434         public override String WebName
435                         {
436                                 get
437                                 {
438                                         return webName;
439                                 }
440                         }
441
442         // Get the Windows code page represented by this object.
443         public override int WindowsCodePage
444                         {
445                                 get
446                                 {
447                                         return windowsCodePage;
448                                 }
449                         }
450
451 #endif // !ECMA_COMPAT
452
453 }; // class ByteEncoding
454
455 #endif
456 }; // namespace I18N.Encoding