mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 00:37:15 +01:00
Implement custom types in JSON.
This commit is contained in:
parent
c9fb257f48
commit
f4720ae77a
@ -13,18 +13,6 @@ namespace SqMod {
|
|||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
SQMOD_DECL_TYPENAME(SqCtxJSON, _SC("SqCtxJSON"))
|
SQMOD_DECL_TYPENAME(SqCtxJSON, _SC("SqCtxJSON"))
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
static SQInteger SqToJSON(HSQUIRRELVM vm) noexcept
|
|
||||||
{
|
|
||||||
return CtxJSON().SerializeParams(vm);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
static SQInteger SqToCompactJSON(HSQUIRRELVM vm) noexcept
|
|
||||||
{
|
|
||||||
return CtxJSON(false).SerializeParams(vm);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
static SQInteger SqFromJson_Push(HSQUIRRELVM vm, const sajson::value & node) noexcept // NOLINT(misc-no-recursion)
|
static SQInteger SqFromJson_Push(HSQUIRRELVM vm, const sajson::value & node) noexcept // NOLINT(misc-no-recursion)
|
||||||
{
|
{
|
||||||
@ -160,6 +148,524 @@ static SQInteger SqFromJSON(HSQUIRRELVM vm) noexcept
|
|||||||
return SQ_SUCCEEDED(r) ? 1 : r;
|
return SQ_SUCCEEDED(r) ? 1 : r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
CtxJSON & CtxJSON::OpenArray()
|
||||||
|
{
|
||||||
|
// Add the array-begin character
|
||||||
|
mOutput.push_back('[');
|
||||||
|
// Go forward one level
|
||||||
|
Advance();
|
||||||
|
// Allow chaining
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
CtxJSON & CtxJSON::CloseArray()
|
||||||
|
{
|
||||||
|
// If the last character is a comma then replace it
|
||||||
|
if (mOutput.back() == ',')
|
||||||
|
{
|
||||||
|
mOutput.back() = ']';
|
||||||
|
}
|
||||||
|
// Append the array-end character
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mOutput.push_back(']');
|
||||||
|
}
|
||||||
|
// Go back one level
|
||||||
|
Retreat();
|
||||||
|
// Allow chaining
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
CtxJSON & CtxJSON::OpenObject()
|
||||||
|
{
|
||||||
|
// Add the object-begin character
|
||||||
|
mOutput.push_back('{');
|
||||||
|
// Go forward one level
|
||||||
|
Advance();
|
||||||
|
// Allow chaining
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
CtxJSON & CtxJSON::CloseObject()
|
||||||
|
{
|
||||||
|
// If the last character is a comma then replace it
|
||||||
|
if (mOutput.back() == ',')
|
||||||
|
{
|
||||||
|
mOutput.back() = '}';
|
||||||
|
}
|
||||||
|
// Append the object-end character
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mOutput.push_back('}');
|
||||||
|
}
|
||||||
|
// Go back one level
|
||||||
|
Retreat();
|
||||||
|
// Allow chaining
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
CtxJSON & CtxJSON::MakeKey()
|
||||||
|
{
|
||||||
|
// If the last character is a comma then replace it
|
||||||
|
if (mOutput.back() == ',')
|
||||||
|
{
|
||||||
|
mOutput.back() = ':';
|
||||||
|
}
|
||||||
|
// Append the array-end character
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mOutput.push_back(':');
|
||||||
|
}
|
||||||
|
// Allow chaining
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
bool CtxJSON::CheckWeakRefWrap(HSQUIRRELVM vm, SQInteger idx) noexcept
|
||||||
|
{
|
||||||
|
SQRESULT r = sq_getweakrefval(vm, idx);
|
||||||
|
// Whether the type doesn't have to be wrapped
|
||||||
|
bool w = true;
|
||||||
|
// Attempt to grab the value pointed by the weak reference
|
||||||
|
if (SQ_SUCCEEDED(r))
|
||||||
|
{
|
||||||
|
// Attempt to serialize the actual value
|
||||||
|
w = sq_gettype(vm, -1) != OT_TABLE && sq_gettype(vm, -1) != OT_ARRAY && sq_gettype(vm, -1) == OT_INSTANCE;
|
||||||
|
// Pop the referenced value
|
||||||
|
sq_poptop(vm);
|
||||||
|
}
|
||||||
|
// Wrap the value by default
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
SQRESULT CtxJSON::SerializeParams(HSQUIRRELVM vm)
|
||||||
|
{
|
||||||
|
bool wrap_everything_in_array = false;
|
||||||
|
// Clear the output buffer if necessary
|
||||||
|
mOutput.clear();
|
||||||
|
mDepth = 0;
|
||||||
|
// Fetch the number of objects on the stack
|
||||||
|
const auto top = sq_gettop(vm);
|
||||||
|
// If there's more than one argument then they all get wrapped inside an array
|
||||||
|
// If there is one argument and is not an array, table or instance then do the same
|
||||||
|
if (top > 2 || (sq_gettype(vm, 2) != OT_TABLE &&
|
||||||
|
sq_gettype(vm, 2) != OT_ARRAY &&
|
||||||
|
sq_gettype(vm, 2) != OT_INSTANCE &&
|
||||||
|
CheckWeakRefWrap(vm, 2)))
|
||||||
|
{
|
||||||
|
wrap_everything_in_array = true;
|
||||||
|
// Open an array
|
||||||
|
OpenArray();
|
||||||
|
}
|
||||||
|
// Serialize every specified argument
|
||||||
|
for (SQInteger i = 2; i <= top; ++i)
|
||||||
|
{
|
||||||
|
if (SQRESULT r = SerializeAt(vm, i); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Was everything wrapped inside an array?
|
||||||
|
if (wrap_everything_in_array)
|
||||||
|
{
|
||||||
|
CloseArray();
|
||||||
|
}
|
||||||
|
// Remove trailing separator, if any
|
||||||
|
else if (mOutput.back() == ',')
|
||||||
|
{
|
||||||
|
mOutput.pop_back();
|
||||||
|
}
|
||||||
|
// Push the output string on the stack
|
||||||
|
sq_pushstring(vm, mOutput.c_str(), static_cast< SQInteger >(mOutput.size()));
|
||||||
|
// Specify that we have a value on the stack
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
SQRESULT CtxJSON::SerializeAt(HSQUIRRELVM vm, SQInteger idx) // NOLINT(misc-no-recursion)
|
||||||
|
{
|
||||||
|
// Identify object type
|
||||||
|
switch (sq_gettype(vm, idx))
|
||||||
|
{
|
||||||
|
case OT_NULL: {
|
||||||
|
PushNull();
|
||||||
|
} break;
|
||||||
|
case OT_INTEGER: {
|
||||||
|
SQInteger v;
|
||||||
|
// Attempt to retrieve the value from the stack
|
||||||
|
if (SQRESULT r = sq_getinteger(vm, idx, &v); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
// Write the value in the output
|
||||||
|
PushInteger(v);
|
||||||
|
} break;
|
||||||
|
case OT_FLOAT: {
|
||||||
|
SQFloat v;
|
||||||
|
// Attempt to retrieve the value from the stack
|
||||||
|
if (SQRESULT r = sq_getfloat(vm, idx, &v); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
// Write the value in the output
|
||||||
|
#ifdef SQUSEDOUBLE
|
||||||
|
PushDouble(v);
|
||||||
|
#else
|
||||||
|
PushFloat(v);
|
||||||
|
#endif
|
||||||
|
} break;
|
||||||
|
case OT_BOOL: {
|
||||||
|
SQBool v;
|
||||||
|
// Attempt to retrieve the value from the stack
|
||||||
|
if (SQRESULT r = sq_getbool(vm, idx, &v); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
// Write the value in the output
|
||||||
|
PushBool(v != SQFalse);
|
||||||
|
} break;
|
||||||
|
case OT_STRING: {
|
||||||
|
const SQChar * v = nullptr;
|
||||||
|
SQInteger n = 0;
|
||||||
|
// Attempt to retrieve and convert the string
|
||||||
|
if (SQRESULT r = sq_getstringandsize(vm, idx, &v, &n); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
// Write the value in the output
|
||||||
|
PushString(v, static_cast< size_t >(n));
|
||||||
|
} break;
|
||||||
|
case OT_TABLE: {
|
||||||
|
if (SQRESULT r = SerializeTable(vm, idx); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
// Include the separator manually
|
||||||
|
mOutput.push_back(',');
|
||||||
|
} break;
|
||||||
|
case OT_ARRAY: {
|
||||||
|
if (SQRESULT r = SerializeArray(vm, idx); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
// Include the separator manually
|
||||||
|
mOutput.push_back(',');
|
||||||
|
} break;
|
||||||
|
case OT_INSTANCE: {
|
||||||
|
if (SQRESULT r = SerializeInstance(vm, idx); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case OT_WEAKREF: {
|
||||||
|
if (SQRESULT r = SerializeWeakRef(vm, idx); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case OT_USERDATA:
|
||||||
|
case OT_CLOSURE:
|
||||||
|
case OT_NATIVECLOSURE:
|
||||||
|
case OT_GENERATOR:
|
||||||
|
case OT_USERPOINTER:
|
||||||
|
case OT_THREAD:
|
||||||
|
case OT_FUNCPROTO:
|
||||||
|
case OT_CLASS:
|
||||||
|
case OT_OUTER:
|
||||||
|
return sq_throwerrorf(vm, _SC("Type (%s) is not serializable"), SqTypeName(sq_gettype(vm, 2)));
|
||||||
|
}
|
||||||
|
// Serialization was successful
|
||||||
|
return SQ_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
SQRESULT CtxJSON::SerializeArray(HSQUIRRELVM vm, SQInteger idx) // NOLINT(misc-no-recursion)
|
||||||
|
{
|
||||||
|
// Begin array scope
|
||||||
|
OpenArray();
|
||||||
|
// Push null to initiate iteration
|
||||||
|
sq_pushnull(vm);
|
||||||
|
// So we can use absolute stack indexes to avoid errors
|
||||||
|
const auto top = sq_gettop(vm);
|
||||||
|
// Start iterating the array at the specified position in the stack
|
||||||
|
for(SQRESULT r = SQ_OK; SQ_SUCCEEDED(sq_next(vm, idx));)
|
||||||
|
{
|
||||||
|
// Attempt serialization of the currently iterated value
|
||||||
|
r = SerializeAt(vm, top + 2);
|
||||||
|
// Check for failures
|
||||||
|
if (SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
// Pop the null iterator, key and value from the stack
|
||||||
|
sq_pop(vm, 3);
|
||||||
|
// Propagate the error
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
// Pop the key and value from the stack (i.e. cleanup after `sq_next`)
|
||||||
|
sq_pop(vm, 2);
|
||||||
|
}
|
||||||
|
// Pop the null iterator
|
||||||
|
sq_poptop(vm);
|
||||||
|
// Close array scope
|
||||||
|
CloseArray();
|
||||||
|
// Serialization was successful
|
||||||
|
return SQ_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
SQRESULT CtxJSON::SerializeTable(HSQUIRRELVM vm, SQInteger idx) // NOLINT(misc-no-recursion)
|
||||||
|
{
|
||||||
|
// Begin object scope
|
||||||
|
OpenObject();
|
||||||
|
// Push null to initiate iteration
|
||||||
|
sq_pushnull(vm);
|
||||||
|
// So we can use absolute stack indexes to avoid errors
|
||||||
|
const auto top = sq_gettop(vm);
|
||||||
|
// Start iterating the object at the specified position in the stack
|
||||||
|
for(SQRESULT r = SQ_OK; SQ_SUCCEEDED(sq_next(vm, idx));)
|
||||||
|
{
|
||||||
|
if (sq_gettype(vm, -2) == OT_STRING)
|
||||||
|
{
|
||||||
|
// Attempt serialization of the currently iterated element key
|
||||||
|
r = SerializeAt(vm, top + 1);
|
||||||
|
// Can we proceed with the value?
|
||||||
|
if (SQ_SUCCEEDED(r))
|
||||||
|
{
|
||||||
|
// Mark the value above as the key of this element and
|
||||||
|
// attempt serialization of the currently iterated element value
|
||||||
|
r = MakeKey().SerializeAt(vm, top + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r = sq_throwerror(vm, _SC("Only string values are accepted as object keys"));
|
||||||
|
}
|
||||||
|
// Check for failures
|
||||||
|
if (SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
// Pop the null iterator, key and value from the stack
|
||||||
|
sq_pop(vm, 3);
|
||||||
|
// Propagate the error
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
// Pop the key and value from the stack (i.e. cleanup after `sq_next`)
|
||||||
|
sq_pop(vm, 2);
|
||||||
|
}
|
||||||
|
// Pop the null iterator
|
||||||
|
sq_poptop(vm);
|
||||||
|
// Close object scope
|
||||||
|
CloseObject();
|
||||||
|
// Serialization was successful
|
||||||
|
return SQ_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
SQRESULT CtxJSON::SerializeInstance(HSQUIRRELVM vm, SQInteger idx)
|
||||||
|
{
|
||||||
|
sq_pushstring(vm, _SC("_tojson"), 7);
|
||||||
|
// Attempt to retrieve the meta-method from the instance
|
||||||
|
if(SQRESULT r = sq_get(vm, idx); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
// Make sure this is actually a closure/function that we can invoke
|
||||||
|
else if (const auto t = sq_gettype(vm, -1); t != OT_CLOSURE && t != OT_NATIVECLOSURE)
|
||||||
|
{
|
||||||
|
// Remove whatever is on the stack
|
||||||
|
sq_poptop(vm);
|
||||||
|
// Abort the operation as we can't do anything about it
|
||||||
|
return sq_throwerrorf(vm, _SC("`_tojson` meta-method is not a closure for type (%s)"), SqTypeName(vm, idx).c_str());
|
||||||
|
}
|
||||||
|
// Push the instance itself the stack (the environment)
|
||||||
|
sq_push(vm, idx);
|
||||||
|
// Push this instance on the stack (the json context)
|
||||||
|
ClassType< CtxJSON >::PushInstance(vm, this);
|
||||||
|
// Invoke the function to perform the conversion in this context
|
||||||
|
SQRESULT r = sq_call(vm, 2, SQFalse, SQFalse);
|
||||||
|
// Remove the closure from the stack
|
||||||
|
sq_poptop(vm);
|
||||||
|
// Propagate the result, whatever that is
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
SQRESULT CtxJSON::SerializeWeakRef(HSQUIRRELVM vm, SQInteger idx) // NOLINT(misc-no-recursion)
|
||||||
|
{
|
||||||
|
SQRESULT r = sq_getweakrefval(vm, idx);
|
||||||
|
// Attempt to grab the value pointed by the weak reference
|
||||||
|
if (SQ_SUCCEEDED(r))
|
||||||
|
{
|
||||||
|
// Attempt to serialize the actual value
|
||||||
|
r = SerializeAt(vm, sq_gettop(vm));
|
||||||
|
// Pop the referenced value
|
||||||
|
sq_poptop(vm);
|
||||||
|
}
|
||||||
|
// Propagate the error, if any
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
SQRESULT CtxJSON::PushValues(HSQUIRRELVM vm)
|
||||||
|
{
|
||||||
|
// Fetch the number of objects on the stack
|
||||||
|
const auto top = sq_gettop(vm);
|
||||||
|
// Do we have a value?
|
||||||
|
if (top < 2)
|
||||||
|
{
|
||||||
|
return sq_throwerror(vm, _SC("Must specify at least one value to be pushed"));
|
||||||
|
}
|
||||||
|
// Serialize every specified argument
|
||||||
|
for (SQInteger i = 2; i <= top; ++i)
|
||||||
|
{
|
||||||
|
if (SQRESULT r = SerializeAt(vm, i); SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Allow chaining
|
||||||
|
sq_push(vm, 1);
|
||||||
|
// Specify that a value was returned
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
SQRESULT CtxJSON::PushElement(HSQUIRRELVM vm)
|
||||||
|
{
|
||||||
|
// Fetch the number of objects on the stack
|
||||||
|
const auto top = sq_gettop(vm);
|
||||||
|
// Do we have a value?
|
||||||
|
if (top < 3)
|
||||||
|
{
|
||||||
|
return sq_throwerrorf(vm, _SC("Not enough parameters. Received %lld but %lld needed"), top-1, 2);
|
||||||
|
}
|
||||||
|
else if (sq_gettype(vm, 2) != OT_STRING)
|
||||||
|
{
|
||||||
|
return sq_throwerrorf(vm, _SC("Element key must be a string"));
|
||||||
|
}
|
||||||
|
// Attempt serialization of the currently iterated element key
|
||||||
|
if (SQRESULT r = SerializeAt(vm, 2); SQ_SUCCEEDED(r))
|
||||||
|
{
|
||||||
|
// Mark the value above as the key of this element and
|
||||||
|
// attempt serialization of the currently iterated element value
|
||||||
|
r = MakeKey().SerializeAt(vm, 3);
|
||||||
|
// Check for failures
|
||||||
|
if (SQ_FAILED(r))
|
||||||
|
{
|
||||||
|
return r; // Propagate the error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Allow chaining
|
||||||
|
sq_push(vm, 1);
|
||||||
|
// Specify that a value was returned
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
CtxJSON & CtxJSON::PushKey(StackStrF & key)
|
||||||
|
{
|
||||||
|
// Validate the string value
|
||||||
|
if (key.mLen >= 0 && SQ_SUCCEEDED(key.mRes))
|
||||||
|
{
|
||||||
|
PushString(key.mPtr, static_cast< size_t >(key.mLen));
|
||||||
|
MakeKey();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
STHROWF("Invalid object key");
|
||||||
|
}
|
||||||
|
// Allow chaining
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void CtxJSON::PushNull()
|
||||||
|
{
|
||||||
|
mOutput.append("null,");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void CtxJSON::PushInteger(SQInteger value)
|
||||||
|
{
|
||||||
|
fmt::format_int f(value);
|
||||||
|
// Append the formatted integer to the buffer
|
||||||
|
mOutput.append(f.data(), f.size());
|
||||||
|
mOutput.push_back(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void CtxJSON::PushFloat(float value)
|
||||||
|
{
|
||||||
|
fmt::format_to(std::back_inserter(mOutput), "{},", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void CtxJSON::PushDouble(double value)
|
||||||
|
{
|
||||||
|
fmt::format_to(std::back_inserter(mOutput), "{},", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void CtxJSON::PushBool(bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
mOutput.append("true,", 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mOutput.append("false,", 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void CtxJSON::PushString(const SQChar * str)
|
||||||
|
{
|
||||||
|
mOutput.push_back('"');
|
||||||
|
mOutput.append(str);
|
||||||
|
mOutput.push_back('"');
|
||||||
|
mOutput.push_back(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void CtxJSON::PushString(const SQChar * str, size_t length)
|
||||||
|
{
|
||||||
|
mOutput.push_back('"');
|
||||||
|
mOutput.append(str, length);
|
||||||
|
mOutput.push_back('"');
|
||||||
|
mOutput.push_back(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
static SQInteger SqToJSON(HSQUIRRELVM vm) noexcept
|
||||||
|
{
|
||||||
|
// Make sure the instance is cleaned up even in the case of exceptions
|
||||||
|
DeleteGuard< CtxJSON > sq_dg(new CtxJSON());
|
||||||
|
// Remember the instance, so we don't have to cast the script object back
|
||||||
|
auto ctx = sq_dg.Get();
|
||||||
|
// Turn it into a script object because it may be passed as a parameter to `_tojson` meta-methods
|
||||||
|
LightObj obj(sq_dg, vm);
|
||||||
|
// Proceed with the serialization
|
||||||
|
return ctx->SerializeParams(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
static SQInteger SqToCompactJSON(HSQUIRRELVM vm) noexcept
|
||||||
|
{
|
||||||
|
// Make sure the instance is cleaned up even in the case of exceptions
|
||||||
|
DeleteGuard< CtxJSON > sq_dg(new CtxJSON(false));
|
||||||
|
// Remember the instance, so we don't have to cast the script object back
|
||||||
|
auto ctx = sq_dg.Get();
|
||||||
|
// Turn it into a script object because it may be passed as a parameter to `_tojson` meta-methods
|
||||||
|
LightObj obj(sq_dg, vm);
|
||||||
|
// Proceed with the serialization
|
||||||
|
return ctx->SerializeParams(vm);
|
||||||
|
}
|
||||||
|
|
||||||
// ================================================================================================
|
// ================================================================================================
|
||||||
void Register_JSON(HSQUIRRELVM vm)
|
void Register_JSON(HSQUIRRELVM vm)
|
||||||
{
|
{
|
||||||
|
@ -128,515 +128,112 @@ struct CtxJSON
|
|||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Begin writing an array.
|
* Begin writing an array.
|
||||||
*/
|
*/
|
||||||
CtxJSON & OpenArray()
|
CtxJSON & OpenArray();
|
||||||
{
|
|
||||||
// Add the array-begin character
|
|
||||||
mOutput.push_back('[');
|
|
||||||
// Go forward one level
|
|
||||||
Advance();
|
|
||||||
// Allow chaining
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Stop writing an array.
|
* Stop writing an array.
|
||||||
*/
|
*/
|
||||||
CtxJSON & CloseArray()
|
CtxJSON & CloseArray();
|
||||||
{
|
|
||||||
// If the last character is a comma then replace it
|
|
||||||
if (mOutput.back() == ',')
|
|
||||||
{
|
|
||||||
mOutput.back() = ']';
|
|
||||||
}
|
|
||||||
// Append the array-end character
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mOutput.push_back(']');
|
|
||||||
}
|
|
||||||
// Go back one level
|
|
||||||
Retreat();
|
|
||||||
// Allow chaining
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Begin writing an object.
|
* Begin writing an object.
|
||||||
*/
|
*/
|
||||||
CtxJSON & OpenObject()
|
CtxJSON & OpenObject();
|
||||||
{
|
|
||||||
// Add the object-begin character
|
|
||||||
mOutput.push_back('{');
|
|
||||||
// Go forward one level
|
|
||||||
Advance();
|
|
||||||
// Allow chaining
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Stop writing an object.
|
* Stop writing an object.
|
||||||
*/
|
*/
|
||||||
CtxJSON & CloseObject()
|
CtxJSON & CloseObject();
|
||||||
{
|
|
||||||
// If the last character is a comma then replace it
|
|
||||||
if (mOutput.back() == ',')
|
|
||||||
{
|
|
||||||
mOutput.back() = '}';
|
|
||||||
}
|
|
||||||
// Append the object-end character
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mOutput.push_back('}');
|
|
||||||
}
|
|
||||||
// Go back one level
|
|
||||||
Retreat();
|
|
||||||
// Allow chaining
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Begin writing a key value.
|
* Begin writing a key value.
|
||||||
*/
|
*/
|
||||||
CtxJSON & MakeKey()
|
CtxJSON & MakeKey();
|
||||||
{
|
|
||||||
// If the last character is a comma then replace it
|
|
||||||
if (mOutput.back() == ',')
|
|
||||||
{
|
|
||||||
mOutput.back() = ':';
|
|
||||||
}
|
|
||||||
// Append the array-end character
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mOutput.push_back(':');
|
|
||||||
}
|
|
||||||
// Allow chaining
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Check whether the specified weak-ref points to a type of value that must be wrapped.
|
* Check whether the specified weak-ref points to a type of value that must be wrapped.
|
||||||
*/
|
*/
|
||||||
SQMOD_NODISCARD static bool CheckWeakRefWrap(HSQUIRRELVM vm, SQInteger idx) noexcept
|
SQMOD_NODISCARD static bool CheckWeakRefWrap(HSQUIRRELVM vm, SQInteger idx) noexcept;
|
||||||
{
|
|
||||||
SQRESULT r = sq_getweakrefval(vm, idx);
|
|
||||||
// Whether the type doesn't have to be wrapped
|
|
||||||
bool w = true;
|
|
||||||
// Attempt to grab the value pointed by the weak reference
|
|
||||||
if (SQ_SUCCEEDED(r))
|
|
||||||
{
|
|
||||||
// Attempt to serialize the actual value
|
|
||||||
w = sq_gettype(vm, -1) != OT_TABLE && sq_gettype(vm, -1) != OT_ARRAY && sq_gettype(vm, -1) == OT_INSTANCE;
|
|
||||||
// Pop the referenced value
|
|
||||||
sq_poptop(vm);
|
|
||||||
}
|
|
||||||
// Wrap the value by default
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Serialize given arguments.
|
* Serialize given arguments.
|
||||||
*/
|
*/
|
||||||
SQRESULT SerializeParams(HSQUIRRELVM vm)
|
SQRESULT SerializeParams(HSQUIRRELVM vm);
|
||||||
{
|
|
||||||
bool wrap_everything_in_array = false;
|
|
||||||
// Clear the output buffer if necessary
|
|
||||||
mOutput.clear();
|
|
||||||
mDepth = 0;
|
|
||||||
// Fetch the number of objects on the stack
|
|
||||||
const auto top = sq_gettop(vm);
|
|
||||||
// If there's more than one argument then they all get wrapped inside an array
|
|
||||||
// If there is one argument and is not an array, table or instance then do the same
|
|
||||||
if (top > 2 || (sq_gettype(vm, 2) != OT_TABLE &&
|
|
||||||
sq_gettype(vm, 2) != OT_ARRAY &&
|
|
||||||
sq_gettype(vm, 2) != OT_INSTANCE &&
|
|
||||||
CheckWeakRefWrap(vm, 2)))
|
|
||||||
{
|
|
||||||
wrap_everything_in_array = true;
|
|
||||||
// Open an array
|
|
||||||
OpenArray();
|
|
||||||
}
|
|
||||||
// Serialize every specified argument
|
|
||||||
for (SQInteger i = 2; i <= top; ++i)
|
|
||||||
{
|
|
||||||
if (SQRESULT r = SerializeAt(vm, i); SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Was everything wrapped inside an array?
|
|
||||||
if (wrap_everything_in_array)
|
|
||||||
{
|
|
||||||
CloseArray();
|
|
||||||
}
|
|
||||||
// Remove trailing separator, if any
|
|
||||||
else if (mOutput.back() == ',')
|
|
||||||
{
|
|
||||||
mOutput.pop_back();
|
|
||||||
}
|
|
||||||
// Push the output string on the stack
|
|
||||||
sq_pushstring(vm, mOutput.c_str(), static_cast< SQInteger >(mOutput.size()));
|
|
||||||
// Specify that we have a value on the stack
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Serialize the value a specific position in the stack.
|
* Serialize the value a specific position in the stack.
|
||||||
*/
|
*/
|
||||||
SQRESULT SerializeAt(HSQUIRRELVM vm, SQInteger idx) // NOLINT(misc-no-recursion)
|
SQRESULT SerializeAt(HSQUIRRELVM vm, SQInteger idx);
|
||||||
{
|
|
||||||
// Identify object type
|
|
||||||
switch (sq_gettype(vm, idx))
|
|
||||||
{
|
|
||||||
case OT_NULL: {
|
|
||||||
PushNull();
|
|
||||||
} break;
|
|
||||||
case OT_INTEGER: {
|
|
||||||
SQInteger v;
|
|
||||||
// Attempt to retrieve the value from the stack
|
|
||||||
if (SQRESULT r = sq_getinteger(vm, idx, &v); SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
// Write the value in the output
|
|
||||||
PushInteger(v);
|
|
||||||
} break;
|
|
||||||
case OT_FLOAT: {
|
|
||||||
SQFloat v;
|
|
||||||
// Attempt to retrieve the value from the stack
|
|
||||||
if (SQRESULT r = sq_getfloat(vm, idx, &v); SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
// Write the value in the output
|
|
||||||
#ifdef SQUSEDOUBLE
|
|
||||||
PushDouble(v);
|
|
||||||
#else
|
|
||||||
PushFloat(v);
|
|
||||||
#endif
|
|
||||||
} break;
|
|
||||||
case OT_BOOL: {
|
|
||||||
SQBool v;
|
|
||||||
// Attempt to retrieve the value from the stack
|
|
||||||
if (SQRESULT r = sq_getbool(vm, idx, &v); SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
// Write the value in the output
|
|
||||||
PushBool(v != SQFalse);
|
|
||||||
} break;
|
|
||||||
case OT_STRING: {
|
|
||||||
const SQChar * v = nullptr;
|
|
||||||
SQInteger n = 0;
|
|
||||||
// Attempt to retrieve and convert the string
|
|
||||||
if (SQRESULT r = sq_getstringandsize(vm, idx, &v, &n); SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
// Write the value in the output
|
|
||||||
PushString(v, static_cast< size_t >(n));
|
|
||||||
} break;
|
|
||||||
case OT_TABLE: {
|
|
||||||
if (SQRESULT r = SerializeTable(vm, idx); SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
// Include the separator manually
|
|
||||||
mOutput.push_back(',');
|
|
||||||
} break;
|
|
||||||
case OT_ARRAY: {
|
|
||||||
if (SQRESULT r = SerializeArray(vm, idx); SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
// Include the separator manually
|
|
||||||
mOutput.push_back(',');
|
|
||||||
} break;
|
|
||||||
case OT_INSTANCE: {
|
|
||||||
if (SQRESULT r = SerializeInstance(vm, idx); SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case OT_WEAKREF: {
|
|
||||||
if (SQRESULT r = SerializeWeakRef(vm, idx); SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case OT_USERDATA:
|
|
||||||
case OT_CLOSURE:
|
|
||||||
case OT_NATIVECLOSURE:
|
|
||||||
case OT_GENERATOR:
|
|
||||||
case OT_USERPOINTER:
|
|
||||||
case OT_THREAD:
|
|
||||||
case OT_FUNCPROTO:
|
|
||||||
case OT_CLASS:
|
|
||||||
case OT_OUTER:
|
|
||||||
return sq_throwerrorf(vm, _SC("Type (%s) is not serializable"), SqTypeName(sq_gettype(vm, 2)));
|
|
||||||
}
|
|
||||||
// Serialization was successful
|
|
||||||
return SQ_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Serialize the array a specific position in the stack. Stack index must be absolute!
|
* Serialize the array a specific position in the stack. Stack index must be absolute!
|
||||||
*/
|
*/
|
||||||
SQRESULT SerializeArray(HSQUIRRELVM vm, SQInteger idx) // NOLINT(misc-no-recursion)
|
SQRESULT SerializeArray(HSQUIRRELVM vm, SQInteger idx);
|
||||||
{
|
|
||||||
// Begin array scope
|
|
||||||
OpenArray();
|
|
||||||
// Push null to initiate iteration
|
|
||||||
sq_pushnull(vm);
|
|
||||||
// So we can use absolute stack indexes to avoid errors
|
|
||||||
const auto top = sq_gettop(vm);
|
|
||||||
// Start iterating the array at the specified position in the stack
|
|
||||||
for(SQRESULT r = SQ_OK; SQ_SUCCEEDED(sq_next(vm, idx));)
|
|
||||||
{
|
|
||||||
// Attempt serialization of the currently iterated value
|
|
||||||
r = SerializeAt(vm, top + 2);
|
|
||||||
// Check for failures
|
|
||||||
if (SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
// Pop the null iterator, key and value from the stack
|
|
||||||
sq_pop(vm, 3);
|
|
||||||
// Propagate the error
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
// Pop the key and value from the stack (i.e. cleanup after `sq_next`)
|
|
||||||
sq_pop(vm, 2);
|
|
||||||
}
|
|
||||||
// Pop the null iterator
|
|
||||||
sq_poptop(vm);
|
|
||||||
// Close array scope
|
|
||||||
CloseArray();
|
|
||||||
// Serialization was successful
|
|
||||||
return SQ_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Serialize the table a specific position in the stack. Stack index must be absolute!
|
* Serialize the table a specific position in the stack. Stack index must be absolute!
|
||||||
*/
|
*/
|
||||||
SQRESULT SerializeTable(HSQUIRRELVM vm, SQInteger idx) // NOLINT(misc-no-recursion)
|
SQRESULT SerializeTable(HSQUIRRELVM vm, SQInteger idx);
|
||||||
{
|
|
||||||
// Begin object scope
|
|
||||||
OpenObject();
|
|
||||||
// Push null to initiate iteration
|
|
||||||
sq_pushnull(vm);
|
|
||||||
// So we can use absolute stack indexes to avoid errors
|
|
||||||
const auto top = sq_gettop(vm);
|
|
||||||
// Start iterating the object at the specified position in the stack
|
|
||||||
for(SQRESULT r = SQ_OK; SQ_SUCCEEDED(sq_next(vm, idx));)
|
|
||||||
{
|
|
||||||
if (sq_gettype(vm, -2) == OT_STRING)
|
|
||||||
{
|
|
||||||
// Attempt serialization of the currently iterated element key
|
|
||||||
r = SerializeAt(vm, top + 1);
|
|
||||||
// Can we proceed with the value?
|
|
||||||
if (SQ_SUCCEEDED(r))
|
|
||||||
{
|
|
||||||
// Mark the value above as the key of this element and
|
|
||||||
// attempt serialization of the currently iterated element value
|
|
||||||
r = MakeKey().SerializeAt(vm, top + 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
r = sq_throwerror(vm, _SC("Only string values are accepted as object keys"));
|
|
||||||
}
|
|
||||||
// Check for failures
|
|
||||||
if (SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
// Pop the null iterator, key and value from the stack
|
|
||||||
sq_pop(vm, 3);
|
|
||||||
// Propagate the error
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
// Pop the key and value from the stack (i.e. cleanup after `sq_next`)
|
|
||||||
sq_pop(vm, 2);
|
|
||||||
}
|
|
||||||
// Pop the null iterator
|
|
||||||
sq_poptop(vm);
|
|
||||||
// Close object scope
|
|
||||||
CloseObject();
|
|
||||||
// Serialization was successful
|
|
||||||
return SQ_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Serialize the instance a specific position in the stack. Stack index must be absolute!
|
* Serialize the instance a specific position in the stack. Stack index must be absolute!
|
||||||
*/
|
*/
|
||||||
SQRESULT SerializeInstance(HSQUIRRELVM vm, SQInteger idx)
|
SQRESULT SerializeInstance(HSQUIRRELVM vm, SQInteger idx);
|
||||||
{
|
|
||||||
return sq_throwerror(vm, _SC("Instance serialization is not yet implemented"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Serialize the weak-ref a specific position in the stack. Stack index must be absolute!
|
* Serialize the weak-ref a specific position in the stack. Stack index must be absolute!
|
||||||
*/
|
*/
|
||||||
SQRESULT SerializeWeakRef(HSQUIRRELVM vm, SQInteger idx)
|
SQRESULT SerializeWeakRef(HSQUIRRELVM vm, SQInteger idx);
|
||||||
{
|
|
||||||
SQRESULT r = sq_getweakrefval(vm, idx);
|
|
||||||
// Attempt to grab the value pointed by the weak reference
|
|
||||||
if (SQ_SUCCEEDED(r))
|
|
||||||
{
|
|
||||||
// Attempt to serialize the actual value
|
|
||||||
r = SerializeAt(vm, sq_gettop(vm));
|
|
||||||
// Pop the referenced value
|
|
||||||
sq_poptop(vm);
|
|
||||||
}
|
|
||||||
// Propagate the error, if any
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Serialize a value to the current container. It assumes an array or object is currently open.
|
* Serialize a value to the current container. It assumes an array or object is currently open.
|
||||||
*/
|
*/
|
||||||
SQRESULT PushValues(HSQUIRRELVM vm)
|
SQRESULT PushValues(HSQUIRRELVM vm);
|
||||||
{
|
|
||||||
// Fetch the number of objects on the stack
|
|
||||||
const auto top = sq_gettop(vm);
|
|
||||||
// Do we have a value?
|
|
||||||
if (top < 2)
|
|
||||||
{
|
|
||||||
return sq_throwerror(vm, _SC("Must specify at least one value to be pushed"));
|
|
||||||
}
|
|
||||||
// Serialize every specified argument
|
|
||||||
for (SQInteger i = 2; i <= top; ++i)
|
|
||||||
{
|
|
||||||
if (SQRESULT r = SerializeAt(vm, i); SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Allow chaining
|
|
||||||
sq_push(vm, 1);
|
|
||||||
// Specify that a value was returned
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Serialize a key/value pair to the current object. It assumes an object is currently open.
|
* Serialize a key/value pair to the current object. It assumes an object is currently open.
|
||||||
*/
|
*/
|
||||||
SQRESULT PushElement(HSQUIRRELVM vm)
|
SQRESULT PushElement(HSQUIRRELVM vm);
|
||||||
{
|
|
||||||
// Fetch the number of objects on the stack
|
|
||||||
const auto top = sq_gettop(vm);
|
|
||||||
// Do we have a value?
|
|
||||||
if (top < 3)
|
|
||||||
{
|
|
||||||
return sq_throwerrorf(vm, _SC("Not enough parameters. Received %lld but %lld needed"), top-1, 2);
|
|
||||||
}
|
|
||||||
else if (sq_gettype(vm, 2) != OT_STRING)
|
|
||||||
{
|
|
||||||
return sq_throwerrorf(vm, _SC("Element key must be a string"));
|
|
||||||
}
|
|
||||||
// Attempt serialization of the currently iterated element key
|
|
||||||
if (SQRESULT r = SerializeAt(vm, 2); SQ_SUCCEEDED(r))
|
|
||||||
{
|
|
||||||
// Mark the value above as the key of this element and
|
|
||||||
// attempt serialization of the currently iterated element value
|
|
||||||
r = MakeKey().SerializeAt(vm, 3);
|
|
||||||
// Check for failures
|
|
||||||
if (SQ_FAILED(r))
|
|
||||||
{
|
|
||||||
return r; // Propagate the error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Allow chaining
|
|
||||||
sq_push(vm, 1);
|
|
||||||
// Specify that a value was returned
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Push a key in the output. It assumes an object was open and previous element closed properly.
|
* Push a key in the output. It assumes an object was open and previous element closed properly.
|
||||||
*/
|
*/
|
||||||
CtxJSON & PushKey(StackStrF & key)
|
CtxJSON & PushKey(StackStrF & key);
|
||||||
{
|
|
||||||
// Validate the string value
|
|
||||||
if (key.mLen >= 0 && SQ_SUCCEEDED(key.mRes))
|
|
||||||
{
|
|
||||||
PushString(key.mPtr, static_cast< size_t >(key.mLen));
|
|
||||||
MakeKey();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
STHROWF("Invalid object key");
|
|
||||||
}
|
|
||||||
// Allow chaining
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Write a null value to the output.
|
* Write a null value to the output.
|
||||||
*/
|
*/
|
||||||
void PushNull()
|
void PushNull();
|
||||||
{
|
|
||||||
mOutput.append("null,");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Write an integer value to the output.
|
* Write an integer value to the output.
|
||||||
*/
|
*/
|
||||||
void PushInteger(SQInteger value)
|
void PushInteger(SQInteger value);
|
||||||
{
|
|
||||||
fmt::format_int f(value);
|
|
||||||
// Append the formatted integer to the buffer
|
|
||||||
mOutput.append(f.data(), f.size());
|
|
||||||
mOutput.push_back(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Write a single precision floating point value to the output.
|
* Write a single precision floating point value to the output.
|
||||||
*/
|
*/
|
||||||
void PushFloat(float value)
|
void PushFloat(float value);
|
||||||
{
|
|
||||||
fmt::format_to(std::back_inserter(mOutput), "{},", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Write a double precision floating point value to the output.
|
* Write a double precision floating point value to the output.
|
||||||
*/
|
*/
|
||||||
void PushDouble(double value)
|
void PushDouble(double value);
|
||||||
{
|
|
||||||
fmt::format_to(std::back_inserter(mOutput), "{},", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Write a boolean value to the output.
|
* Write a boolean value to the output.
|
||||||
*/
|
*/
|
||||||
void PushBool(bool value)
|
void PushBool(bool value);
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
mOutput.append("true,", 5);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mOutput.append("false,", 6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Write a string value to the output.
|
* Write a string value to the output.
|
||||||
*/
|
*/
|
||||||
void PushString(const SQChar * str)
|
void PushString(const SQChar * str);
|
||||||
{
|
|
||||||
mOutput.push_back('"');
|
|
||||||
mOutput.append(str);
|
|
||||||
mOutput.push_back('"');
|
|
||||||
mOutput.push_back(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Write a string value to the output.
|
* Write a string value to the output.
|
||||||
*/
|
*/
|
||||||
void PushString(const SQChar * str, size_t length)
|
void PushString(const SQChar * str, size_t length);
|
||||||
{
|
|
||||||
mOutput.push_back('"');
|
|
||||||
mOutput.append(str, length);
|
|
||||||
mOutput.push_back('"');
|
|
||||||
mOutput.push_back(',');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Namespace:: SqMod
|
} // Namespace:: SqMod
|
||||||
|
@ -195,21 +195,7 @@ struct LightObj {
|
|||||||
///
|
///
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
template<class T, class... A>
|
template<class T, class... A>
|
||||||
LightObj(SqTypeIdentity< T > SQ_UNUSED_ARG(t), HSQUIRRELVM vm, A&&... a) {
|
LightObj(SqTypeIdentity< T > SQ_UNUSED_ARG(t), HSQUIRRELVM vm, A&&... a) : LightObj(DeleteGuard< T >(new T(std::forward< A >(a)...))) {
|
||||||
// Create the instance and guard it to make sure it gets deleted in case of exceptions
|
|
||||||
DeleteGuard< T > instance(new T(std::forward< A >(a)...));
|
|
||||||
// Preserve the stack state
|
|
||||||
const StackGuard sg(vm);
|
|
||||||
// Push the instance on the stack
|
|
||||||
ClassType<T>::PushInstance(vm, instance);
|
|
||||||
// Attempt to retrieve it
|
|
||||||
if (SQ_FAILED(sq_getstackobj(vm, -1, &mObj))) {
|
|
||||||
sq_resetobject(&mObj);
|
|
||||||
} else {
|
|
||||||
sq_addref(vm, &mObj);
|
|
||||||
}
|
|
||||||
// Stop guarding the instance
|
|
||||||
instance.Release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -246,7 +232,21 @@ struct LightObj {
|
|||||||
///
|
///
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
template<class T>
|
template<class T>
|
||||||
LightObj(DeleteGuard<T> guard, HSQUIRRELVM v = SqVM()) : LightObj(guard.Get(), v) {
|
LightObj(DeleteGuard<T> && guard, HSQUIRRELVM v = SqVM()) : LightObj(guard.Get(), v) {
|
||||||
|
guard.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Constructs an LightObj from a C++ instance wrapped inside a DeleteGuard
|
||||||
|
///
|
||||||
|
/// \param instance Pointer to a C++ class instance that has been bound already
|
||||||
|
/// \param v VM that the object will exist in
|
||||||
|
///
|
||||||
|
/// \tparam T Type of instance
|
||||||
|
///
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class T>
|
||||||
|
LightObj(DeleteGuard<T> & guard, HSQUIRRELVM v = SqVM()) : LightObj(guard.Get(), v) {
|
||||||
guard.Release();
|
guard.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user