1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00
Beef/BeefLibs/Beefy2D/src/utils/StructuredData.bf
2019-09-18 13:02:01 -07:00

2552 lines
60 KiB
Beef

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Diagnostics;
using System.IO;
namespace Beefy.utils
{
public class StructuredData : ILeakIdentifiable
{
public enum Error
{
case FileError;
case FormatError(int line);
case ParseError;
case ColonNotExpected;
case KeyInArray;
case KeyRequiredForVal;
case ValueExpected;
case PrecedingCommaExpected;
case UnexpectedObjectEnd;
case ExpectedArrayNameEnd;
/*public override void ToString(String str)
{
switch (this)
{
case FormatError: str.Append("Format error");
case ParseError: str.Append("Parse error");
case ColonNotExpected: str.Append("Colon not expected");
case KeyInArray: str.Append("Cannot add key/val to array");
case KeyRequiredForVal: str.Append("Key required for value");
case ValueExpected: str.Append("Value expected");
case PrecedingCommaExpected: str.Append("Preceding comma expected");
case UnexpectedObjectEnd: str.Append("Unexpected object end");
}
}*/
}
public struct Enumerator : IEnumerator<StringView>
{
StructuredData mStructuredData;
bool mIsFirst = true;
public this(StructuredData data)
{
mStructuredData = data;
}
public StringView Current
{
get
{
if (mStructuredData.mCurrent.mLastKey != -1)
return mStructuredData.mKeys[mStructuredData.mCurrent.mLastKey];
return StringView();
}
}
public bool MoveNext() mut
{
var current = ref mStructuredData.mCurrent;
if (current.mLastValue == -1)
return false;
if (mIsFirst)
{
//mStructuredData.mOpenStack.Add(mStructuredData.mCurrent);
//mStructuredData.mCurrent = CurrentEntry(mValues);
mIsFirst = false;
return true;
}
if (current.mLastKey != -1)
current.mLastKey = mStructuredData.mNextKeys[current.mLastKey];
current.mLastValue = mStructuredData.mNextValues[current.mLastValue];
return current.mLastValue != -1;
}
public void Dispose()
{
//if (mIsOpen)
{
mStructuredData.Close();
}
}
public Result<StringView> GetNext() mut
{
if (!MoveNext())
return .Err;
return Current;
}
}
public class Values
{
public int32 mValueIdx = -1;
public virtual bool IsArray
{
get
{
return true;
}
}
public virtual bool IsObject
{
get
{
return false;
}
}
public virtual bool ForceInline
{
get
{
return false;
}
}
}
public class InlineValues : Values
{
public override bool ForceInline
{
get
{
return true;
}
}
}
public class NamedValues : Values
{
public int32 mKeyIdx = -1;
public override bool IsArray
{
get
{
return false;
}
}
public override bool IsObject
{
get
{
return true;
}
}
}
public class InlineNamedValues : NamedValues
{
public override bool ForceInline
{
get
{
return true;
}
}
}
struct CurrentEntry
{
public Values mValues;
public int32 mLastValue = -1;
public int32 mLastKey = -1;
public this(Values values)
{
mValues = values;
}
public bool HasValues
{
get
{
return mLastValue != -1;
}
}
}
static Object sEmptySentinel = new Object() ~ delete _;
BumpAllocator mBumpAllocator = CreateAllocator() ~ delete _;
String mSource;
List<CurrentEntry> mOpenStack = new:mBumpAllocator List<CurrentEntry>() ~ delete:mBumpAllocator _;
Values mHeadValues;
CurrentEntry mCurrent = .(null);
NamedValues mEmptyData;
List<Object> mValues ~
{
/*for (var item in _)
if (item != sEmptySentinel)
delete item;*/
delete:mBumpAllocator _;
};
List<StringView> mKeys ~ delete:mBumpAllocator _;
List<int32> mNextValues ~ delete:mBumpAllocator _;
List<int32> mNextKeys ~ delete:mBumpAllocator _;
Dictionary<String, Object> mMap ~ delete:mBumpAllocator _;
DisposeProxy mStructuredDisposeProxy ~ delete _;
bool mCanWrite = true;
bool mCanRead = true;
public this()
{
}
public ~this()
{
}
protected virtual BumpAllocator CreateAllocator()
{
var bumpAlloc = new BumpAllocator();
bumpAlloc.DestructorHandling = .Ignore;
return bumpAlloc;
}
/*public ListRef<String> Keys
{
get
{
if (var namedValues = mCurrent as NamedValues)
{
return ListRef<String>(mKeys, namedValues.mKeyIdx, namedValues.mCount);
}
return ListRef<String>(null, 0, 0);
}
}
public ListRef<Object> Values
{
get
{
return ListRef<Object>(mValues, mCurrent.mValueIdx, mCurrent.mCount);
}
}
public int Count
{
get { return mCurrent.mCount; }
}*/
public bool IsArray
{
get { return GetCurrent().GetType() == typeof(Values); }
}
public bool IsObject
{
get { return GetCurrent().GetType() == typeof(NamedValues); }
}
public int GetValueCount()
{
int count = 0;
int32 checkValue = mCurrent.mValues.mValueIdx;
while (checkValue != -1)
{
if (mValues[checkValue] != sEmptySentinel)
count++;
checkValue = mNextValues[checkValue];
}
return count;
}
/*public Values Current
{
get { return mCurrent; }
}*/
/*protected override void GCMarkMembers()
{
// We do this because these containers are held in the bump allocator so they don't get marked
GC.Mark(mKeys.[Friend]mItems);
GC.Mark(mNextValues.[Friend]mItems);
GC.Mark(mNextKeys.[Friend]mItems);
if (mMap != null)
{
GC.Mark(mMap.[Friend]mBuckets);
GC.Mark(mMap.[Friend]mEntries);
}
}*/
static int32 sIdx;
int32 mIdx = sIdx++;
public void ToLeakString(String str)
{
str.AppendF("Idx:{0}", mIdx);
}
public bool TryGet(String name, out Object value)
{
var current = GetCurrent();
if ((current == null) || (current.GetType() != typeof(NamedValues)))
{
value = null;
return false;
}
let namedValues = (NamedValues)current;
/*if (mMap != null)
return mMap.TryGetValue(name, out value);
if (mKeys.Count > 32)
{
mMap = new Dictionary<String, Object>();
// Only create a map for large lists
for (int32 i = 0; i < mKeys.Count; i++)
mMap[mKeys[i]] = mValues[i];
return mMap.TryGetValue(name, out value);
}*/
int32 checkKey = namedValues.mKeyIdx;
int32 checkValue = namedValues.mValueIdx;
while (checkKey != -1)
{
if (mKeys[checkKey] == name)
{
value = mValues[checkValue];
return true;
}
checkValue = mNextValues[checkValue];
checkKey = mNextKeys[checkKey];
}
value = null;
return false;
}
public bool TryGet(String name, out NamedValues namedValues, out int keyIdx, out int valueIdx)
{
var current = GetCurrent();
if ((current == null) || (current.GetType() != typeof(NamedValues)))
{
//value = null;
namedValues = null;
valueIdx = -1;
keyIdx = -1;
return false;
}
namedValues = (NamedValues)current;
/*if (mMap != null)
return mMap.TryGetValue(name, out value);
if (mKeys.Count > 32)
{
mMap = new Dictionary<String, Object>();
// Only create a map for large lists
for (int32 i = 0; i < mKeys.Count; i++)
mMap[mKeys[i]] = mValues[i];
return mMap.TryGetValue(name, out value);
}*/
int32 checkKey = namedValues.mKeyIdx;
int32 checkValue = namedValues.mValueIdx;
while (checkKey != -1)
{
if (mKeys[checkKey] == name)
{
valueIdx = checkValue;
keyIdx = checkKey;
return true;
}
checkValue = mNextValues[checkValue];
checkKey = mNextKeys[checkKey];
}
namedValues = null;
valueIdx = -1;
keyIdx = -1;
return false;
}
public bool Contains(String name)
{
Object value;
return TryGet(name, out value);
}
public Object Get(String name)
{
Object value;
TryGet(name, out value);
return value;
}
public bool Get(String name, ref bool val)
{
Object aVal = Get(name);
if ((aVal == null) || (!(aVal is bool)))
return false;
val = (bool)aVal;
return true;
}
public void Get(String name, ref int32 val)
{
Object obj = Get(name);
if (obj == null)
return;
switch (obj.GetType())
{
case typeof(Int32): val = (int32)obj;
default:
}
}
public void Get(String name, ref float val)
{
Object obj = Get(name);
if (obj == null)
return;
switch (obj.GetType())
{
case typeof(Int32): val = (int32)obj;
case typeof(Float): val = (float)obj;
default:
}
}
public void Get<T>(String name, ref T val) where T : Enum
{
Object obj = Get(name);
if (obj == null)
return;
Result<T> result;
if (let str = obj as String)
result = Enum.Parse<T>(str);
else if (obj is StringView)
result = Enum.Parse<T>((StringView)obj);
else
return;
if (result case .Ok(var parsedVal))
val = parsedVal;
}
public void Get(String name, String outString)
{
Object obj = Get(name);
if (obj == null)
return;
outString.Clear();
if (obj is uint64)
obj.ToString(outString);
if (obj != null)
{
var type = obj.GetType();
if (type == typeof(StringView))
outString.Append((StringView)obj);
else if (type == typeof(String))
outString.Append((String)obj);
}
}
public Object GetCurrent()
{
/*int32 checkValue = mCurrent.mValues.mValueIdx;
for (int i < idx)
{
checkValue = mNextValues[checkValue];
}
return mValues[checkValue];*/
if (mCurrent.mLastValue == -1)
return null;
return mValues[mCurrent.mLastValue];
}
public void GetString(String name, String outString, String theDefault = null)
{
Object val = Get(name);
outString.Clear();
if (val is uint64)
val.ToString(outString);
if (val != null)
{
var type = val.GetType();
if (type == typeof(StringView))
{
outString.Append((StringView)val);
return;
}
if (type == typeof(String))
{
outString.Append((String)val);
return;
}
}
if (theDefault != null)
outString.Append(theDefault);
return;
}
public int32 GetInt(String name, int32 theDefault = 0)
{
Object aVal = Get(name);
if ((aVal == null) || (!(aVal is int32)))
return theDefault;
return (int32)aVal;
}
public int64 GetLong(String name, int64 theDefault = 0)
{
Object aVal = Get(name);
if (aVal is int32)
return (int64)(int32)aVal;
if ((aVal == null) || (!(aVal is int64)))
return theDefault;
return (int64)aVal;
}
public uint64 GetULong(String name, uint64 theDefault = 0)
{
Object aVal = Get(name);
if (aVal is int32)
return (uint64)(int32)aVal;
if ((aVal == null) || (!(aVal is uint64)))
return theDefault;
return (uint64)aVal;
}
public float GetFloat(String name, float theDefault = 0)
{
Object val = Get(name);
if (val == null)
return theDefault;
switch (val.GetType())
{
case typeof(Float): return (float)val;
case typeof(Int32): return (int32)val;
case typeof(Int): return (int)val;
default: return theDefault;
}
}
public bool GetBool(String name, bool theDefault = false)
{
Object aVal = Get(name);
if ((aVal == null) || (!(aVal is bool)))
return theDefault;
return (bool)aVal;
}
public T GetEnum<T>(String name, T defaultVal = default(T)) where T : Enum
{
Object obj = Get(name);
if (obj == null)
return defaultVal;
Result<T> result;
if (let str = obj as String)
result = Enum.Parse<T>(str);
else if (obj is StringView)
result = Enum.Parse<T>((StringView)obj);
else
return defaultVal;
if (result case .Ok(var val))
return val;
return defaultVal;
}
public bool GetEnum<T>(String name, ref T val) where T : Enum
{
Object obj = Get(name);
if (obj == null)
return false;
Result<T> result;
if (let str = obj as String)
result = Enum.Parse<T>(str);
else if (obj is StringView)
result = Enum.Parse<T>((StringView)obj);
else
return false;
if (result case .Ok(out val))
return true;
return false;
}
///
public void GetCurString(String outString, String theDefault = null)
{
Object val = GetCurrent();
outString.Clear();
if (val is uint64)
val.ToString(outString);
if (val != null)
{
var type = val.GetType();
if (type == typeof(StringView))
{
outString.Append((StringView)val);
return;
}
if (type == typeof(String))
{
outString.Append((String)val);
return;
}
}
if (theDefault != null)
outString.Append(theDefault);
return;
}
public int32 GetCurInt(int32 theDefault = 0)
{
Object aVal = GetCurrent();
if ((aVal == null) || (!(aVal is int32)))
return theDefault;
return (int32)aVal;
}
public uint32 GetCurUInt(uint32 theDefault = 0)
{
Object aVal = GetCurrent();
if ((aVal == null) || (!(aVal is uint32)))
return theDefault;
return (uint32)aVal;
}
public float GetCurFloat(float theDefault = 0)
{
Object aVal = GetCurrent();
if ((aVal == null) || (!(aVal is float)))
return theDefault;
return (float)aVal;
}
public bool GetCurBool(bool theDefault = false)
{
Object aVal = GetCurrent();
if ((aVal == null) || (!(aVal is bool)))
return theDefault;
return (bool)aVal;
}
///
public void DoAdd(ref CurrentEntry currentEntry, Object obj)
{
//Temporary type check?
Debug.Assert(IsValidWriteObject(obj));
Debug.Assert(mCanWrite);
Debug.Assert(!currentEntry.mValues.IsObject); // Can't be an object
Debug.Assert((currentEntry.mValues.mValueIdx == -1) == (currentEntry.mLastValue == -1));
EnsureHasData();
int valueIdx = mValues.Count;
if (currentEntry.mLastValue != -1)
mNextValues[currentEntry.mLastValue] = (int32)valueIdx;
else
currentEntry.mValues.mValueIdx = (int32)valueIdx;
currentEntry.mLastValue = (int32)valueIdx;
mValues.Add(obj);
mNextValues.Add(-1);
}
public void Add<T>(T value) where T : struct
{
DoAdd(ref mCurrent, new:mBumpAllocator box value);
}
public void Add(String value)
{
DoAdd(ref mCurrent, (Object)new:mBumpAllocator String(value));
}
public void Add<T>(String name, T value) where T : struct
{
DoAdd(ref mCurrent, AllocStringView(name), new:mBumpAllocator box value);
}
public void ConditionalAdd<T>(String name, T value) where T : var
{
if (value != default(T))
Add(name, value);
}
public void ConditionalAdd<T>(String name, T value, T defaultVal) where T : var
{
if ((value != null) && (value != defaultVal))
Add(name, value);
}
void EnsureHasData()
{
if (mValues == null)
{
mValues = new:mBumpAllocator ListWithAlloc<Object>(mBumpAllocator);
mKeys = new:mBumpAllocator ListWithAlloc<StringView>(mBumpAllocator);
mNextKeys = new:mBumpAllocator ListWithAlloc<int32>(mBumpAllocator);
mNextValues = new:mBumpAllocator ListWithAlloc<int32>(mBumpAllocator);
}
}
void DoAdd(ref CurrentEntry currentEntry, StringView name, Object value)
{
EnsureHasData();
//Temporary type check?
Debug.Assert(IsValidWriteObject(value));
Debug.Assert(mCanWrite);
Debug.Assert(!currentEntry.mValues.IsArray); // Can't be an array
Debug.Assert((currentEntry.mValues.mValueIdx == -1) == (currentEntry.mLastValue == -1));
var namedValues = currentEntry.mValues as NamedValues;
int valueIdx = mValues.Count;
int keyIdx = mKeys.Count;
if (currentEntry.mLastValue != -1)
mNextValues[currentEntry.mLastValue] = (int32)valueIdx;
else
currentEntry.mValues.mValueIdx = (int32)valueIdx;
if (currentEntry.mLastKey != -1)
mNextKeys[currentEntry.mLastKey] = (int32)keyIdx;
else
namedValues.mKeyIdx = (int32)keyIdx;
currentEntry.mLastValue = (int32)valueIdx;
currentEntry.mLastKey = (int32)keyIdx;
mKeys.Add(name);
mNextKeys.Add(-1);
mValues.Add(value);
mNextValues.Add(-1);
}
StringView AllocStringView(String str)
{
int len = str.Length;
char8* ptr = (char8*)mBumpAllocator.Alloc(len, 1);
Internal.MemCpy(ptr, str.Ptr, len);
return StringView(ptr, len);
}
public void Add(String name, String value)
{
DoAdd(ref mCurrent, AllocStringView(name), (Object)new:mBumpAllocator String(value));
}
public void ConditionalAdd(String name, String value, String defaultVal)
{
if (value != defaultVal)
DoAdd(ref mCurrent, AllocStringView(name), (Object)new:mBumpAllocator String(value));
}
public void ConditionalAdd(String name, String value)
{
if ((value != null) && (value != ""))
DoAdd(ref mCurrent, AllocStringView(name), (Object)new:mBumpAllocator String(value));
}
///
public void Close()
{
mCurrent = mOpenStack.PopBack();
}
public void RemoveIfEmpty()
{
bool hasValue = false;
if (mCurrent.mLastValue != -1)
{
if (mValues[mCurrent.mLastValue] != sEmptySentinel)
hasValue = true;
else
{
// We need a (potentially) full value pass if we have sEmptySentinel
int checkValue = mCurrent.mValues.mValueIdx;
while (checkValue != -1)
{
if (mValues[checkValue] != sEmptySentinel)
{
hasValue = true;
break;
}
checkValue = mNextValues[checkValue];
}
}
}
if (!hasValue)
{
var prev = ref mOpenStack.Back;
var lastVal = mValues[prev.mLastValue];
Debug.Assert(lastVal == mCurrent.mValues);
if (lastVal == mCurrent.mValues)
{
mValues[prev.mLastValue] = sEmptySentinel;
}
}
}
public IDisposable Open(String name)
{
if (mStructuredDisposeProxy == null)
{
mStructuredDisposeProxy = new DisposeProxy();
mStructuredDisposeProxy.mDisposeProxyDelegate = new => Close;
}
mOpenStack.Add(mCurrent);
NamedValues namedValues;
int keyIdx;
int valueIdx;
if (TryGet(name, out namedValues, out keyIdx, out valueIdx))
{
mCurrent = CurrentEntry(namedValues);
mCurrent.mLastKey = (int32)keyIdx;
mCurrent.mLastValue = (int32)valueIdx;
}
else
{
if (mEmptyData == null)
{
mEmptyData = new:mBumpAllocator NamedValues();
}
mCurrent = CurrentEntry(mEmptyData);
}
return mStructuredDisposeProxy;
}
public Enumerator Enumerate(String name)
{
Enumerator enumerator;
mOpenStack.Add(mCurrent);
Object val;
if ((TryGet(name, out val)) && (let values = val as Values))
{
mCurrent = CurrentEntry(values);
if (let namedValues = values as NamedValues)
mCurrent.mLastKey = namedValues.mKeyIdx;
mCurrent.mLastValue = values.mValueIdx;
enumerator = Enumerator(this);
}
else
{
mCurrent = CurrentEntry(null);
enumerator = Enumerator(this);
}
return enumerator;
}
public Enumerator Enumerate()
{
Enumerator enumerator;
mOpenStack.Add(mCurrent);
Object val = GetCurrent();
if (let values = val as Values)
{
mCurrent = CurrentEntry(values);
if (let namedValues = values as NamedValues)
mCurrent.mLastKey = namedValues.mKeyIdx;
mCurrent.mLastValue = values.mValueIdx;
enumerator = Enumerator(this);
}
else
{
mCurrent = CurrentEntry(null);
enumerator = Enumerator(this);
}
return enumerator;
}
/*IEnumerator IEnumerable.GetEnumerator()
{
return new Enumerator(this);
}*/
/*public Enumerator GetEnumerator()
{
return Enumerator(this, mCurrent.mValues);
}*/
/*public IEnumerable<StructuredData> GetValues(String name)
{
Open(name);
return this;
}*/
IDisposable GetDisposeProxy()
{
if (mStructuredDisposeProxy == null)
{
mStructuredDisposeProxy = new DisposeProxy();
mStructuredDisposeProxy.mDisposeProxyDelegate = new => Close;
}
return mStructuredDisposeProxy;
}
public IDisposable Open(int index)
{
ThrowUnimplemented();
/*if (mStructuredDisposeProxy == null)
{
mStructuredDisposeProxy = new DisposeProxy();
mStructuredDisposeProxy.mDisposeProxyDelegate = new => Close;
}
mOpenStack.Add(mCurrent);
Object val = Get(index);
if (let values = val as Values)
{
mCurrent = CurrentEntry(values);
}
else
{
if (mEmptyData == null)
{
mEmptyData = new NamedValues();
}
mCurrent = CurrentEntry(mEmptyData);
}
return mStructuredDisposeProxy;*/
}
public IDisposable Open(Enumerator enumerator)
{
ThrowUnimplemented();
/*if (mStructuredDisposeProxy == null)
{
mStructuredDisposeProxy = new DisposeProxy();
mStructuredDisposeProxy.mDisposeProxyDelegate = new => Close;
}
mOpenStack.Add(mCurrent);
Object val = mValues[enumerator.[Friend]mValueIdx];
if (let values = val as Values)
{
mCurrent = CurrentEntry(values);
}
else
{
if (mEmptyData == null)
{
mEmptyData = new NamedValues();
}
mCurrent = CurrentEntry(mEmptyData);
}
return mStructuredDisposeProxy;*/
}
public void CreateNew()
{
if (mCurrent.mValues == null)
{
NamedValues values = new:mBumpAllocator NamedValues();
mCurrent = CurrentEntry(values);
}
}
public IDisposable CreateArray()
{
Values values = new:mBumpAllocator Values();
DoAdd(ref mCurrent, values);
mOpenStack.Add(mCurrent);
mCurrent = CurrentEntry(values);
return GetDisposeProxy();
}
public IDisposable CreateObject(bool forceInline = false)
{
//NamedValues values = new:mBumpAllocator NamedValues();
NamedValues values;
if (forceInline)
values = new:mBumpAllocator InlineNamedValues();
else
values = new:mBumpAllocator NamedValues();
DoAdd(ref mCurrent, values);
mOpenStack.Add(mCurrent);
mCurrent = CurrentEntry(values);
return GetDisposeProxy();
}
public IDisposable CreateArray(String name, bool forceInline = false)
{
//Values values = new:mBumpAllocator Values();
Values values;
if (forceInline)
values = new:mBumpAllocator InlineValues();
else
values = new:mBumpAllocator Values();
DoAdd(ref mCurrent, AllocStringView(name), values);
mOpenStack.Add(mCurrent);
mCurrent = CurrentEntry(values);
return GetDisposeProxy();
}
public IDisposable CreateObject(String name, bool forceInline = false)
{
NamedValues values;
if (forceInline)
values = new:mBumpAllocator InlineNamedValues();
else
values = new:mBumpAllocator NamedValues();
DoAdd(ref mCurrent, AllocStringView(name), values);
mOpenStack.Add(mCurrent);
mCurrent = CurrentEntry(values);
return GetDisposeProxy();
}
public void ForceInline()
{
//mCurrent.mValues;
}
static void StringEncode(String outString, StringView theString)
{
outString.Append('"');
for (int32 i = 0; i < theString.Length; i++)
{
char8 c = theString.Ptr[i];
char8 slashC = '\0';
switch (c)
{
case '\b':
slashC = 'b';
break;
case '\f':
slashC = 'f';
break;
case '\n':
slashC = 'n';
break;
case '\r':
slashC = 'r';
break;
case '\t':
slashC = 't';
break;
case '"':
slashC = '"';
break;
case '\\':
slashC = '\\';
break;
}
if (slashC != '\0')
{
outString.Append('\\');
outString.Append(slashC);
}
else
outString.Append(c);
}
outString.Append('"');
}
static void StringDecode(String inString)
{
bool lastWasSlash = false;
char8* headPtr = inString.Ptr;
char8* strInPtr = headPtr;
char8* strOutPtr = headPtr;
char8* strInEndPtr = headPtr + inString.Length;
while (strInPtr < strInEndPtr)
{
char8 c = *(strInPtr++);
if (lastWasSlash)
{
switch (c)
{
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
}
lastWasSlash = false;
}
else if (c == '\\')
{
lastWasSlash = true;
continue;
}
*(strOutPtr++) = c;
}
inString.[Friend]mLength = (int32)(strOutPtr - headPtr);
}
bool IsValidWriteObject(Object theObject)
{
if (theObject == null)
return true;
var type = theObject.GetType();
switch (type)
{
case typeof(Boolean): return true;
case typeof(Int32): return true;
case typeof(UInt32): return true;
case typeof(Int64): return true;
case typeof(UInt64): return true;
case typeof(Float): return true;
case typeof(Double): return true;
case typeof(Int): return true;
case typeof(UInt): return true;
case typeof(Values): return true;
case typeof(InlineValues): return true;
case typeof(NamedValues): return true;
case typeof(InlineNamedValues): return true;
case typeof(StringView): return true;
default:
if (type.IsEnum)
return true;
if ((theObject is String) ||
(theObject is StructuredData))
return true;
return false;
}
}
Result<void> ObjectToString(String str, Object theObject)
{
var type = theObject.GetType();
Debug.Assert(type != null);
if (theObject == null)
str.Append("null");
else if (type == typeof(String))
StringEncode(str, StringView((String)theObject));
else if (type == typeof(StringView))
StringEncode(str, (StringView)theObject);
else if (type == typeof(System.Int32))
((int32)theObject).ToString(str);
else if (type == typeof(System.UInt32))
{
((uint32)theObject).ToString(str);
str.Append("U");
}
else if (type == typeof(System.Int64))
{
((uint64)theObject).ToString(str);
str.Append("L");
}
else if (type == typeof(System.UInt64))
{
((uint64)theObject).ToString(str);
str.Append("UL");
}
else if (type == typeof(System.Int))
{
((int32)(int)theObject).ToString(str);
//str.Append("L");
}
else if (type == typeof(System.UInt))
{
((uint32)(uint)theObject).ToString(str);
//str.Append("UL");
}
else if (type == typeof(System.Float))
{
str.AppendF("{0:0.0######}", (float)theObject);
}
else if (type == typeof(System.Double))
{
str.AppendF("{0:0.0######}", (float)(double)theObject);
}
else if (type == typeof(System.Boolean))
{
str.Append(((bool)theObject) ? "true" : "false");
}
else if (type.IsEnum)
{
String enumStr = scope String();
theObject.ToString(enumStr);
StringEncode(str, StringView(enumStr));
}
else
{
theObject.ToString(str);
}
/*else
{
//return new Exception("Invalid type: " + theObject.GetType().FullName);
String name = scope String();
type.GetName(name);
Debug.WriteLine("Invalid Type: {0}", name);
return new Exception();
}*/
return .Ok;
}
void ToJSONHelper(Values values, String outStr, bool humanReadable, String prefix)
{
String innerPrefix = scope String(prefix.Length + 1);
innerPrefix.Append(prefix);
if (humanReadable)
innerPrefix.Append('\t');
if (let namedValues = values as NamedValues)
{
int keyIdx = namedValues.mKeyIdx;
int valueIdx = namedValues.mValueIdx;
outStr.Append('{');
if (humanReadable)
outStr.Append('\n');
bool isFirst = true;
while (valueIdx != -1)
{
StringView key = mKeys[keyIdx];
Object value = mValues[valueIdx];
if (value == sEmptySentinel)
{
valueIdx = mNextValues[valueIdx];
keyIdx = mNextKeys[keyIdx];
continue;
}
if (isFirst)
{
isFirst = false;
}
else
{
outStr.Append(',');
if (humanReadable)
outStr.Append('\n');
}
if (humanReadable)
outStr.Append(innerPrefix);
StringEncode(outStr, key);
if (humanReadable)
outStr.Append(": ");
else
outStr.Append(":");
if (let subValues = value as Values)
{
ToJSONHelper(subValues, outStr, humanReadable, innerPrefix);
}
else
ObjectToString(outStr, value);
valueIdx = mNextValues[valueIdx];
keyIdx = mNextKeys[keyIdx];
}
if (humanReadable)
outStr.Append('\n');
if (humanReadable)
outStr.Append(prefix);
outStr.Append('}');
}
else
{
outStr.Append('[');
if (humanReadable)
outStr.Append('\n');
int valueIdx = values.mValueIdx;
bool isFirst = true;
while (valueIdx != -1)
{
Object value = mValues[valueIdx];
if (value == sEmptySentinel)
{
valueIdx = mNextValues[valueIdx];
continue;
}
if (isFirst)
{
isFirst = false;
}
else
{
outStr.Append(',');
if (humanReadable)
outStr.Append('\n');
}
if (humanReadable)
outStr.Append(innerPrefix);
if (let subValues = value as Values)
{
ToJSONHelper(subValues, outStr, humanReadable, innerPrefix);
}
else
ObjectToString(outStr, value);
valueIdx = mNextValues[valueIdx];
}
if (humanReadable)
outStr.Append('\n');
if (humanReadable)
outStr.Append(prefix);
outStr.Append(']');
}
}
public void ToJSON(String str, bool humanReadable = false)
{
//String aStringBuilder = new String();
ToJSONHelper(mCurrent.mValues, str, humanReadable, "");
//return aStringBuilder.ToString();
}
public void ToTOML(String outStr)
{
List<StringView> nameStack = scope List<StringView>();
List<Values> valueStack = scope List<Values>();
void EncodeName(StringView str)
{
bool NeedsEncoding(StringView str)
{
if (str.Length == 0)
return true;
if (str[0].IsNumber)
return true;
for (int i < str.Length)
{
char8 c = str[i];
if ((c.IsLetterOrDigit) || (c == '_') || (c == '-'))
{
// Valid
}
else
return true;
}
return false;
}
if (NeedsEncoding(str))
StringEncode(outStr, str);
else
outStr.Append(str);
}
void EncodeObject(Object obj)
{
if (let values = obj as Values)
{
if (let namedValues = values as NamedValues)
{
int keyIdx = namedValues.mKeyIdx;
int valueIdx = namedValues.mValueIdx;
outStr.Append('{');
bool wasFirst = true;
while (valueIdx != -1)
{
StringView key = mKeys[keyIdx];
Object value = mValues[valueIdx];
if (value == sEmptySentinel)
{
valueIdx = mNextValues[valueIdx];
keyIdx = mNextKeys[keyIdx];
continue;
}
if (!wasFirst)
outStr.Append(", ");
EncodeName(key);
outStr.Append(" = ");
EncodeObject(value);
valueIdx = mNextValues[valueIdx];
keyIdx = mNextKeys[keyIdx];
wasFirst = false;
}
outStr.Append('}');
return;
}
bool wasFirst = true;
outStr.Append("[");
int valueIdx = values.mValueIdx;
while (valueIdx != -1)
{
Object value = mValues[valueIdx];
if (value == sEmptySentinel)
{
valueIdx = mNextValues[valueIdx];
continue;
}
if (!wasFirst)
outStr.Append(", ");
EncodeObject(value);
valueIdx = mNextValues[valueIdx];
wasFirst = false;
}
outStr.Append("]");
return;
}
ObjectToString(outStr, obj);
}
void EncodeHeader()
{
bool isArray = false;
if (valueStack.Count > 1)
isArray = valueStack[valueStack.Count - 2].IsArray;
if (!outStr.IsEmpty)
outStr.Append("\n");
outStr.Append(isArray ? "[[" : "[");
for (int nameIdx < nameStack.Count)
{
if (nameIdx > 0)
outStr.Append(".");
EncodeName(nameStack[nameIdx]);
}
outStr.Append(isArray ? "]]\n" : "]\n");
}
void EncodeValues(Values values)
{
valueStack.Add(values);
defer valueStack.PopBack();
if (let namedValues = values as NamedValues)
{
bool forceAllInline = namedValues is InlineNamedValues;
//bool needsHeader = false;
if ((!outStr.IsEmpty) || (nameStack.Count > 0))
{
int valueIdx = namedValues.mValueIdx;
bool needsHeader = false;
while (valueIdx != -1)
{
Object value = mValues[valueIdx];
if (value == sEmptySentinel)
{
valueIdx = mNextValues[valueIdx];
continue;
}
if ((!value is NamedValues) || (forceAllInline))
needsHeader = true;
valueIdx = mNextValues[valueIdx];
}
if (needsHeader)
EncodeHeader();
}
for (int pass = 0; pass < 2; pass++)
{
bool isInlinePass = pass == 0;
int keyIdx = namedValues.mKeyIdx;
int valueIdx = namedValues.mValueIdx;
while (valueIdx != -1)
{
StringView key = mKeys[keyIdx];
Object value = mValues[valueIdx];
if (value == sEmptySentinel)
{
valueIdx = mNextValues[valueIdx];
keyIdx = mNextKeys[keyIdx];
continue;
}
bool doValuesInline = true;
if ((!forceAllInline) && (var subValues = value as Values))
{
if (!subValues.ForceInline)
{
doValuesInline = false;
if (subValues.IsArray)
{
int subValueIdx = subValues.mValueIdx;
while (subValueIdx != -1)
{
Object subValue = mValues[subValueIdx];
if (!subValue is NamedValues)
{
doValuesInline = true;
}
subValueIdx = mNextValues[subValueIdx];
}
}
}
if ((!doValuesInline) && (!isInlinePass))
{
nameStack.Add(key);
EncodeValues(subValues);
nameStack.PopBack();
}
}
if (doValuesInline && isInlinePass)
{
EncodeName(key);
outStr.Append(" = ");
EncodeObject(value);
outStr.Append("\n");
}
valueIdx = mNextValues[valueIdx];
keyIdx = mNextKeys[keyIdx];
}
}
}
else
{
int valueIdx = values.mValueIdx;
while (valueIdx != -1)
{
bool needsHeader = true;
Object value = mValues[valueIdx];
if (value == sEmptySentinel)
{
valueIdx = mNextValues[valueIdx];
continue;
}
if (var subValues = value as Values)
{
EncodeValues(subValues);
needsHeader = true;
}
else
{
ThrowUnimplemented();
/*if (needsHeader)
{
outStr.Append("[");
for (int nameIdx < nameStack.Count)
{
if (nameIdx > 0)
outStr.Append(".");
EncodeName(nameStack[nameIdx]);
}
outStr.Append("]\n");
needsHeader = false;
}
EncodeName(key);
outStr.Append(" = ");
ObjectToString(outStr, value);
outStr.Append("\n");*/
}
valueIdx = mNextValues[valueIdx];
}
}
}
//String aStringBuilder = new String();
EncodeValues(mCurrent.mValues);
//return aStringBuilder.ToString();
}
public override void ToString(String str)
{
ToJSON(str, false);
}
Result<Object, Error> LoadJSONHelper(String string, ref int32 idx, ref int32 lineNum)
{
CurrentEntry currentEntry = default;
Values values = null;
NamedValues namedValues = null;
Object valueData = null;
int32 valueStartIdx = -1;
int32 valueEndIdx = -1;
int32 keyStartIdx = -1;
int32 keyEndIdx = -1;
bool valueHadWhitespace = false;
bool inQuote = false;
bool lastWasSlash = false;
bool hadSlash = false;
bool keyHadSlash = false;
bool returningValues = false;
/*defer
{
if (!returningValues)
delete values;
};*/
int length = string.Length;
char8* cPtr = string.Ptr;
for (int32 char8Idx = idx; char8Idx < length; char8Idx++)
{
char8 c = cPtr[char8Idx];
if (lastWasSlash)
{
lastWasSlash = false;
valueEndIdx = char8Idx;
continue;
}
bool doProcess = false;
bool atEnd = false;
if (c == '\n')
lineNum++;
else if (c == '\\')
{
lastWasSlash = true;
hadSlash = true;
}
else if (c == '"')
{
if (inQuote)
{
valueEndIdx = char8Idx;
inQuote = false;
}
else
{
if ((valueStartIdx != -1) && (valueHadWhitespace))
return .Err(.FormatError(lineNum));
//Runtime.FatalError("Preceding comma expected");
valueStartIdx = char8Idx;
inQuote = true;
}
}
else if (inQuote)
{
valueEndIdx = char8Idx;
}
else if (c == '{')
{
if (values != null)
{
var result = LoadJSONHelper(string, ref char8Idx, ref lineNum);
if (result case .Err(var err))
return .Err(err);
valueData = result.Get();
}
else
{
values = namedValues = new:mBumpAllocator NamedValues();
currentEntry = CurrentEntry(values);
}
}
else if (c == '}')
{
if (namedValues == null)
return .Err(.UnexpectedObjectEnd);
atEnd = true;
if (((valueStartIdx != -1) || (valueData != null)) && (keyStartIdx != -1))
doProcess = true;
}
else if (c == '[')
{
if (values != null)
{
//valueData = ETry!(LoadJSONHelper(theString, ref char8Idx, ref lineNum));
var result = LoadJSONHelper(string, ref char8Idx, ref lineNum);
if (result case .Err(var err))
return .Err(err);
valueData = result.Get();
}
else
{
values = new:mBumpAllocator Values();
currentEntry = CurrentEntry(values);
}
}
else if (c == ']')
{
if ((values == null) || (namedValues != null))
return .Err(Error.UnexpectedObjectEnd);
atEnd = true;
if ((valueStartIdx != -1) || (valueData != null))
doProcess = true;
}
else if (c == ',')
{
doProcess = true;
}
else if (c == ':')
{
if ((keyStartIdx != -1) || (valueStartIdx == -1))
return .Err(Error.ColonNotExpected);
keyStartIdx = valueStartIdx;
keyEndIdx = valueEndIdx;
keyHadSlash = hadSlash;
valueStartIdx = -1;
valueEndIdx = -1;
}
else if (!c.IsWhiteSpace)
{
if (valueStartIdx == -1)
{
valueHadWhitespace = false;
valueStartIdx = char8Idx;
}
else if (valueHadWhitespace)
return .Err(Error.PrecedingCommaExpected);
valueEndIdx = char8Idx;
}
else
{
valueHadWhitespace = true;
}
if (doProcess)
{
Object aValue = null;
if (valueData != null)
{
if (valueStartIdx != -1)
return .Err(Error.FormatError(lineNum));
aValue = valueData;
valueData = null;
//WTF was this?
/*Values values = aValue as StructuredDataa;
if (valueStructuredData != null)
valueStructuredData.mParent = values;*/
}
else
{
if (valueStartIdx == -1)
return .Err(Error.ValueExpected);
if (cPtr[valueStartIdx] == '"')
{
//String str = new:mBumpAllocator String();
//theString.Substring(valueStartIdx + 1, valueEndIdx - valueStartIdx - 1, str);
if (hadSlash)
{
String str = new:mBumpAllocator String(string, valueStartIdx + 1, valueEndIdx - valueStartIdx - 1);
StringDecode(str);
aValue = str;
}
else
{
aValue = new:mBumpAllocator box StringView(string, valueStartIdx + 1, valueEndIdx - valueStartIdx - 1);
}
}
else
{
//String str = scope String(string, valueStartIdx, valueEndIdx - valueStartIdx + 1);
StringView strView = StringView(string, valueStartIdx, valueEndIdx - valueStartIdx + 1);
//string.Substring(valueStartIdx, valueEndIdx - valueStartIdx + 1, str);
char8 lastC = strView.Ptr[strView.Length - 1];
switch (lastC)
{
case 'l':
if (strView.Equals("null"))
aValue = null;
case 'e':
if (strView.Equals("true"))
aValue = new:mBumpAllocator box true;
else if (strView.Equals("false"))
aValue = new:mBumpAllocator box false;
case 'U':
ThrowUnimplemented();
case 'L':
ThrowUnimplemented();
default:
if (strView.Contains('.'))
{
aValue = new:mBumpAllocator box (float)float.Parse(strView);
}
else
{
var parseVal = int32.Parse(strView);
if (parseVal case .Ok(var intVal))
aValue = new:mBumpAllocator box intVal;
else
{
//TODO: Is temporary
/*if (str.StartsWith("Int@"))
aValue = new:mBumpAllocator box (int)0;
else if (str.StartsWith("Double@"))
aValue = new:mBumpAllocator box 0.0f;
else*/
return .Err(Error.ParseError);
}
}
}
}
}
if (namedValues == null)
{
if (keyStartIdx != -1)
return .Err(Error.KeyInArray);
//DoAdd(ref currentEntry, aValue);
int valueIdx = mValues.Count;
if (currentEntry.mLastValue != -1)
mNextValues[currentEntry.mLastValue] = (int32)valueIdx;
else
currentEntry.mValues.mValueIdx = (int32)valueIdx;
currentEntry.mLastValue = (int32)valueIdx;
mValues.Add(aValue);
mNextValues.Add(-1);
}
else
{
if (keyStartIdx == -1)
return .Err(Error.KeyRequiredForVal);
StringView key;
if (keyHadSlash)
{
String keyStr = new:mBumpAllocator String(string, keyStartIdx + 1, keyEndIdx - keyStartIdx - 1);
StringDecode(keyStr);
key = StringView(keyStr);
}
else
{
key = StringView(string, keyStartIdx + 1, keyEndIdx - keyStartIdx - 1);
}
//DoAdd(ref currentEntry, key, aValue);
int valueIdx = mValues.Count;
int keyIdx = mKeys.Count;
if (currentEntry.mLastValue != -1)
mNextValues[currentEntry.mLastValue] = (int32)valueIdx;
else
currentEntry.mValues.mValueIdx = (int32)valueIdx;
if (currentEntry.mLastKey != -1)
mNextKeys[currentEntry.mLastKey] = (int32)keyIdx;
else
namedValues.mKeyIdx = (int32)keyIdx;
currentEntry.mLastValue = (int32)valueIdx;
currentEntry.mLastKey = (int32)keyIdx;
mKeys.Add(key);
mNextKeys.Add(-1);
mValues.Add(aValue);
mNextValues.Add(-1);
keyStartIdx = -1;
keyEndIdx = -1;
}
valueStartIdx = -1;
valueEndIdx = -1;
valueHadWhitespace = false;
hadSlash = false;
keyHadSlash = false;
}
if (atEnd)
{
idx = char8Idx;
//values.mCanWrite = false;
returningValues = true;
return values;
}
}
//if (values != null)
//values.mCanWrite = false;
returningValues = true;
return values;
}
class LoadSection
{
public Dictionary<String, LoadSection> mSectionDict ~
{
if (_ != null)
{
for (var val in _.Values)
delete val;
delete _;
}
};
public List<LoadSection> mSectionList ~
{
if (_ != null)
{
for (var val in _)
delete val;
delete _;
}
};
public CurrentEntry mCurrentEntry;
public bool IsArray
{
get
{
return mSectionList != null;
}
}
}
Result<void, Error> LoadTOMLHelper(String contentStr, NamedValues values, ref int32 idx, ref int32 lineNum)
{
LoadSection loadRoot = scope LoadSection();
loadRoot.mSectionDict = new Dictionary<String, LoadSection>();
loadRoot.mCurrentEntry = CurrentEntry(values);
LoadSection loadSection = loadRoot;
//CurrentEntry currentEntry = default;
char8* cPtr = contentStr.CStr();
char8 NextChar()
{
char8 c = cPtr[idx];
if (c != 0)
idx++;
return c;
}
char8 PeekNextChar()
{
return cPtr[idx];
}
void EatWhiteSpace()
{
while (true)
{
char8 nextC = PeekNextChar();
if ((nextC != ' ') && (nextC != '\t'))
return;
idx++;
}
}
void EatInlineWhiteSpace()
{
while (true)
{
char8 nextC = PeekNextChar();
switch (nextC)
{
case ' ': break;
case '\t': break;
case '\n': break;
case '\r': break;
default: return;
}
idx++;
}
}
bool ReadToLineEnd()
{
bool foundContentChars = false;
bool inComment = false;
while (true)
{
char8 c = NextChar();
if (c == 0)
return !foundContentChars;
if (c == '\r')
continue;
if (c == '\n')
{
lineNum++;
return !foundContentChars;
}
if (inComment)
continue;
if (c == '#')
{
if (!foundContentChars)
inComment = true;
continue;
}
if ((c != '\t') && (c != ' '))
foundContentChars = true;
}
}
bool ReadName(String outName)
{
int startIdx = idx;
char8 c = NextChar();
if (c == '"')
{
bool hadSlash = false;
bool inSlash = false;
while (true)
{
char8 nextC = NextChar();
if (nextC == 0)
return false;
if (inSlash)
{
inSlash = false;
continue;
}
if (nextC == '\\')
{
hadSlash = true;
inSlash = true;
continue;
}
if (nextC == '"')
break;
}
outName.Append(contentStr, startIdx + 1, idx - startIdx - 2);
if (hadSlash)
StringDecode(outName);
return true;
}
else
{
while (true)
{
char8 nextC = NextChar();
if ((nextC.IsLetterOrDigit) || (nextC == '_') || (nextC == '-'))
{
// Valid part of name, keep going
continue;
}
idx--;
outName.Append(contentStr, startIdx, idx - startIdx);
return true;
}
}
}
Object ReadObject()
{
EatWhiteSpace();
char8 c = NextChar();
if ((c == '"') || (c == '\''))
{
String valueStr = scope String(256);
idx--;
if (!ReadName(valueStr))
return null;
return new:mBumpAllocator String(valueStr);
}
if (c == '[')
{
// Array
Values values = new:mBumpAllocator Values();
CurrentEntry arrayEntry = CurrentEntry(values);
while (true)
{
EatInlineWhiteSpace();
char8 nextC = NextChar();
if (nextC == ']')
break;
if (!arrayEntry.HasValues)
idx--;
else if (nextC != ',')
return null;
EatInlineWhiteSpace();
Object obj = ReadObject();
if (obj == null)
return null;
DoAdd(ref arrayEntry, obj);
}
return values;
}
if (c == '{')
{
// Inline table
NamedValues namedValues = new:mBumpAllocator NamedValues();
CurrentEntry tableEntry = CurrentEntry(namedValues);
while (true)
{
EatInlineWhiteSpace();
char8 nextC = NextChar();
if (nextC == '}')
break;
if (!tableEntry.HasValues)
idx--;
else if (nextC != ',')
return null;
EatInlineWhiteSpace();
String key = scope String(256);
if (!ReadName(key))
return null;
EatInlineWhiteSpace();
char8 eqChar = NextChar();
if (eqChar != '=')
return null;
EatInlineWhiteSpace();
Object obj = ReadObject();
if (obj == null)
return null;
DoAdd(ref tableEntry, new:mBumpAllocator String(key), obj);
}
return namedValues;
}
bool isNumber = false;
if ((c == '-') || (c.IsNumber))
isNumber = true;
bool hasDate = false;
bool hasTime = false;
bool isFloat = false;
bool isCompoundDate = false;
int startIdx = idx - 1;
ValueStrLoop: while (true)
{
char8 nextC = NextChar();
if ((nextC == ' ') && (hasDate))
{
if (!isCompoundDate)
{
if (PeekNextChar().IsNumber)
{
isCompoundDate = true;
continue;
}
}
}
switch (nextC)
{
case 0: fallthrough;
case ' ': fallthrough;
case '\t': fallthrough;
case '\r': fallthrough;
case '\n': fallthrough;
case ',': fallthrough;
case ']': fallthrough;
case '}':
idx--;
break ValueStrLoop;
}
if (nextC == '-')
hasDate = true;
else if (nextC == ':')
hasTime = true;
else if (nextC == 'T')
isCompoundDate = true;
else if (nextC == '.')
isFloat = true;
}
String value = scope String(256);
value.Append(contentStr, startIdx, idx - startIdx);
if ((hasDate) || (hasTime))
{
return new:mBumpAllocator String(value);
}
if (isNumber)
{
for (int i < value.Length)
if (value[i] == '_')
value.Remove(i--);
if (isFloat)
{
switch (Float.Parse(value))
{
case .Err: return null;
case .Ok(let num): return new:mBumpAllocator box num;
}
}
switch (Int32.Parse(value))
{
case .Err: return null;
case .Ok(let num): return new:mBumpAllocator box num;
}
}
if (value == "true")
return new:mBumpAllocator box true;
if (value == "false")
return new:mBumpAllocator box false;
return null;
}
while (true)
{
char8 c = NextChar();
if (c == 0)
{
return .Ok;
}
if (c == '#')
{
ReadToLineEnd();
continue;
}
if (c == '\n')
{
lineNum++;
continue;
}
if ((c == ' ') || (c == '\t') || (c == '\r'))
continue;
if (c == '[')
{
bool outerIsArray = PeekNextChar() == '[';
if (outerIsArray)
NextChar();
loadSection = loadRoot;
// TODO: Allow quoted names
while (true)
{
String entryName = scope String(256);
EatWhiteSpace();
if (!ReadName(entryName))
return .Err(.FormatError(lineNum));
bool hasMoreParts = false;
EatWhiteSpace();
char8 nextC = NextChar();
hasMoreParts = nextC == '.';
bool isArray = outerIsArray && !hasMoreParts;
/*if (loadSection.IsArray)
{
if (hasMoreParts)
{
if (loadSection.mSectionList.Count == 0)
return .Err(.FormatError);
loadSection = loadSection.mSectionList.Back;
}
}*/
String* keyPtr;
LoadSection* valuePtr;
if (loadSection.mSectionDict.TryAdd(entryName, out keyPtr, out valuePtr))
{
var key = new:mBumpAllocator String(entryName);
*keyPtr = key;
let newSection = new LoadSection();
if (isArray)
{
newSection.mSectionList = new List<LoadSection>();
newSection.mCurrentEntry = CurrentEntry(new:mBumpAllocator Values());
}
else
{
newSection.mSectionDict = new Dictionary<String, LoadSection>();
newSection.mCurrentEntry = CurrentEntry(new:mBumpAllocator NamedValues());
}
DoAdd(ref loadSection.mCurrentEntry, key, newSection.mCurrentEntry.mValues);
loadSection = newSection;
*valuePtr = loadSection;
}
else
{
loadSection = *valuePtr;
if (hasMoreParts)
{
if (loadSection.mCurrentEntry.mValues.IsArray)
{
// Use most recent entry
if (loadSection.mSectionList.Count == 0)
return .Err(.FormatError(lineNum));
loadSection = loadSection.mSectionList.Back;
}
}
else
{
if (loadSection.mCurrentEntry.mValues.IsArray != isArray)
return .Err(.FormatError(lineNum));
}
}
if (isArray)
{
var newSection = new LoadSection();
newSection.mSectionDict = new Dictionary<String, LoadSection>();
newSection.mCurrentEntry = CurrentEntry(new:mBumpAllocator NamedValues());
loadSection.mSectionList.Add(newSection);
DoAdd(ref loadSection.mCurrentEntry, newSection.mCurrentEntry.mValues);
loadSection = newSection;
//currentEntry = loadSection.mCurrentEntry;
}
else
{
//currentEntry = loadSection.mCurrentEntry;
}
if (hasMoreParts)
{
// Go deeper!
continue;
}
if (nextC == ']')
{
if ((outerIsArray) && (NextChar() != ']'))
return .Err(.ExpectedArrayNameEnd);
break;
}
return .Err(.FormatError(lineNum));
}
continue;
}
// It's a value
String key = scope String(256);
idx--;
if (!ReadName(key))
return .Err(.FormatError(lineNum));
EatWhiteSpace();
char8 eqChar = NextChar();
if (eqChar != '=')
return .Err(.FormatError(lineNum));
Object obj = ReadObject();
if (obj == null)
return .Err(.FormatError(lineNum));
DoAdd(ref loadSection.mCurrentEntry, new:mBumpAllocator String(key), obj);
if (!ReadToLineEnd())
return .Err(.FormatError(lineNum));
}
}
protected Result<void, Error> Load()
{
EnsureHasData();
int guessItems = mSource.Length / 32;
mValues.Reserve(guessItems);
mKeys.Reserve(guessItems);
mNextValues.Reserve(guessItems);
mNextKeys.Reserve(guessItems);
bool isJson = false;
for (char8 c in mSource.RawChars)
{
if (c.IsWhiteSpace)
continue;
if (c == '{')
isJson = true;
break;
}
int32 aLineNum = 1;
int32 anIdx = 0;
Object objResult;
if (isJson)
{
let result = LoadJSONHelper(mSource, ref anIdx, ref aLineNum);
if (result case .Err(var err))
return .Err(err);
objResult = result.Get();
}
else
{
var values = new:mBumpAllocator NamedValues();
let result = LoadTOMLHelper(mSource, values, ref anIdx, ref aLineNum);
if (result case .Err(var err))
return .Err(err);
objResult = values;
}
if (var values = objResult as Values)
{
//mCurrent = CurrentEntry(values);
int valIdx = mValues.Count;
mHeadValues = new:mBumpAllocator Values();
mHeadValues.mValueIdx = (int32)valIdx;
mCurrent.mValues = mHeadValues;
mCurrent.mLastValue = (int32)valIdx;
mValues.Add(values);
mNextValues.Add(-1);
return .Ok;
}
else
{
//delete obj;
return .Err(Error.FormatError(aLineNum));
}
}
public Result<void, Error> Load(StringView fileName)
{
mSource = new:mBumpAllocator StringWithAlloc(mBumpAllocator);
if (File.ReadAllText(scope String(fileName), mSource, true) case .Err)
{
return .Err(Error.FileError);
}
return Load();
}
public Result<void, Error> LoadFromString(StringView data)
{
mSource = new:mBumpAllocator StringWithAlloc(mBumpAllocator);
mSource.Reserve(data.Length + 1);
mSource.Append(data);
return Load();
}
}
}