mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 00:37:15 +01:00
Implement JSON serialization.
Just the basics.
This commit is contained in:
parent
3e75e36cdf
commit
42ac0e32b7
@ -10,14 +10,23 @@
|
|||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
namespace SqMod {
|
namespace SqMod {
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
SQMOD_DECL_TYPENAME(SqCtxJSON, _SC("SqCtxJSON"))
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
static SQInteger SqToJSON(HSQUIRRELVM vm) noexcept
|
static SQInteger SqToJSON(HSQUIRRELVM vm) noexcept
|
||||||
{
|
{
|
||||||
return sq_throwerror(vm, _SC("Not implemented yet!"));
|
return CtxJSON().SerializeParams(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
static SQInteger SqFromJson_Push(HSQUIRRELVM vm, const sajson::value & node) noexcept
|
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)
|
||||||
{
|
{
|
||||||
// Operation result
|
// Operation result
|
||||||
SQInteger r = SQ_OK;
|
SQInteger r = SQ_OK;
|
||||||
@ -155,7 +164,34 @@ static SQInteger SqFromJSON(HSQUIRRELVM vm) noexcept
|
|||||||
void Register_JSON(HSQUIRRELVM vm)
|
void Register_JSON(HSQUIRRELVM vm)
|
||||||
{
|
{
|
||||||
RootTable(vm).SquirrelFunc(_SC("SqToJSON"), SqToJSON);
|
RootTable(vm).SquirrelFunc(_SC("SqToJSON"), SqToJSON);
|
||||||
|
RootTable(vm).SquirrelFunc(_SC("SqToCompactJSON"), SqToCompactJSON);
|
||||||
RootTable(vm).SquirrelFunc(_SC("SqFromJSON"), SqFromJSON);
|
RootTable(vm).SquirrelFunc(_SC("SqFromJSON"), SqFromJSON);
|
||||||
|
// --------------------------------------------------------------------------------------------
|
||||||
|
RootTable(vm).Bind(_SC("SqCtxJSON"),
|
||||||
|
Class< CtxJSON, NoCopy< CtxJSON > >(vm, SqCtxJSON::Str)
|
||||||
|
// Constructors
|
||||||
|
.Ctor()
|
||||||
|
.Ctor< bool >()
|
||||||
|
// Meta-methods
|
||||||
|
.SquirrelFunc(_SC("_typename"), &SqCtxJSON::Fn)
|
||||||
|
// Properties
|
||||||
|
.Prop(_SC("Output"), &CtxJSON::GetOutput)
|
||||||
|
.Prop(_SC("Depth"), &CtxJSON::GetDepth)
|
||||||
|
.Prop(_SC("OOA"), &CtxJSON::GetObjectOverArray, &CtxJSON::SetObjectOverArray)
|
||||||
|
.Prop(_SC("ObjectOverArray"), &CtxJSON::GetObjectOverArray, &CtxJSON::SetObjectOverArray)
|
||||||
|
// Member Methods
|
||||||
|
.SquirrelMethod< CtxJSON, &CtxJSON::SerializeParams >(_SC("Serialize"))
|
||||||
|
.SquirrelMethod< CtxJSON, &CtxJSON::PushValues >(_SC("PushValues"))
|
||||||
|
.SquirrelMethod< CtxJSON, &CtxJSON::PushElement >(_SC("PushElement"))
|
||||||
|
.Func(_SC("OpenArray"), &CtxJSON::OpenArray)
|
||||||
|
.Func(_SC("CloseArray"), &CtxJSON::CloseArray)
|
||||||
|
.Func(_SC("OpenObject"), &CtxJSON::OpenObject)
|
||||||
|
.Func(_SC("CloseObject"), &CtxJSON::CloseObject)
|
||||||
|
.Func(_SC("MakeKey"), &CtxJSON::MakeKey)
|
||||||
|
.FmtFunc(_SC("PushKey"), &CtxJSON::PushKey)
|
||||||
|
.Func(_SC("SetOOA"), &CtxJSON::SetObjectOverArray)
|
||||||
|
.Func(_SC("SetObjectOverArray"), &CtxJSON::SetObjectOverArray)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Namespace:: SqMod
|
} // Namespace:: SqMod
|
||||||
|
@ -6,11 +6,637 @@
|
|||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
#include <sajson.h>
|
#include <sajson.h>
|
||||||
|
#include <fmt/args.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <fmt/xchar.h>
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
namespace SqMod {
|
namespace SqMod {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------
|
||||||
|
* JSON serializer. The generated JSON output is always minified for efficiency reasons.
|
||||||
|
*/
|
||||||
|
struct CtxJSON
|
||||||
|
{
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Output string.
|
||||||
|
*/
|
||||||
|
String mOutput{};
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Prefer a table with named members even when a simple array would do the job.
|
||||||
|
* Take a Vector3 for example. Compact array [x, y, z] or named object {x: #.#, y: #.#, z: #.#}
|
||||||
|
*/
|
||||||
|
bool mObjectOverArray{true};
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* How nested are we currently.
|
||||||
|
*/
|
||||||
|
uint32_t mDepth{0};
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Default constructor.
|
||||||
|
*/
|
||||||
|
CtxJSON() noexcept = default;
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Explicit constructor.
|
||||||
|
*/
|
||||||
|
explicit CtxJSON(bool ooa) noexcept
|
||||||
|
: CtxJSON()
|
||||||
|
{
|
||||||
|
mObjectOverArray = ooa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Copy constructor.
|
||||||
|
*/
|
||||||
|
CtxJSON(const CtxJSON &) = default;
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Move constructor.
|
||||||
|
*/
|
||||||
|
CtxJSON(CtxJSON &&) noexcept = default;
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~CtxJSON() = default;
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Copy assignment operator.
|
||||||
|
*/
|
||||||
|
CtxJSON & operator = (const CtxJSON &) = default;
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Move assignment operator.
|
||||||
|
*/
|
||||||
|
CtxJSON & operator = (CtxJSON &&) noexcept = default;
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Retrieve the current depth.
|
||||||
|
*/
|
||||||
|
SQMOD_NODISCARD const String & GetOutput() const noexcept
|
||||||
|
{
|
||||||
|
return mOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Retrieve the current depth.
|
||||||
|
*/
|
||||||
|
SQMOD_NODISCARD SQInteger GetDepth() const noexcept
|
||||||
|
{
|
||||||
|
return mDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Retrieve whether objects are preferred over arrays.
|
||||||
|
*/
|
||||||
|
SQMOD_NODISCARD bool GetObjectOverArray() const noexcept
|
||||||
|
{
|
||||||
|
return mObjectOverArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Retrieve whether objects are preferred over arrays.
|
||||||
|
*/
|
||||||
|
CtxJSON & SetObjectOverArray(bool toggle) noexcept
|
||||||
|
{
|
||||||
|
mObjectOverArray = toggle;
|
||||||
|
// Allow chaining
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Increase indentation by one level.
|
||||||
|
*/
|
||||||
|
void Advance() noexcept
|
||||||
|
{
|
||||||
|
++mDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Decrease indentation by one level.
|
||||||
|
*/
|
||||||
|
void Retreat() noexcept
|
||||||
|
{
|
||||||
|
assert(mDepth > 0);
|
||||||
|
if (mDepth) --mDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Begin writing an array.
|
||||||
|
*/
|
||||||
|
CtxJSON & OpenArray()
|
||||||
|
{
|
||||||
|
// Add the array-begin character
|
||||||
|
mOutput.push_back('[');
|
||||||
|
// Go forward one level
|
||||||
|
Advance();
|
||||||
|
// Allow chaining
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Stop writing an array.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
CtxJSON & OpenObject()
|
||||||
|
{
|
||||||
|
// Add the object-begin character
|
||||||
|
mOutput.push_back('{');
|
||||||
|
// Go forward one level
|
||||||
|
Advance();
|
||||||
|
// Allow chaining
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Stop writing an object.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
SQRESULT 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Serialize the array a specific position in the stack. Stack index must be absolute!
|
||||||
|
*/
|
||||||
|
SQRESULT 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Serialize the table a specific position in the stack. Stack index must be absolute!
|
||||||
|
*/
|
||||||
|
SQRESULT 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Serialize the instance a specific position in the stack. Stack index must be absolute!
|
||||||
|
*/
|
||||||
|
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!
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
void PushNull()
|
||||||
|
{
|
||||||
|
mOutput.append("null,");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Write an integer value to the output.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
void PushFloat(float value)
|
||||||
|
{
|
||||||
|
fmt::format_to(std::back_inserter(mOutput), "{},", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Write a double precision floating point value to the output.
|
||||||
|
*/
|
||||||
|
void PushDouble(double value)
|
||||||
|
{
|
||||||
|
fmt::format_to(std::back_inserter(mOutput), "{},", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Write a boolean value to the output.
|
||||||
|
*/
|
||||||
|
void PushBool(bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
mOutput.append("true,", 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mOutput.append("false,", 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Write a string value to the output.
|
||||||
|
*/
|
||||||
|
void PushString(const SQChar * str)
|
||||||
|
{
|
||||||
|
mOutput.push_back('"');
|
||||||
|
mOutput.append(str);
|
||||||
|
mOutput.push_back('"');
|
||||||
|
mOutput.push_back(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------
|
||||||
|
* Write a string value to the output.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user