using System; using System.Linq; using System.Text; namespace UniJSON { public static class JsonString { #region Quote public static void Escape(String s, IStore w) { if (String.IsNullOrEmpty(s)) { return; } var it = s.ToCharArray().Cast().GetEnumerator(); while (it.MoveNext()) { switch (it.Current) { case '"': case '\\': case '/': // \\ prefix w.Write('\\'); w.Write(it.Current); break; case '\b': w.Write('\\'); w.Write('b'); break; case '\f': w.Write('\\'); w.Write('f'); break; case '\n': w.Write('\\'); w.Write('n'); break; case '\r': w.Write('\\'); w.Write('r'); break; case '\t': w.Write('\\'); w.Write('t'); break; default: w.Write(it.Current); break; } } } public static void Escape(Utf8String s, IStore w) { if (s.IsEmpty) { return; } var it = s.GetIterator(); while (it.MoveNext()) { var l = it.CurrentByteLength; if (l == 1) { var b = it.Current; switch (b) { case (Byte)'"': case (Byte)'\\': case (Byte)'/': // \\ prefix w.Write((Byte)'\\'); w.Write(b); break; case (Byte)'\b': w.Write((Byte)'\\'); w.Write((Byte)'b'); break; case (Byte)'\f': w.Write((Byte)'\\'); w.Write((Byte)'f'); break; case (Byte)'\n': w.Write((Byte)'\\'); w.Write((Byte)'n'); break; case (Byte)'\r': w.Write((Byte)'\\'); w.Write((Byte)'r'); break; case (Byte)'\t': w.Write((Byte)'\\'); w.Write((Byte)'t'); break; default: w.Write(b); break; } // ascii } else if (l == 2) { w.Write(it.Current); w.Write(it.Second); } else if (l == 3) { w.Write(it.Current); w.Write(it.Second); w.Write(it.Third); } else if (l == 4) { w.Write(it.Current); w.Write(it.Second); w.Write(it.Third); w.Write(it.Fourth); } else { throw new ParserException("invalid utf8"); } } } public static string Escape(String s) { var sb = new StringBuilder(); Escape(s, new StringBuilderStore(sb)); return sb.ToString(); } public static void Quote(String s, IStore w) { w.Write('"'); Escape(s, w); w.Write('"'); } public static void Quote(Utf8String s, IStore w) { w.Write((Byte)'"'); Escape(s, w); w.Write((Byte)'"'); } /// /// Added " and Escape /// /// /// public static string Quote(string s) { var sb = new StringBuilder(); Quote(s, new StringBuilderStore(sb)); return sb.ToString(); } public static Utf8String Quote(Utf8String s) { var sb = new BytesStore(s.ByteLength); Quote(s, sb); return new Utf8String(sb.Bytes); } #endregion #region Unquote static byte CheckHex(int b) { switch ((char)b) { case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; case 'A': case 'a': return 10; case 'B': case 'b': return 11; case 'C': case 'c': return 12; case 'D': case 'd': return 13; case 'E': case 'e': return 14; case 'F': case 'f': return 15; } throw new ArgumentOutOfRangeException(); } public static int Unescape(string src, IStore w) { int writeCount = 0; Action Write = c => { if (w != null) { w.Write(c); } ++writeCount; }; int i = 0; int length = src.Length - 1; while (i < length) { if (src[i] == '\\') { var c = src[i + 1]; switch (c) { case '\\': case '/': case '"': // remove prefix Write(c); i += 2; continue; case 'b': Write('\b'); i += 2; continue; case 'f': Write('\f'); i += 2; continue; case 'n': Write('\n'); i += 2; continue; case 'r': Write('\r'); i += 2; continue; case 't': Write('\t'); i += 2; continue; case 'u': { var u0 = CheckHex(src[i + 2]); var u1 = CheckHex(src[i + 3]); var u2 = CheckHex(src[i + 4]); var u3 = CheckHex(src[i + 5]); var u = (u0 << 12) + (u1 << 8) + (u2 << 4) + u3; Write((char)u); i += 6; } continue; } } Write(src[i]); i += 1; } while (i <= length) { Write(src[i++]); } return writeCount; } public static int Unescape(Utf8String s, IStore w) { int writeCount = 0; Action Write = c => { if (w != null) { w.Write(c); } ++writeCount; }; var it = s.GetIterator(); while (it.MoveNext()) { var l = it.CurrentByteLength; if (l == 1) { if (it.Current == (Byte)'\\') { var c = it.Second; switch (c) { case (Byte)'\\': case (Byte)'/': case (Byte)'"': // remove prefix Write(c); it.MoveNext(); continue; case (Byte)'b': Write((Byte)'\b'); it.MoveNext(); continue; case (Byte)'f': Write((Byte)'\f'); it.MoveNext(); continue; case (Byte)'n': Write((Byte)'\n'); it.MoveNext(); continue; case (Byte)'r': Write((Byte)'\r'); it.MoveNext(); continue; case (Byte)'t': Write((Byte)'\t'); it.MoveNext(); continue; case (Byte)'u': { // skip back slash it.MoveNext(); // skip u it.MoveNext(); var u0 = CheckHex((char)it.Current); it.MoveNext(); var u1 = CheckHex((char)it.Current); it.MoveNext(); var u2 = CheckHex((char)it.Current); it.MoveNext(); var u3 = CheckHex((char)it.Current); var u = (u0 << 12) + (u1 << 8) + (u2 << 4) + u3; var utf8 = Utf8String.From(new string(new char[] { (char)u })); // var utf8 = Utf8String.From((int)u); foreach (var x in utf8.Bytes) { Write(x); } } continue; } } Write(it.Current); } else if (l == 2) { Write(it.Current); Write(it.Second); } else if (l == 3) { Write(it.Current); Write(it.Second); Write(it.Third); } else if (l == 4) { Write(it.Current); Write(it.Second); Write(it.Third); Write(it.Fourth); } else { throw new ParserException("invalid utf8"); } } return writeCount; } public static string Unescape(string src) { var sb = new StringBuilder(); Unescape(src, new StringBuilderStore(sb)); return sb.ToString(); } public static int Unquote(string src, IStore w) { return Unescape(src.Substring(1, src.Length - 2), w); } public static int Unquote(Utf8String src, IStore w) { return Unescape(src.Subbytes(1, src.ByteLength - 2), w); } public static string Unquote(string src) { var count = Unquote(src, null); if (count == src.Length - 2) { return src.Substring(1, src.Length - 2); } else { var sb = new StringBuilder(count); Unquote(src, new StringBuilderStore(sb)); var str = sb.ToString(); return str; } } public static Utf8String Unquote(Utf8String src) { var count = Unquote(src, null); if (count == src.ByteLength - 2) { return src.Subbytes(1, src.ByteLength - 2); } else { var sb = new BytesStore(count); Unquote(src, sb); return new Utf8String(sb.Bytes); } } #endregion } }