3 using System.Collections;
4 using System.Collections.Generic;
11 public enum MTextFormat
13 MTEXT_FORMAT_US_ASCII,
15 MTEXT_FORMAT_UTF_16BE,
16 MTEXT_FORMAT_UTF_16LE,
17 MTEXT_FORMAT_UTF_32BE,
18 MTEXT_FORMAT_UTF_32LE,
22 public class MTextProperty
28 /// A text inserted before a character that has this property
29 /// inherits this property. If the text already has properties
30 /// of the same key, they are deleted. See the documentation of
31 /// Sensitive for exception.
33 /// A text inserted after a character that has this property
34 /// inherits this property. If the text already has properties
35 /// of the same key, they are deleted. See the documentation of
36 /// Sensitive for exception.
38 /// This property is deleted from a span of text if the span is
39 /// modified (i.e. a character is changed, a text is inserted,
40 /// some part is deleted). If this property is also FrontSticky
41 /// (or RearSticky), text insertion just before (or after) the
42 /// span also deletes this property from the span of text.
49 public MSymbol Key { get { return key; } }
50 public object Val { get { return val; } }
52 public MTextProperty (MSymbol key, object val)
58 public override string ToString ()
60 return key.ToString () + ":" + val;
64 public class MText : IEnumerable, IEquatable<MText>, IComparable<MText>
67 public enum MTextFormat format;
69 private StringBuilder sb;
71 private int cache_pos;
72 private int cache_idx;
73 private MPlist intervals;
74 private MPlist default_property;
75 private bool read_only;
77 private static UTF8Encoding utf8 = new UTF8Encoding ();
79 private static int count_chars (String str)
81 int len = str.Length, n = 0;
83 for (int i = 0; i < len; i++, n++)
84 if (surrogate_high_p (str[i]))
89 private static int count_chars (StringBuilder str)
91 int len = str.Length, n = 0;
93 for (int i = 0; i < len; i++, n++)
94 if (surrogate_high_p (str[i]))
101 sb = new StringBuilder ();
102 intervals = new MPlist ();
105 public MText (byte[] str)
107 sb = new StringBuilder (utf8.GetString (str));
108 nchars = count_chars (sb);
109 intervals = new MPlist ();
112 public MText (String str)
114 sb = new StringBuilder (str);
115 nchars = count_chars (str);
116 intervals = new MPlist ();
119 public MText (StringBuilder str)
122 nchars = count_chars (str);
123 intervals = new MPlist ();
126 public static MText operator+ (object obj, MText mt)
129 return new MText ((string) obj) + mt;
130 throw new Exception ("Unknown object type: " + obj.GetType());
133 public static MText operator+ (MText mt1, MText mt2)
135 MText mt = new MText ();
137 mt.sb.Append (mt1.sb);
138 mt.sb.Append (mt2.sb);
139 mt.nchars = mt1.nchars + mt2.nchars;
143 public static MText operator+ (string str, MText mt)
145 MText mtnew = new MText (str);
147 mtnew.sb.Append (mt.sb);
148 mtnew.nchars += mt.nchars;
153 public bool ReadOnly { get { return read_only; } }
154 public int Length { get { return nchars; } }
158 // for IEnumerable interface
159 public IEnumerator GetEnumerator() { return new MTextEnum (this); }
161 // for IEquatable interface
162 public bool Equals (MText other) { return this.sb.Equals (other.sb); }
164 // for IComparable interface
165 public int CompareTo (MText other)
167 return this.sb.ToString ().CompareTo (other.sb.ToString ());
170 public override String ToString () { return "\"" + sb.ToString () + "\""; }
172 private static bool surrogate_high_p (char c)
174 return (c >= 0xD800 && c < 0xDC00);
177 private static bool surrogate_low_p (char c)
179 return (c >= 0xDC00 && c < 0xE000);
182 private static int inc_idx (StringBuilder sb, int i)
184 return (i + (surrogate_high_p (sb[i]) ? 2 : 1));
187 private static int dec_idx (StringBuilder sb, int i)
189 return (i - (surrogate_low_p (sb[i - 1]) ? 2 : 1));
192 private static int pos_to_idx (MText mt, int pos)
194 if (pos == mt.cache_pos)
200 if (pos < mt.cache_pos)
202 if (mt.cache_pos == mt.cache_idx)
204 if (pos < mt.cache_pos - pos)
211 p = mt.cache_pos; i = mt.cache_idx;
217 if (mt.nchars - mt.cache_pos == mt.sb.Length - mt.cache_idx)
218 return (mt.cache_idx + pos - mt.cache_pos);
219 if (pos - mt.cache_pos < mt.nchars - pos)
221 p = mt.cache_pos; i = mt.cache_idx;
226 p = mt.nchars; i = mt.sb.Length;
231 for (; p < pos; i = inc_idx (mt.sb, i), p++);
233 for (; p > pos; i = dec_idx (mt.sb, i), p--);
239 private void check_pos (int pos, bool tail_ok)
241 if (pos < 0 || (tail_ok ? pos > nchars : pos >= nchars))
242 throw new Exception ("Invalid MText position:" + pos);
245 private bool check_range (int from, int to, bool zero_ok)
247 if (from < 0 || (zero_ok ? from > to : from >= to)
249 throw new Exception ("Invalid MText range");
253 private void insert (int pos, MText mt2, int from, int to)
255 check_pos (pos, true);
259 Console.Write ("inserting {0} to {1} of ", from, to);
260 mt2.DumpPropNested ();
264 foreach (MPlist plist in intervals)
266 MPlist p = mt2.intervals.Find (plist.Key);
267 MInterval i = p == null ? null : (MInterval) p.Val;
269 ((MInterval) plist.Val).Insert (pos, i, from, to);
271 foreach (MPlist plist in mt2.intervals)
272 if (intervals.Find (plist.Key) == null)
277 root = ((MInterval) plist.Val).Copy (this, from, to);
280 root = new MInterval (plist.Key, this);
281 root.Insert (pos, (MInterval) plist.Val, from, to);
283 intervals.Push (plist.Key, root);
286 int pos_idx = pos_to_idx (this, pos);
287 int from_idx = pos_to_idx (mt2, from);
288 int to_idx = pos_to_idx (mt2, to);
290 sb.Insert (pos_idx, mt2.sb.ToString (from_idx, to_idx - from_idx));
294 private void insert (int pos, int c)
296 check_pos (pos, true);
298 int pos_idx = pos_to_idx (this, pos);
303 sb.Insert (pos_idx, ch);
307 char high = (char) (0xD800 + ((c - 0x10000) >> 10));
308 char low = (char) (0xDC00 + ((c - 0x10000) & 0x3FF));
309 sb.Insert (pos_idx, low);
310 sb.Insert (pos_idx, high);
313 foreach (MPlist plist in intervals)
314 ((MInterval) plist.Val).Insert (pos, null, 0, 1);
317 public int this[int i]
320 i = pos_to_idx (this, i);
323 if (surrogate_high_p (sb[i]))
325 sb[i] = (char) value;
329 char high = (char) (0xD800 + ((value - 0x10000) >> 10));
330 char low = (char) (0xDC00 + ((value - 0x10000) & 0x3FF));
332 if (! surrogate_high_p (sb[i]))
339 i = pos_to_idx (this, i);
340 return (surrogate_high_p (sb[i])
341 ? ((sb[i] - 0xD800) << 10) + (sb[i + 1] - 0xDC00) + 0x10000
348 MText mt = new MText (sb.ToString ());
350 foreach (MPlist p in intervals)
351 mt.intervals.Add (p.Key, ((MInterval) p.Val).Copy (mt, 0, Length));
355 public MText Dup (int from, int to)
357 if (check_range (from, to, true))
359 int from_idx = pos_to_idx (this, from);
360 int len = pos_to_idx (this, to) - from_idx;
361 MText mt = new MText (sb.ToString ().Substring (from_idx, len));
363 foreach (MPlist p in intervals)
364 mt.intervals.Add (p.Key, ((MInterval) p.Val).Copy (mt, from, to));
368 public MText Ins (int pos, int c)
374 public MText Ins (int pos, MText mt)
376 insert (pos, mt, 0, mt.nchars);
380 public MText Ins (int pos, MText mt, int from, int to)
382 insert (pos, mt, from, to);
386 public MText Cat (int c)
392 public MText Del (int from, int to)
394 if (check_range (from, to, true))
397 sb.Remove (from, pos_to_idx (this, to) - pos_to_idx (this, from));
401 foreach (MPlist plist in intervals)
402 ((MInterval) plist.Val).Delete (from, to);
410 public object GetProp (int pos, MSymbol key)
412 check_pos (pos, false);
414 MInterval i = (MInterval) intervals.Get (key);
418 MTextProperty prop = i.Get (pos);
419 return (prop != null ? prop.Val : null);
422 public object GetProp (int pos, MSymbol key, out MTextProperty prop)
424 check_pos (pos, false);
426 MInterval i = (MInterval) intervals.Get (key);
428 return (prop = null);
430 return (prop != null ? prop.Val : null);
433 public object GetProp (int pos, MSymbol key, out MTextProperty[] array)
435 check_pos (pos, false);
437 MInterval i = (MInterval) intervals.Get (key);
439 return (array = null);
440 MTextProperty prop = i.Get (pos, out array);
441 return (prop != null ? prop.Val : null);
444 public void PushProp (int from, int to, MSymbol key, object val)
446 if (! check_range (from, to, true))
447 PushProp (from, to, new MTextProperty (key, val));
450 public void PushProp (int from, int to, MTextProperty prop)
454 if (default_property == null)
455 default_property = new MPlist ();
456 default_property.Push (prop.key, prop.val);
460 if (check_range (from, to, true))
463 MPlist p = intervals.Find (prop.key);
468 root = new MInterval (prop.key, this);
469 intervals.Push (prop.key, root);
472 root = (MInterval) p.Val;
474 root.Push (from, to, prop);
478 public void PopProp (int from, int to, MSymbol key)
482 if (default_property == null)
484 MPlist p = default_property.Find (key);
491 if (check_range (from, to, true))
494 MPlist p = intervals.Find (key);
498 MInterval root = (MInterval) p.Val;
500 root.MergeAfterChange (from, to);
505 public void DumpProp ()
508 foreach (MPlist p in intervals)
509 ((MInterval) p.Val).Dump (true);
510 Console.WriteLine (")");
513 public void DumpPropNested ()
515 Console.WriteLine ("total length = {0}", Length);
516 foreach (MPlist p in intervals)
517 ((MInterval) p.Val).DumpNested (true);
520 private class MInterval
522 // position: 0 1 2 3 4 5 6 7
523 // | A | B | C | D | E F | G |
524 // interval |---|---|---|<->|-------|---|
525 // |---|<->|---| |<----->|---|
529 // [3 (1 2)] [3 (4 6)]
530 // [1 (0 1)] [2 (2 3)] [1 (6 7)]
532 private static int count = 0;
535 private int From, To;
537 private MPlist Stack;
538 private MInterval Left, Right, Parent;
541 public MInterval (MSymbol key, MText mt, int length)
544 throw new Exception ("Invalid interval length");
548 Stack = new MPlist ();
552 public MInterval (MSymbol key, MText mt)
556 Length = mt.sb.Length;
559 Stack = new MPlist ();
563 /// POS must be smaller than Length;
564 public MTextProperty Get (int pos)
566 MInterval i = find (pos);
570 return (i.Stack.IsEmpty ? null : (MTextProperty) i.Stack.Val);
573 /// POS must be smaller than Length;
574 public MTextProperty Get (int pos, out MTextProperty[] array)
576 MInterval i = find (pos);
585 array = new MTextProperty[i.Stack.Count];
589 for (idx = 0, p = i.Stack; ! p.IsEmpty; idx++, p = p.Next)
590 array[idx] = (MTextProperty) p.Val;
594 private MInterval (MSymbol key, MText mt, int length, MPlist stack)
601 Stack = stack == null ? new MPlist () : stack.Clone ();
605 private bool isRearSticky
607 get { return ((Key.TextPropertyFlags & MTextProperty.Flags.RearSticky)
608 != MTextProperty.Flags.None); }
611 private bool isFrontSticky
613 get { return ((Key.TextPropertyFlags & MTextProperty.Flags.FrontSticky)
614 != MTextProperty.Flags.None); }
617 private bool isSensitive
619 get { return ((Key.TextPropertyFlags & MTextProperty.Flags.Sensitive)
620 != MTextProperty.Flags.None); }
623 private void update_from_to ()
628 To = Length - RightLength;
630 else if (Parent.Left == this)
632 From = Parent.From - Length + LeftLength;
633 To = Parent.From - RightLength;
637 From = Parent.To + LeftLength;
638 To = Parent.To + Length - RightLength;
642 private int LeftLength
644 get { return (Left == null ? 0 : Left.Length); }
647 private int RightLength
649 get { return (Right == null ? 0 : Right.Length); }
652 private MInterval LeftMost
658 return Left.LeftMost;
662 private MInterval RightMost
668 return Right.RightMost;
672 private MInterval Prev {
678 for (i = Left; i.Right != null; i = i.Right)
684 MInterval child = this;
685 for (i = Parent; i != null && i.Left == child;
686 child = i, i = i.Parent);
692 private MInterval Next {
698 for (i = Right; i.Left != null; i = i.Left)
704 MInterval child = this;
705 for (i = Parent; i != null && i.Right == child;
706 child = i, i = i.Parent);
712 private MInterval find (int pos)
716 return Left.find (pos);
718 return Right.find (pos);
722 private bool mergeable (MInterval i)
726 for (p1 = Stack, p2 = i.Stack; ! p1.IsEmpty && ! p2.IsEmpty;
727 p1 = p1.Next, p2 = p2.Next)
728 if (p1.Val != p2.Val)
730 return (p1.IsEmpty && p2.IsEmpty);
733 // p-. or .-p p-. or .-p
734 // .-this-. .-right-.
735 // left .-right-. -> .-this-. c2
737 private MInterval promote_right ()
739 int right_length = Right.Length;
743 mtext.intervals.Put (Key, Right);
744 else if (Parent.Left == this)
747 Parent.Right = Right;
748 Right.Parent = Parent;
754 Parent.Length += Length;
755 Length -= right_length;
759 Parent.Length -= c1.Length;
765 // p-. or .-p p-. or .-p
767 // .-left-. .-right-. -> c1 .-this-.
769 private MInterval promote_left ()
771 int left_length = Left.Length;
775 mtext.intervals.Put (Key, Left);
776 else if (Parent.Left == this)
780 Left.Parent = Parent;
786 Parent.Length += Length;
787 Length -= left_length;
791 Parent.Length -= c1.Length;
797 private MInterval balance ()
804 // .-left-. .-right-.
806 int diff = i.LeftLength - i.RightLength;
811 new_diff = (i.Length - i.LeftLength
812 + i.Left.RightLength - i.Left.LeftLength);
813 if (Math.Abs (new_diff) >= diff)
815 i = i.promote_left ();
820 new_diff = (i.Length - i.RightLength
821 + i.Right.LeftLength - i.Right.RightLength);
822 if (Math.Abs (new_diff) >= diff)
824 i = i.promote_right ();
831 public MInterval Copy (MText mt, int start, int end)
833 MInterval copy, left_copy = null, right_copy = null;
840 return Left.Copy (mt, start, end);
841 left_copy = Left.Copy (mt, start, From);
846 return Right.Copy (mt, start, end);
847 right_copy = Right.Copy (mt, To, end);
850 copy = new MInterval (Key, null, end - start, Stack);
852 if (isSensitive && (From < start || end < To))
854 if (left_copy != null)
856 copy.Left = left_copy;
857 left_copy.Parent = copy;
859 if (right_copy != null)
861 copy.Right = right_copy;
862 right_copy.Parent = copy;
870 private MInterval divide_right (int pos)
872 MInterval interval = new MInterval (Key, mtext, To - pos, Stack);
874 M17n.DebugPrint ("divide-right({0}) at ", pos); DumpOne (false, true);
878 interval.Right = Right;
879 Right.Parent = interval;
880 interval.Length += Right.Length;
882 interval.Parent = this;
890 private MInterval divide_left (int pos)
892 MInterval interval = new MInterval (Key, mtext, pos - From, Stack);
894 M17n.DebugPrint ("divide-left({0}) at ", pos); DumpOne (false, true);
898 interval.Left = Left;
899 Left.Parent = interval;
900 interval.Length += Left.Length;
902 interval.Parent = this;
907 private void set_mtext (MText mt)
913 Right.set_mtext (mt);
916 private void enlarge (int len)
920 for (MInterval prev = this, i = this.Parent; i != null;
921 prev = i, i = i.Parent)
932 private int graft_forward (MInterval interval, int start, int end)
936 if (! Stack.IsEmpty && isRearSticky)
938 else if (interval == null)
939 len = Stack.IsEmpty ? end - start : 0;
942 MInterval i = interval.find (start);
944 if (i.To == start && i.Right != null)
947 while (i != null && mergeable (i))
949 M17n.DebugPrint (" grafting "); i.DumpOne (false, false);
950 len += i.To - i.From;
952 len -= start - i.From;
962 M17n.DebugPrint (" grafted {0} in ", len); DumpOne (false, true);
968 private int graft_backward (MInterval interval, int start, int end)
972 if (! Stack.IsEmpty && isFrontSticky)
974 else if (interval == null)
975 len = Stack.IsEmpty ? end - start : 0;
978 MInterval i = interval.find (end);
980 if (i.From == end && i.Left != null)
983 while (i != null && mergeable (i))
985 M17n.DebugPrint (" grafting "); i.DumpOne (false, false);
986 len += i.To - i.From;
991 len -= start - i.From;
998 M17n.DebugPrint (" grafted {0} in ", len); DumpOne (false, true);
1004 public void Insert (int pos, MInterval interval, int start, int end)
1008 M17n.DebugPrint ("insert({0} to {1}) at {2} in ", start, end, pos);
1009 DumpOne (false, false);
1012 Left.Insert (pos, interval, start, end);
1013 else if (pos == From)
1017 MInterval prev = Prev;
1019 if (prev.isSensitive && prev.isRearSticky)
1020 prev.Stack.Clear ();
1021 start += prev.graft_forward (interval, start, end);
1023 if (isSensitive && isFrontSticky)
1027 end -= graft_backward (interval, start, end);
1030 if (interval != null)
1031 interval = interval.Copy (mtext, start, end);
1033 interval = new MInterval (Key, mtext, end - start, null);
1038 // .-this-. ==> .-this-.
1050 interval.Parent = i;
1051 for (; i != null; i = i.Parent)
1052 i.Length += interval.Length;
1061 int len = graft_forward (interval, start, end);
1066 end -= graft_backward (interval, start, end);
1069 if (interval != null)
1070 interval = interval.Copy (mtext, start, end);
1072 interval = new MInterval (Key, mtext, end - start, null);
1075 Right.Left = interval;
1076 interval.Parent = Right;
1077 for (MInterval i = Right; i != null; i = i.Parent)
1078 i.Length += interval.Length;
1086 MInterval next = Next;
1088 if (next.isSensitive && next.isFrontSticky)
1089 next.Stack.Clear ();
1090 end -= next.graft_backward (interval, start, end);
1092 if (isSensitive && isRearSticky)
1096 start += graft_forward (interval, start, end);
1099 if (interval != null)
1100 interval = interval.Copy (mtext, start, end);
1102 interval = new MInterval (Key, mtext, end - start, null);
1107 // .-this-. ==> .-this-.
1120 interval.Parent = i;
1121 for (; i != null; i = i.Parent)
1122 i.Length += interval.Length;
1127 Next.Insert (pos, interval, start, end);
1128 M17n.DebugPrint (" done\n");
1131 private void vacate_node (MInterval interval)
1133 vacate_node (interval, null);
1136 private void vacate_node (MInterval interval, MInterval stop)
1138 if (interval != null)
1139 M17n.DebugPrint ("vacate #{0} to #{1}\n", ID, interval.ID);
1141 M17n.DebugPrint ("vacate #{0} to null\n", ID);
1142 if (interval != null)
1143 interval.Parent = Parent;
1147 mtext.intervals.Put (Key, interval);
1151 if (this == Parent.Right)
1152 Parent.Right = interval;
1154 Parent.Left = interval;
1157 if (interval != null)
1158 diff -= interval.Length;
1159 for (MInterval i = Parent; i != stop; i = i.Parent)
1164 public void Delete (int start, int end)
1167 M17n.DebugPrint ("delete({0} {1}) from ", start, end); DumpOne (false, true);
1172 Left.Delete (start, end);
1175 Left.Delete (start, From);
1177 end -= From - start;
1184 Right.Delete (start, end);
1187 Right.Delete (To, end);
1190 if (start == From && end == To)
1202 for (i = Right; i.Left != null; i = i.Left)
1203 i.Length += Left.Length;
1204 i.Length += Left.Length;
1208 vacate_node (Right);
1213 int len = end - start;
1215 for (MInterval i = this; i != null; i = i.Parent)
1220 public void Push (int start, int end, MTextProperty prop)
1223 M17n.DebugPrint ("push({0} {1}) at ", start, end); DumpOne (false, true);
1228 Left.Push (start, end, prop);
1231 Left.Push (start, From, prop);
1238 Right.Push (start, end, prop);
1241 Right.Push (To, end, prop);
1246 divide_left (start);
1249 Stack.Push (prop.key, prop);
1252 /// Combine intervals between HEAD and TAIL (both inclusive) to
1253 /// the common parent of HEAD and TAIL while assuming that the
1254 /// intervals are mergeable.
1255 private static void combine (MInterval head, MInterval tail)
1257 M17n.DebugPrint ("merging "); head.DumpOne (true, false);
1258 M17n.DebugPrint (" through "); tail.DumpOne (true, false);
1260 int from = head.From;
1262 // The nearest common parent of HEAD and TAIL.
1265 for (root = head; root.To + root.RightLength < to;
1266 root = root.Parent);
1268 M17n.DebugPrint (" common root is "); root.DumpOne (false, true);
1270 if (from < root.From)
1272 MInterval prev = root.Prev;
1276 M17n.DebugPrint ("merging "); prev.DumpOne (false, true);
1277 prev.vacate_node (prev.Left, root);
1280 if (prev.Left != null)
1281 prev = prev.Left.RightMost;
1288 MInterval next = root.Next;
1292 M17n.DebugPrint ("merging "); next.DumpOne (false, true);
1293 next.vacate_node (next.Right, root);
1296 if (next.Right != null)
1297 next = next.Right.LeftMost;
1304 public void MergeAfterChange (int start, int end)
1309 Prev.MergeAfterChange (start, end);
1313 MInterval head = this, tail = this, i;
1315 if (start == From && start > 0)
1321 while (tail.To < end)
1324 if (! tail.mergeable (i))
1327 combine (head, tail);
1335 if (i == null || ! tail.mergeable (i))
1340 combine (head, tail);
1343 public void Pop (int start, int end)
1346 M17n.DebugPrint ("pop({0} {1}) at ", start, end); DumpOne (false, true);
1351 Left.Pop (start, end);
1354 Left.Pop (start, From);
1361 Right.Pop (start, end);
1364 Right.Pop (To, end);
1368 if (! Stack.IsEmpty)
1371 divide_left (start);
1378 private void DumpOne (bool with_prop, bool newline)
1380 DumpOne (with_prop, newline, false);
1383 private void DumpOne (bool with_prop, bool newline, bool force)
1385 if (force || M17n.debug)
1387 Console.Write ("#{0}({1} {2} {3}", ID, Length, From, To);
1389 foreach (MPlist p in Stack)
1390 Console.Write (" " + p.Val);
1391 Console.Write (")");
1393 Console.WriteLine ();
1395 throw new Exception ("Invalid interval length");
1399 public void Dump () { Dump (false); }
1401 public void Dump (bool force)
1403 if (force || M17n.debug)
1410 Console.Write (" ");
1411 DumpOne (true, false, force);
1418 get { return (Parent == null ? 0 : Parent.Depth + 1); }
1421 public void DumpNested (bool force)
1423 DumpNested (Key.ToString () + ":", force);
1426 public void DumpNested (string indent, bool force)
1428 if (force || M17n.debug)
1430 int indent_type = (Parent == null ? 1
1431 : Parent.Left == this ? 0 : 2);
1436 if (indent_type <= 1)
1437 Left.DumpNested (indent + " ", force);
1439 Left.DumpNested (indent + "| ", force);
1441 Console.Write (indent);
1442 if (indent_type == 0)
1443 Console.Write (".-");
1444 else if (indent_type == 2)
1445 Console.Write ("`-");
1446 DumpOne (true, true, true);
1449 if (indent_type >= 1)
1450 Right.DumpNested (indent + " ", force);
1452 Right.DumpNested (indent + "| ", force);
1458 private class MTextEnum : IEnumerator
1461 private int pos = -1;
1463 public MTextEnum (MText mt)
1468 public bool MoveNext ()
1471 return (pos < mt.nchars);
1474 public void Reset ()
1479 public object Current
1482 //if (pos < 0 || pos >= mt.nchars)
1483 //throw new InvalidOperationException ();