void FillSurrogateSortKeyRaw (int i, SortKeyBuffer buf)
		{
			int diffbase = 0;
			int segment = 0;
			byte lower = 0;

			if (i < 0xD840) {
				diffbase = 0xD800;
				segment = 0x41;
				lower = (byte) ((i == 0xD800) ? 0x3E : 0x3F);
			} else if (0xD840 <= i && i < 0xD880) {
				diffbase = 0xD840;
				segment = 0xF2;
				lower = 0x3E;
			} else if (0xDB80 <= i && i < 0xDC00) {
				diffbase = 0xDB80 - 0x40;
				segment = 0xFE;
				lower = 0x3E;
			} else {
				diffbase = 0xDC00 - 0xF8 + 2;
				segment = 0x41;
				lower = 0x3F;
			}
			int diff = i - diffbase;

			buf.AppendNormal (
				(byte) (segment + diff / 254),
				(byte) (diff % 254 + 2),
				lower,
				lower);
		}
		unsafe void GetSortKey (string s, int start, int end,
			SortKeyBuffer buf, CompareOptions opt)
		{
			byte* prevbuf = stackalloc byte [4];
			ClearBuffer (prevbuf, 4);
			Context ctx = new Context (opt, null, null, null, null, prevbuf, false);

			for (int n = start; n < end; n++) {
				int i = s [n];

				ExtenderType ext = GetExtenderType (i);
				if (ext != ExtenderType.None) {
					i = FilterExtender (ctx.PrevCode, ext, opt);
					if (i >= 0)
						FillSortKeyRaw (i, ext, buf, opt);
					else if (ctx.PrevSortKey != null) {
						byte* b = ctx.PrevSortKey;
						buf.AppendNormal (
							b [0],
							b [1],
							b [2] != 1 ? b [2] : Level2 (i, ext),
							b [3] != 1 ? b [3] : Uni.Level3 (i));
					}
					// otherwise do nothing.
					// (if the extender is the first char
					// in the string, then just ignore.)
					continue;
				}

				if (IsIgnorable (i, opt))
					continue;
				i = FilterOptions (i, opt);

				Contraction ct = GetContraction (s, n, end);
				if (ct != null) {
					if (ct.Replacement != null) {
						GetSortKey (ct.Replacement, 0, ct.Replacement.Length, buf, opt);
					} else {
						byte* b = ctx.PrevSortKey;
						for (int bi = 0; bi < ct.SortKey.Length; bi++)
							b [bi] = ct.SortKey [bi];
						buf.AppendNormal (
							b [0],
							b [1],
							b [2] != 1 ? b [2] : Level2 (i, ext),
							b [3] != 1 ? b [3] : Uni.Level3 (i));
						ctx.PrevCode = -1;
					}
					n += ct.Source.Length - 1;
				}
				else {
					if (!Uni.IsIgnorableNonSpacing (i))
						ctx.PrevCode = i;
					FillSortKeyRaw (i, ExtenderType.None, buf, opt);
				}
			}
		}
		void FillSortKeyRaw (int i, ExtenderType ext,
			SortKeyBuffer buf, CompareOptions opt)
		{
			if (0x3400 <= i && i <= 0x4DB5) {
				int diff = i - 0x3400;
				buf.AppendCJKExtension (
					(byte) (0x10 + diff / 254),
					(byte) (diff % 254 + 2));
				return;
			}

			UnicodeCategory uc = char.GetUnicodeCategory ((char) i);
			switch (uc) {
			case UnicodeCategory.PrivateUse:
				int diff = i - 0xE000;
				buf.AppendNormal (
					(byte) (0xE5 + diff / 254),
					(byte) (diff % 254 + 2),
					0,
					0);
				return;
			case UnicodeCategory.Surrogate:
				FillSurrogateSortKeyRaw (i, buf);
				return;
			}

			byte level2 = Level2 (i, ext);
			if (Uni.HasSpecialWeight ((char) i)) {
				byte level1 = Level1 (i);
				buf.AppendKana (
					Category (i),
					level1,
					level2,
					Uni.Level3 (i),
					Uni.IsJapaneseSmallLetter ((char) i),
					ToDashTypeValue (ext, opt),
					!Uni.IsHiragana ((char) i),
					IsHalfKana ((char) i, opt)
					);
				if ((opt & COpt.IgnoreNonSpace) == 0 && ext == ExtenderType.Voiced)
					// Append voice weight
					buf.AppendNormal (1, 1, 1, 0);
			}
			else
				buf.AppendNormal (
					Category (i),
					Level1 (i),
					level2,
					Uni.Level3 (i));
		}
		public SortKey GetSortKey (string s, int start, int length, CompareOptions options)
		{
			SortKeyBuffer buf = new SortKeyBuffer (lcid);
			buf.Initialize (options, lcid, s, frenchSort);
			int end = start + length;
			GetSortKey (s, start, end, buf, options);
			return buf.GetResultAndReset ();
		}