39e0714a4d64eff7a67ba5ed0d4774a7013be22a
[m17n/m17n-lib-cs.git] / MCharTable.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using M17N;
5 using M17N.Core;
6
7 namespace M17N.Core
8 {
9   public class MCharRange
10   {
11     internal int from;
12     internal int to;
13     internal object value;
14     internal MCharTable from_table;
15     internal MCharTable to_table;
16
17     public int From { get { return from; } }
18     public int To { get { return to; } }
19     public object Value { get { return value; } }
20
21     internal static void CheckChar (int c)
22     {
23       if (c < 0 || c > 0x10FFFF)
24         throw new ArgumentException ("Invalid Unicode character: " + c);
25     }
26     
27     public MCharRange (int c, MCharTable table)
28     {
29       CheckChar (c);
30       value = table.Get (c, out table);
31       if (c == 0)
32         {
33           from = c;
34           from_table = table;
35         }
36       else
37         from = table.PrevBoundary (c - 1, value, out from_table) + 1;
38       if (c == 0x10FFFF)
39         {
40           to = c;
41           to_table = table;
42         }
43       else
44         to = table.NextBoundary (c + 1, value, out to_table) - 1;
45     }
46
47     public bool Prev ()
48     {
49       if (from == 0)
50         return false;
51       to = from - 1;
52       value = from_table.Get (to, out to_table);
53       if (to == 0)
54         {
55           from = to;
56           from_table = to_table;
57         }
58       else
59         from = to_table.PrevBoundary (to - 1, value, out from_table) + 1;
60       return true;
61     }
62
63     public bool Next ()
64     {
65       if (to == 0x10FFFF)
66         return false;
67       from = to + 1;
68       value = to_table.Get (from, out from_table);
69       if (from == 0x10FFFF)
70         {
71           to = from;
72           to_table = from_table;
73         }
74       else
75         to = from_table.NextBoundary (from + 1, value, out to_table) - 1;
76       return true;
77     }
78
79     public override string ToString ()
80     {
81       return String.Format ("[U+{0:X}..U+{1:X} {2}]", from, to,
82                             value == null ? "null" : value);
83     }
84   }
85
86   public class MCharTable : IEnumerable<MCharRange>
87   {
88     private static readonly int[] nchars
89       = new int[] { 0x110000, 0x10000, 0x1000, 0x80 };
90     private static readonly int[] slots
91       = new int[] { 17, 16, 32, 128 };
92     private static readonly int[] shift
93       = new int[] { 16, 12, 7, 0 };
94
95     private static bool IsSubCharTable (object obj)
96       {
97         return (obj is MCharTable
98                 && ((MCharTable) obj).depth > 0);
99       }
100
101     private int index (int c) { return ((c - min_char) >> shift[depth]); }
102
103     private MCharTable parent;
104     private int depth;
105     private int min_char, max_char;
106     private object[] contents;
107
108     public MCharTable ()
109       {
110         parent = null;
111         depth = 0;
112         min_char = 0;
113         max_char = 0x10FFFF;
114         contents = new object[slots[0]];
115       }
116
117     private MCharTable (MCharTable parent, int min_char, object value)
118       {
119         this.parent = parent;
120         this.min_char = min_char;
121         depth = parent.depth + 1;
122         max_char = min_char + nchars[depth] - 1;
123         contents = new object[slots[depth]];
124         for (int i = 0; i < slots[depth]; i++)
125           contents[i] = value;
126       }
127
128     private object Get (int c)
129     {
130       object slot = contents[index (c)];
131       if (IsSubCharTable (slot))
132         return ((MCharTable) slot).Get (c);
133       return slot;
134     }
135
136     internal object Get (int c, out MCharTable table)
137     {
138       if (c < min_char || c > max_char)
139         return parent.Get (c, out table);
140       object slot = contents[index (c)];
141       if (IsSubCharTable (slot))
142         return ((MCharTable) slot).Get (c, out table);
143       table = this;
144       return slot;
145     }
146
147     private void Set (int c, object value)
148     {
149       int i = index (c);
150
151       if (depth == 3)
152         contents[i] = value;
153       else
154         {
155           if (! IsSubCharTable (contents[i]))
156             contents[i] = new MCharTable (this, min_char + (i << shift[depth]),
157                                           contents[i]);
158           ((MCharTable) contents[i]).Set (c, value);
159         }
160     }
161
162     public object this[int c]
163     {
164       get {
165         MCharRange.CheckChar (c);
166         return Get (c);
167       }
168
169       set {
170         MCharRange.CheckChar (c);
171         Set (c, value);
172       }
173     }
174
175     public object this[int from, int to]
176     {
177       set {
178         MCharRange.CheckChar (from);
179         MCharRange.CheckChar (to);
180         set_range (from, to, value);
181       }
182     }
183
184     private void set_range (int from, int to, object value)
185     {
186       if (to > max_char)
187         to = max_char;
188       int i0 = index (from), i1 = index (to);
189
190       if (depth == 3)
191         {
192           for (; i0 <= i1; i0++)
193             contents[i0] = value;
194         }
195       else
196         {
197           int min0 = min_char + (i0 << shift[depth]);
198           int min1 = min_char + (i1 << shift[depth]);
199
200           if (min0 < from)
201             {
202               if (contents[i0] != value)
203                 {
204                   if (! IsSubCharTable (contents[i0]))
205                     contents[i0] = new MCharTable (this, min0, contents[i0]);
206                   ((MCharTable) contents[i0]).set_range (from, to, value);
207                 }
208               from = min0 + nchars[depth + 1];
209               if (from >= to)
210                 return;
211               i0++;
212             }
213           for (; i0 < i1; i0++)
214             contents[i0] = value;
215           if (to == min1 + nchars[depth + 1] - 1)
216             contents[i1] = value;
217           else if (contents[i1] != value)
218             {
219               if (! IsSubCharTable (contents[i1]))
220                 contents[i1] = new MCharTable (this, min1, contents[i1]);
221               ((MCharTable) contents[i1]).set_range (min1, to, value);
222             }
223         }
224     }
225
226     internal int NextBoundary (int c, object value, out MCharTable table)
227     {
228       table = this;
229       for (int i = index (c); i < slots[depth];
230            i++, c = min_char + (i << shift[depth]))
231         if (contents[i] != value)
232           return (IsSubCharTable (contents[i])
233                   ? ((MCharTable) contents[i]).NextBoundary (c, value,
234                                                              out table)
235                   : c);
236       return (depth == 0
237               ? c : parent.NextBoundary (c, value, out table));
238     }
239
240     internal int PrevBoundary (int c, object value, out MCharTable table)
241     {
242       table = this;
243       for (int i = index (c); i >= 0;
244            c = min_char + (i << shift[depth]) - 1, i--)
245         if (contents[i] != value)
246           return (IsSubCharTable (contents[i])
247                   ? ((MCharTable) contents[i]).PrevBoundary (c, value,
248                                                              out table)
249                   : c);
250       return (depth == 0
251               ? c : parent.PrevBoundary (c, value, out table));
252     }
253
254     // for IEnumerable interface
255     public IEnumerator<MCharRange> GetEnumerator()
256     {
257       return new MCharTableEnum (this);
258     }
259
260     IEnumerator IEnumerable.GetEnumerator()
261     {
262       return GetEnumerator ();
263     }
264
265     private class MCharTableEnum : IEnumerator<MCharRange>
266     {
267       MCharTable table;
268       private MCharRange range;
269
270       public MCharTableEnum (MCharTable table) {
271         this.table = table;
272       }
273
274       public bool MoveNext ()
275       {
276         if (range == null)
277           {
278             range = new MCharRange (0, table);
279             return true;
280           }
281         return range.Next ();
282       }
283
284       public void Reset () { range = null; }
285
286       public MCharRange Current { get { return range; } }
287
288       object IEnumerator.Current { get { return Current; } }
289
290       public void Dispose () {}
291     }
292   }
293 }