/**
* vim: set ts=4 :
* =============================================================================
* sm-json
* Provides a pure SourcePawn implementation of JSON encoding and decoding.
* https://github.com/clugg/sm-json
*
* sm-json (C)2018 James D. (clug)
* SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or .
*/
#if defined _json_decode_helpers_included
#endinput
#endif
#define _json_decode_helpers_included
#include
/**
* @section Analysing format of incoming JSON cells.
*/
/**
* Checks whether the character at the given
* position in the buffer is whitespace.
*
* @param buffer String buffer of data.
* @param pos Position to check in buffer.
* @return True if buffer[pos] is whitespace, false otherwise.
*/
stock bool json_is_whitespace(const char[] buffer, int &pos) {
return buffer[pos] == ' ' || buffer[pos] == '\t' ||
buffer[pos] == '\r' || buffer[pos] == '\n';
}
/**
* Checks whether the character at the beginning
* of the buffer is the start of a string.
*
* @param buffer String buffer of data.
* @return True if buffer[0] is the start of a string, false otherwise.
*/
stock bool json_is_string(const char[] buffer) {
return buffer[0] == '"';
}
/**
* Checks whether the buffer provided contains an int.
*
* @param buffer String buffer of data.
* @return True if buffer contains an int, false otherwise.
*/
stock bool json_is_int(const char[] buffer) {
int length = strlen(buffer);
if (buffer[0] != '+' && buffer[0] != '-' && !IsCharNumeric(buffer[0])) {
return false;
}
for (int i = 0; i < length; ++i) {
if (!IsCharNumeric(buffer[i])) return false;
}
return true;
}
/**
* Checks whether the buffer provided contains a float.
*
* @param buffer String buffer of data.
* @return True if buffer contains a float, false otherwise.
*/
stock bool json_is_float(const char[] buffer) {
bool has_decimal = false;
int length = strlen(buffer);
if (buffer[0] != '+' && buffer[0] != '-' && buffer[0] != '.' && !IsCharNumeric(buffer[0])) {
return false;
}
for (int i = 0; i < length; ++i) {
if (buffer[i] == '.') {
if (has_decimal) {
return false;
}
has_decimal = true;
} else if (!IsCharNumeric(buffer[i])) {
return false;
}
}
return true;
}
/**
* Checks whether the buffer provided contains a bool.
*
* @param buffer String buffer of data.
* @return True if buffer contains a bool, false otherwise.
*/
stock bool json_is_bool(const char[] buffer) {
return StrEqual(buffer, "true") ||
StrEqual(buffer, "false");
}
/**
* Checks whether the buffer provided contains null.
*
* @param buffer String buffer of data.
* @return True if buffer contains null, false otherwise.
*/
stock bool json_is_null(const char[] buffer) {
return StrEqual(buffer, "null");
}
/**
* Checks whether the character at the beginning
* of the buffer is the start of an object.
*
* @param buffer String buffer of data.
* @return True if buffer[0] is the start of an object, false otherwise.
*/
stock bool json_is_object(const char[] buffer) {
return buffer[0] == '{';
}
/**
* Checks whether the character at the beginning
* of the buffer is the end of an object.
*
* @param buffer String buffer of data.
* @return True if buffer[0] is the end of an object, false otherwise.
*/
stock bool json_is_object_end(const char[] buffer) {
return buffer[0] == '}';
}
/**
* Checks whether the character at the beginning
* of the buffer is the start of an array.
*
* @param buffer String buffer of data.
* @return True if buffer[0] is the start of an array, false otherwise.
*/
stock bool json_is_array(const char[] buffer) {
return buffer[0] == '[';
}
/**
* Checks whether the character at the beginning
* of the buffer is the start of an array.
*
* @param buffer String buffer of data.
* @return True if buffer[0] is the start of an array, false otherwise.
*/
stock bool json_is_array_end(const char[] buffer) {
return buffer[0] == ']';
}
/**
* Checks whether the character at the given position in the buffer
* is considered a valid 'end point' for some data, such as a
* colon (indicating a key), a comma (indicating a new element),
* or the end of an object or array.
*
* @param buffer String buffer of data.
* @param pos Position to check in buffer.
* @return True if buffer[pos] is a valid data end point, false otherwise.
*/
stock bool json_is_at_end(const char[] buffer, int &pos, bool is_array) {
return buffer[pos] == ',' ||
(!is_array && buffer[pos] == ':') ||
json_is_object_end(buffer[pos]) ||
json_is_array_end(buffer[pos]);
}
/**
* Moves the position until it reaches a non-whitespace
* character or the end of the buffer's maximum size.
*
* @param buffer String buffer of data.
* @param maxlen Maximum size of string buffer.
* @param pos Position to increment.
* @return True if pos is not at the end of the buffer, false otherwise.
*/
stock bool json_skip_whitespace(const char[] buffer, int maxlen, int &pos) {
while (json_is_whitespace(buffer, pos) && pos < maxlen) {
++pos;
}
return pos < maxlen;
}
/**
* Extracts a JSON cell from the buffer until
* a valid end point is reached.
*
* @param buffer String buffer of data.
* @param maxlen Maximum size of string buffer.
* @param pos Position to increment.
* @param output String buffer to store output.
* @param output_maxlen Maximum size of output string buffer.
* @param is_array Whether the decoder is currently processing an array.
* @return True if pos is not at the end of the buffer, false otherwise.
*/
stock bool json_extract_until_end(const char[] buffer, int maxlen, int &pos, char[] output, int output_maxlen, bool is_array) {
// extracts a string from current pos until a valid 'end point'
strcopy(output, output_maxlen, "");
int start = pos;
while (!json_is_whitespace(buffer, pos) && !json_is_at_end(buffer, pos, is_array) && pos < maxlen) {
++pos;
}
int end = pos - 1;
// skip trailing whitespace
json_skip_whitespace(buffer, maxlen, pos);
if (!json_is_at_end(buffer, pos, is_array)) return false;
strcopy(output, end - start + 2, buffer[start]);
return pos < maxlen;
}
/**
* Extracts a JSON string from the buffer until
* a valid end point is reached.
*
* @param buffer String buffer of data.
* @param maxlen Maximum size of string buffer.
* @param pos Position to increment.
* @param output String buffer to store output.
* @param output_maxlen Maximum size of output string buffer.
* @param is_array Whether the decoder is currently processing an array.
* @return True if pos is not at the end of the buffer, false otherwise.
*/
stock bool json_extract_string(const char[] buffer, int maxlen, int &pos, char[] output, int output_maxlen, bool is_array) {
// extracts a string which needs to be quote-escaped
strcopy(output, output_maxlen, "");
++pos;
int start = pos;
while (!(buffer[pos] == '"' && buffer[pos - 1] != '\\') && pos < maxlen) {
++pos;
}
int end = pos - 1;
// jump 1 ahead since we ended on " instead of an ending char
++pos;
// skip trailing whitespace
json_skip_whitespace(buffer, maxlen, pos);
if (!json_is_at_end(buffer, pos, is_array)) return false;
// copy only from start with length end - start + 2 (+2 for NULL terminator and something else)
strcopy(output, end - start + 2, buffer[start]);
json_unescape_string(output, maxlen);
return pos < maxlen;
}
/**
* Extracts an int from the buffer.
*
* @param buffer String buffer of data.
* @return Int value of the buffer.
*/
stock int json_extract_int(const char[] buffer) {
return StringToInt(buffer);
}
/**
* Extracts a float from the buffer.
*
* @param buffer String buffer of data.
* @return Float value of the buffer.
*/
stock float json_extract_float(const char[] buffer) {
return StringToFloat(buffer);
}
/**
* Extracts a bool from the buffer.
*
* @param buffer String buffer of data.
* @return Bool value of the buffer.
*/
stock bool json_extract_bool(const char[] buffer) {
return StrEqual(buffer, "true");
}