448 lines
14 KiB
C#
448 lines
14 KiB
C#
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<char>().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)'"');
|
|
}
|
|
|
|
/// <summary>
|
|
/// Added " and Escape
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <param name="w"></param>
|
|
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<Char> 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<Byte> 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
|
|
}
|
|
}
|