1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +01:00

Compare commits

...

4 Commits

Author SHA1 Message Date
Sandu Liviu Catalin
5f20ffc4de Remove the manual comma. 2022-09-17 23:49:37 +03:00
Sandu Liviu Catalin
08106156c4 Don't add comma for the root container. 2022-09-17 23:42:48 +03:00
Sandu Liviu Catalin
60119ff8fa Include comma after separator braces. 2022-09-17 23:41:43 +03:00
Sandu Liviu Catalin
f4720ae77a Implement custom types in JSON. 2022-09-17 23:25:16 +03:00
3 changed files with 556 additions and 453 deletions

View File

@ -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(']');
}
// Move the comma after the closing character
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('}');
}
// Move the comma after the closing character
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
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
}
} break;
case OT_ARRAY: {
if (SQRESULT r = SerializeArray(vm, idx); SQ_FAILED(r))
{
return r; // Propagate the error
}
} 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)
{ {

View File

@ -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

View File

@ -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();
} }