using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
/*
* This file has been copied from unity/trunk Tools/Bee/Bee.Tools/SimpleJsonReader.cs
* Changes made:
* - moved from Bee.Tools name space to ValidationSuite.Utilities
* - changed the SimpleJsonReader visibility from public to internal
*/
namespace UnityEditor.PackageManager.ValidationSuite
{
// Using specialized simple JSON reader for Tundra build JSON file, since we need as much performance as we can get.
// It can also "fast-skip" parts of JSON file we're not interested in, via keyFilter argument.
internal static class SimpleJsonReader
{
static readonly char[] Escapes = new char[128];
static SimpleJsonReader()
{
// Valid single-character escapes
Escapes['"'] = '"';
Escapes['\\'] = '\\';
Escapes['/'] = '/';
Escapes['b'] = '\b';
Escapes['f'] = '\f'; // per RFC 8259
Escapes['n'] = '\n';
Escapes['r'] = '\r';
Escapes['t'] = '\t';
}
///
/// Reads an object from JSON:
/// - Objects {...} are returned as Dictionary of string,object
/// - Arrays [...] are returned as List of object
/// - Strings "..." are returned as string
/// - Numbers are returned as either int or double
/// - "true", "false" and "null" are returned as themselves
///
/// JSON string
/// When non-null, only adds Object keys that pass the key filter to the result.
/// Object read from JSON, or null on invalid JSON.
public static object Read(string json, Func keyFilter = null)
{
var idx = ParseValue(json, 0, keyFilter, out var value, false);
if (idx == -1)
return null;
SkipSpace(json, ref idx, json.Length);
if (idx != json.Length)
return null;
return value;
}
public static Dictionary ReadObject(string json, Func keyFilter = null)
{
return Read(json, keyFilter) as Dictionary;
}
static int SkipUntilStringEnd(string json, int beginIdx, int len)
{
for (var i = beginIdx + 1; i < len; i++)
{
if (json[i] == '\\')
{
i++; // Skip next character as it is escaped
}
else if (json[i] == '"')
{
return i + 1;
}
}
return -1;
}
static int CharToHex(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static string DecodeString(string json, int beginIdx, int endIdx)
{
StringBuilder sb = new StringBuilder(endIdx - beginIdx);
for (var i = beginIdx + 1; i < endIdx - 1; ++i)
{
var c = json[i];
if (c == '\\')
{
// handle JSON escapes
++i;
if (i >= endIdx - 1)
return null;
c = json[i];
if (c >= 128)
return null;
if (c == 'u')
{
// Unicode escape in form of "\uHHHH"
if (i + 4 >= endIdx - 1)
return null;
var h0 = CharToHex(json[i + 1]);
var h1 = CharToHex(json[i + 2]);
var h2 = CharToHex(json[i + 3]);
var h3 = CharToHex(json[i + 4]);
var h = (h0 << 12) | (h1 << 8) | (h2 << 4) | h3;
if (h < 0)
return null;
c = (char)h;
i += 4;
}
else
{
// Simple escape, e.g. "\n"
c = Escapes[c];
if (c == 0) return null; // not a valid escape
}
}
sb.Append(c);
}
return sb.ToString();
}
static void SkipSpace(string json, ref int idx, int len)
{
while (idx < len)
{
var c = json[idx];
if (c != '\x20' && c != '\n' && c != '\r' && c != '\t') break;
++idx;
}
}
static int ParseDict(string json, int idx, Dictionary res, Func keyFilter, bool skip)
{
var length = json.Length;
SkipSpace(json, ref idx, length);
// empty?
if (idx < length && json[idx] == '}')
return idx + 1;
while (idx < length)
{
// key name
string name = null;
if (json[idx] == '"')
{
var endS = SkipUntilStringEnd(json, idx, length);
if (endS == length || endS == -1)
return -1;
if (!skip)
{
name = DecodeString(json, idx, endS);
if (name == null)
return -1;
}
idx = endS;
}
else
return -1;
SkipSpace(json, ref idx, length);
// :
if (idx >= length || json[idx] != ':')
return -1;
++idx;
SkipSpace(json, ref idx, length);
// value
var skipThisValue = skip || (keyFilter != null && !keyFilter(name));
var endO = ParseValue(json, idx, keyFilter, out object val, skipThisValue);
if (endO == -1)
return -1;
if (!skipThisValue)
res.Add(name, val);
idx = endO;
// next should be either , or }
SkipSpace(json, ref idx, length);
if (idx >= length)
return -1;
if (json[idx] == '}')
return idx + 1;
if (json[idx] != ',')
return -1;
++idx;
SkipSpace(json, ref idx, length);
// Skip trailing commas
if (idx < length && json[idx] == '}')
{
return idx + 1;
}
}
return -1;
}
static int ParseList(string json, int idx, List