mirror of
https://github.com/libretro/libretro-common.git
synced 2024-11-23 16:19:50 +00:00
(libretro-common) Updates
This commit is contained in:
parent
d55d7d31b9
commit
c51927f872
@ -1,316 +0,0 @@
|
|||||||
/* Copyright (C) 2010-2020 The RetroArch team
|
|
||||||
*
|
|
||||||
* ---------------------------------------------------------------------------------------
|
|
||||||
* The following license statement only applies to this file (jsonsax.c).
|
|
||||||
* ---------------------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge,
|
|
||||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
|
||||||
* to deal in the Software without restriction, including without limitation the rights to
|
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
|
||||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <setjmp.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#include <retro_inline.h>
|
|
||||||
#include <formats/jsonsax.h>
|
|
||||||
|
|
||||||
#ifdef JSONSAX_ERRORS
|
|
||||||
const char* jsonsax_errors[] =
|
|
||||||
{
|
|
||||||
"Ok",
|
|
||||||
"Interrupted",
|
|
||||||
"Missing key",
|
|
||||||
"Unterminated key",
|
|
||||||
"Missing value",
|
|
||||||
"Unterminated object",
|
|
||||||
"Unterminated array",
|
|
||||||
"Unterminated string",
|
|
||||||
"Invalid value"
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
const jsonsax_handlers_t* handlers;
|
|
||||||
|
|
||||||
const char* json;
|
|
||||||
void* ud;
|
|
||||||
jmp_buf env;
|
|
||||||
}
|
|
||||||
state_t;
|
|
||||||
|
|
||||||
static INLINE void skip_spaces( state_t* state )
|
|
||||||
{
|
|
||||||
while ( isspace( (unsigned char)*state->json ) )
|
|
||||||
state->json++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static INLINE void skip_digits( state_t* state )
|
|
||||||
{
|
|
||||||
while ( isdigit( (unsigned char)*state->json ) )
|
|
||||||
state->json++;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define HANDLE_0( event ) \
|
|
||||||
do { \
|
|
||||||
if ( state->handlers->event && state->handlers->event( state->ud ) ) \
|
|
||||||
longjmp( state->env, JSONSAX_INTERRUPTED ); \
|
|
||||||
} while ( 0 )
|
|
||||||
|
|
||||||
#define HANDLE_1( event, arg1 ) \
|
|
||||||
do { \
|
|
||||||
if ( state->handlers->event && state->handlers->event( state->ud, arg1 ) ) \
|
|
||||||
longjmp( state->env, JSONSAX_INTERRUPTED ); \
|
|
||||||
} while ( 0 )
|
|
||||||
|
|
||||||
#define HANDLE_2( event, arg1, arg2 ) \
|
|
||||||
do { \
|
|
||||||
if ( state->handlers->event && state->handlers->event( state->ud, arg1, arg2 ) ) \
|
|
||||||
longjmp( state->env, JSONSAX_INTERRUPTED ); \
|
|
||||||
} while ( 0 )
|
|
||||||
|
|
||||||
static void jsonx_parse_value(state_t* state);
|
|
||||||
|
|
||||||
static void jsonx_parse_object( state_t* state )
|
|
||||||
{
|
|
||||||
state->json++; /* we're sure the current character is a '{' */
|
|
||||||
skip_spaces( state );
|
|
||||||
HANDLE_0( start_object );
|
|
||||||
|
|
||||||
while ( *state->json != '}' )
|
|
||||||
{
|
|
||||||
const char *name = NULL;
|
|
||||||
if ( *state->json != '"' )
|
|
||||||
longjmp( state->env, JSONSAX_MISSING_KEY );
|
|
||||||
|
|
||||||
name = ++state->json;
|
|
||||||
|
|
||||||
for ( ;; )
|
|
||||||
{
|
|
||||||
const char* quote = strchr( state->json, '"' );
|
|
||||||
|
|
||||||
if ( !quote )
|
|
||||||
longjmp( state->env, JSONSAX_UNTERMINATED_KEY );
|
|
||||||
|
|
||||||
state->json = quote + 1;
|
|
||||||
|
|
||||||
if ( quote[ -1 ] != '\\' )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE_2( key, name, state->json - name - 1 );
|
|
||||||
skip_spaces( state );
|
|
||||||
|
|
||||||
if ( *state->json != ':' )
|
|
||||||
longjmp( state->env, JSONSAX_MISSING_VALUE );
|
|
||||||
|
|
||||||
state->json++;
|
|
||||||
skip_spaces( state );
|
|
||||||
jsonx_parse_value( state );
|
|
||||||
skip_spaces( state );
|
|
||||||
|
|
||||||
if ( *state->json != ',' )
|
|
||||||
break;
|
|
||||||
|
|
||||||
state->json++;
|
|
||||||
skip_spaces( state );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( *state->json != '}' )
|
|
||||||
longjmp( state->env, JSONSAX_UNTERMINATED_OBJECT );
|
|
||||||
|
|
||||||
state->json++;
|
|
||||||
HANDLE_0( end_object );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jsonx_parse_array(state_t* state)
|
|
||||||
{
|
|
||||||
unsigned int ndx = 0;
|
|
||||||
|
|
||||||
state->json++; /* we're sure the current character is a '[' */
|
|
||||||
skip_spaces( state );
|
|
||||||
HANDLE_0( start_array );
|
|
||||||
|
|
||||||
while ( *state->json != ']' )
|
|
||||||
{
|
|
||||||
HANDLE_1( array_index, ndx++ );
|
|
||||||
jsonx_parse_value( state );
|
|
||||||
skip_spaces( state );
|
|
||||||
|
|
||||||
if ( *state->json != ',' )
|
|
||||||
break;
|
|
||||||
|
|
||||||
state->json++;
|
|
||||||
skip_spaces( state );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( *state->json != ']' )
|
|
||||||
longjmp( state->env, JSONSAX_UNTERMINATED_ARRAY );
|
|
||||||
|
|
||||||
state->json++;
|
|
||||||
HANDLE_0( end_array );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jsonx_parse_string(state_t* state)
|
|
||||||
{
|
|
||||||
const char* string = ++state->json;
|
|
||||||
|
|
||||||
for ( ;; )
|
|
||||||
{
|
|
||||||
const char* quote = strchr( state->json, '"' );
|
|
||||||
|
|
||||||
if ( !quote )
|
|
||||||
longjmp( state->env, JSONSAX_UNTERMINATED_STRING );
|
|
||||||
|
|
||||||
state->json = quote + 1;
|
|
||||||
|
|
||||||
if ( quote[ -1 ] != '\\' )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE_2( string, string, state->json - string - 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jsonx_parse_boolean(state_t* state)
|
|
||||||
{
|
|
||||||
if ( !strncmp( state->json, "true", 4 ) )
|
|
||||||
{
|
|
||||||
state->json += 4;
|
|
||||||
HANDLE_1( boolean, 1 );
|
|
||||||
}
|
|
||||||
else if ( !strncmp( state->json, "false", 5 ) )
|
|
||||||
{
|
|
||||||
state->json += 5;
|
|
||||||
HANDLE_1( boolean, 0 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jsonx_parse_null(state_t* state)
|
|
||||||
{
|
|
||||||
if ( !strncmp( state->json + 1, "ull", 3 ) ) /* we're sure the current character is a 'n' */
|
|
||||||
{
|
|
||||||
state->json += 4;
|
|
||||||
HANDLE_0( null );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jsonx_parse_number(state_t* state)
|
|
||||||
{
|
|
||||||
const char* number = state->json;
|
|
||||||
|
|
||||||
if ( *state->json == '-' )
|
|
||||||
state->json++;
|
|
||||||
|
|
||||||
if ( !isdigit( (unsigned char)*state->json ) )
|
|
||||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
|
||||||
|
|
||||||
skip_digits( state );
|
|
||||||
|
|
||||||
if ( *state->json == '.' )
|
|
||||||
{
|
|
||||||
state->json++;
|
|
||||||
|
|
||||||
if ( !isdigit( (unsigned char)*state->json ) )
|
|
||||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
|
||||||
|
|
||||||
skip_digits( state );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( *state->json == 'e' || *state->json == 'E' )
|
|
||||||
{
|
|
||||||
state->json++;
|
|
||||||
|
|
||||||
if ( *state->json == '-' || *state->json == '+' )
|
|
||||||
state->json++;
|
|
||||||
|
|
||||||
if ( !isdigit( (unsigned char)*state->json ) )
|
|
||||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
|
||||||
|
|
||||||
skip_digits( state );
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE_2( number, number, state->json - number );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jsonx_parse_value(state_t* state)
|
|
||||||
{
|
|
||||||
skip_spaces( state );
|
|
||||||
|
|
||||||
switch ( *state->json )
|
|
||||||
{
|
|
||||||
case '{':
|
|
||||||
jsonx_parse_object(state);
|
|
||||||
break;
|
|
||||||
case '[':
|
|
||||||
jsonx_parse_array( state );
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
jsonx_parse_string( state );
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
case 'f':
|
|
||||||
jsonx_parse_boolean( state );
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
jsonx_parse_null( state );
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
case '-':
|
|
||||||
jsonx_parse_number( state );
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
longjmp( state->env, JSONSAX_INVALID_VALUE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int jsonsax_parse( const char* json, const jsonsax_handlers_t* handlers, void* userdata )
|
|
||||||
{
|
|
||||||
state_t state;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
state.json = json;
|
|
||||||
state.handlers = handlers;
|
|
||||||
state.ud = userdata;
|
|
||||||
|
|
||||||
if ( ( res = setjmp( state.env ) ) == 0 )
|
|
||||||
{
|
|
||||||
if ( handlers->start_document )
|
|
||||||
handlers->start_document( userdata );
|
|
||||||
|
|
||||||
jsonx_parse_value(&state);
|
|
||||||
|
|
||||||
if ( handlers->end_document )
|
|
||||||
handlers->end_document( userdata );
|
|
||||||
|
|
||||||
res = JSONSAX_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
1385
formats/json/rjson.c
Normal file
1385
formats/json/rjson.c
Normal file
File diff suppressed because it is too large
Load Diff
263
include/formats/rjson.h
Normal file
263
include/formats/rjson.h
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/* Copyright (C) 2010-2020 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rjson.h).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge,
|
||||||
|
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBRETRO_SDK_FORMAT_RJSON_H__
|
||||||
|
#define __LIBRETRO_SDK_FORMAT_RJSON_H__
|
||||||
|
|
||||||
|
#include <retro_common_api.h>
|
||||||
|
#include <retro_inline.h> /* INLINE */
|
||||||
|
#include <boolean.h> /* bool */
|
||||||
|
#include <stddef.h> /* size_t */
|
||||||
|
|
||||||
|
RETRO_BEGIN_DECLS
|
||||||
|
|
||||||
|
/* List of possible element types returned by rjson_next */
|
||||||
|
enum rjson_type
|
||||||
|
{
|
||||||
|
RJSON_DONE,
|
||||||
|
RJSON_OBJECT, RJSON_ARRAY, RJSON_OBJECT_END, RJSON_ARRAY_END,
|
||||||
|
RJSON_STRING, RJSON_NUMBER, RJSON_TRUE, RJSON_FALSE, RJSON_NULL,
|
||||||
|
RJSON_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Options that can be passed to rjson_set_options */
|
||||||
|
enum rjson_option
|
||||||
|
{
|
||||||
|
/* Allow UTF-8 byte order marks */
|
||||||
|
RJSON_OPTION_ALLOW_UTF8BOM = (1<<0),
|
||||||
|
/* Allow JavaScript style comments in the stream */
|
||||||
|
RJSON_OPTION_ALLOW_COMMENTS = (1<<1),
|
||||||
|
/* Allow unescaped control characters in strings (bytes 0x00 - 0x1F) */
|
||||||
|
RJSON_OPTION_ALLOW_UNESCAPED_CONTROL_CHARACTERS = (1<<2),
|
||||||
|
/* Ignore invalid Unicode escapes and don't validate UTF-8 codes */
|
||||||
|
RJSON_OPTION_IGNORE_INVALID_ENCODING = (1<<3),
|
||||||
|
/* Replace invalid Unicode escapes and UTF-8 codes with a '?' character */
|
||||||
|
RJSON_OPTION_REPLACE_INVALID_ENCODING = (1<<4),
|
||||||
|
/* Ignore carriage return (\r escape sequence) in strings */
|
||||||
|
RJSON_OPTION_IGNORE_STRING_CARRIAGE_RETURN = (1<<5),
|
||||||
|
/* Allow data after the end of the top JSON object/array/value */
|
||||||
|
RJSON_OPTION_ALLOW_TRAILING_DATA = (1<<6)
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Custom data input callback
|
||||||
|
* Should return > 0 and <= len on success, 0 on file end and < 0 on error. */
|
||||||
|
typedef int (*rjson_io_t)(void* buf, int len, void *user_data);
|
||||||
|
typedef struct rjson rjson_t;
|
||||||
|
struct intfstream_internal;
|
||||||
|
struct RFILE;
|
||||||
|
|
||||||
|
/* Create a new parser instance from various sources */
|
||||||
|
rjson_t *rjson_open_stream(struct intfstream_internal *stream);
|
||||||
|
rjson_t *rjson_open_rfile(struct RFILE *rfile);
|
||||||
|
rjson_t *rjson_open_buffer(const void *buffer, size_t size);
|
||||||
|
rjson_t *rjson_open_string(const char *string);
|
||||||
|
rjson_t *rjson_open_user(rjson_io_t io, void *user_data, int io_block_size);
|
||||||
|
|
||||||
|
/* Free the parser instance created with rjson_open_* */
|
||||||
|
void rjson_free(rjson_t *json);
|
||||||
|
|
||||||
|
/* Set one or more enum rjson_option, will override previously set options.
|
||||||
|
* Use bitwise OR to concatenate multiple options.
|
||||||
|
* By default none of the options are set. */
|
||||||
|
void rjson_set_options(rjson_t *json, char rjson_option_flags);
|
||||||
|
|
||||||
|
/* Sets the maximum context depth, recursion inside arrays and objects.
|
||||||
|
* By default this is set to 50. */
|
||||||
|
void rjson_set_max_depth(rjson_t *json, unsigned int max_depth);
|
||||||
|
|
||||||
|
/* Parse to the next JSON element and return the type of it.
|
||||||
|
* Will return RJSON_DONE when successfully reaching the end or
|
||||||
|
* RJSON_ERROR when an error was encountered. */
|
||||||
|
enum rjson_type rjson_next(rjson_t *json);
|
||||||
|
|
||||||
|
/* Get the current string, null-terminated unescaped UTF-8 encoded.
|
||||||
|
* Can only be used when the current element is RJSON_STRING or RJSON_NUMBER.
|
||||||
|
* The returned pointer is only valid until the parsing continues. */
|
||||||
|
const char *rjson_get_string(rjson_t *json, size_t *length);
|
||||||
|
|
||||||
|
/* Returns the current number (or string) converted to double or int */
|
||||||
|
double rjson_get_double(rjson_t *json);
|
||||||
|
int rjson_get_int(rjson_t *json);
|
||||||
|
|
||||||
|
/* Returns a string describing the error once rjson_next/rjson_parse
|
||||||
|
* has returned an unrecoverable RJSON_ERROR (otherwise returns ""). */
|
||||||
|
const char *rjson_get_error(rjson_t *json);
|
||||||
|
|
||||||
|
/* Can be used to set a custom error description on an invalid JSON structure.
|
||||||
|
* Maximum length of 79 characters and once set the parsing can't continue. */
|
||||||
|
void rjson_set_error(rjson_t *json, const char* error);
|
||||||
|
|
||||||
|
/* Functions to get the current position in the source stream as well as */
|
||||||
|
/* a bit of source json arround the current position for additional detail
|
||||||
|
* when parsing has failed with RJSON_ERROR.
|
||||||
|
* Intended to be used with printf style formatting like:
|
||||||
|
* printf("Invalid JSON at line %d, column %d - %s - Source: ...%.*s...\n",
|
||||||
|
* (int)rjson_get_source_line(json), (int)rjson_get_source_column(json),
|
||||||
|
* rjson_get_error(json), rjson_get_source_context_len(json),
|
||||||
|
* rjson_get_source_context_buf(json)); */
|
||||||
|
size_t rjson_get_source_line(rjson_t *json);
|
||||||
|
size_t rjson_get_source_column(rjson_t *json);
|
||||||
|
int rjson_get_source_context_len(rjson_t *json);
|
||||||
|
const char* rjson_get_source_context_buf(rjson_t *json);
|
||||||
|
|
||||||
|
/* Confirm the parsing context stack, for example calling
|
||||||
|
rjson_check_context(json, 2, RJSON_OBJECT, RJSON_ARRAY)
|
||||||
|
returns true when inside "{ [ ..." but not for "[ .." or "{ [ { ..." */
|
||||||
|
bool rjson_check_context(rjson_t *json, unsigned int depth, ...);
|
||||||
|
|
||||||
|
/* Returns the current level of nested objects/arrays */
|
||||||
|
unsigned int rjson_get_context_depth(rjson_t *json);
|
||||||
|
|
||||||
|
/* Return the current parsing context, that is, RJSON_OBJECT if we are inside
|
||||||
|
* an object, RJSON_ARRAY if we are inside an array, and RJSON_DONE or
|
||||||
|
* RJSON_ERROR if we are not yet/anymore in either. */
|
||||||
|
enum rjson_type rjson_get_context_type(rjson_t *json);
|
||||||
|
|
||||||
|
/* While inside an object or an array, this return the number of parsing
|
||||||
|
* events that have already been observed at this level with rjson_next.
|
||||||
|
* In particular, inside an object, an odd number would indicate that the just
|
||||||
|
* observed RJSON_STRING event is a member name. */
|
||||||
|
size_t rjson_get_context_count(rjson_t *json);
|
||||||
|
|
||||||
|
/* Parse an entire JSON stream with a list of element specific handlers.
|
||||||
|
* Each of the handlers can be passed a function or NULL to ignore it.
|
||||||
|
* If a handler returns false, the parsing will abort and the returned
|
||||||
|
* rjson_type will indicate on which element type parsing was aborted.
|
||||||
|
* Otherwise the return value will be RJSON_DONE or RJSON_ERROR. */
|
||||||
|
enum rjson_type rjson_parse(rjson_t *json, void* context,
|
||||||
|
bool (*object_member_handler)(void *context, const char *str, size_t len),
|
||||||
|
bool (*string_handler )(void *context, const char *str, size_t len),
|
||||||
|
bool (*number_handler )(void *context, const char *str, size_t len),
|
||||||
|
bool (*start_object_handler )(void *context),
|
||||||
|
bool (*end_object_handler )(void *context),
|
||||||
|
bool (*start_array_handler )(void *context),
|
||||||
|
bool (*end_array_handler )(void *context),
|
||||||
|
bool (*boolean_handler )(void *context, bool value),
|
||||||
|
bool (*null_handler )(void *context));
|
||||||
|
|
||||||
|
/* A simpler interface to parse a JSON in memory. This will avoid any memory
|
||||||
|
* allocations unless the document contains strings longer than 512 characters.
|
||||||
|
* In the error handler, error will be "" if any of the other handlers aborted. */
|
||||||
|
bool rjson_parse_quick(const char *string, void* context, char option_flags,
|
||||||
|
bool (*object_member_handler)(void *context, const char *str, size_t len),
|
||||||
|
bool (*string_handler )(void *context, const char *str, size_t len),
|
||||||
|
bool (*number_handler )(void *context, const char *str, size_t len),
|
||||||
|
bool (*start_object_handler )(void *context),
|
||||||
|
bool (*end_object_handler )(void *context),
|
||||||
|
bool (*start_array_handler )(void *context),
|
||||||
|
bool (*end_array_handler )(void *context),
|
||||||
|
bool (*boolean_handler )(void *context, bool value),
|
||||||
|
bool (*null_handler )(void *context),
|
||||||
|
void (*error_handler )(void *context, int line, int col, const char* error));
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Options that can be passed to rjsonwriter_set_options */
|
||||||
|
enum rjsonwriter_option
|
||||||
|
{
|
||||||
|
/* Don't write spaces, tabs or newlines to the output (except in strings) */
|
||||||
|
RJSONWRITER_OPTION_SKIP_WHITESPACE = (1<<0)
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Custom data output callback
|
||||||
|
* Should return len on success and < len on a write error. */
|
||||||
|
typedef int (*rjsonwriter_io_t)(const void* buf, int len, void *user_data);
|
||||||
|
typedef struct rjsonwriter rjsonwriter_t;
|
||||||
|
|
||||||
|
/* Create a new writer instance to various targets */
|
||||||
|
rjsonwriter_t *rjsonwriter_open_stream(struct intfstream_internal *stream);
|
||||||
|
rjsonwriter_t *rjsonwriter_open_rfile(struct RFILE *rfile);
|
||||||
|
rjsonwriter_t *rjsonwriter_open_user(rjsonwriter_io_t io, void *user_data);
|
||||||
|
|
||||||
|
/* Free rjsonwriter handle and return result of final rjsonwriter_flush call */
|
||||||
|
bool rjsonwriter_free(rjsonwriter_t *writer);
|
||||||
|
|
||||||
|
/* Set one or more enum rjsonwriter_option, will override previously set options.
|
||||||
|
* Use bitwise OR to concatenate multiple options.
|
||||||
|
* By default none of the options are set. */
|
||||||
|
void rjsonwriter_set_options(rjsonwriter_t *writer, int rjsonwriter_option_flags);
|
||||||
|
|
||||||
|
/* Flush any buffered output data to the output stream.
|
||||||
|
* Returns true if the data was successfully written. Once writing fails once,
|
||||||
|
* no more data will be written and flush will always returns false */
|
||||||
|
bool rjsonwriter_flush(rjsonwriter_t *writer);
|
||||||
|
|
||||||
|
/* Returns a string describing an error or "" if there was none.
|
||||||
|
* The only error possible is "output error" after the io function failed.
|
||||||
|
* If rjsonwriter_rawf were used manually, "out of memory" is also possible. */
|
||||||
|
const char *rjsonwriter_get_error(rjsonwriter_t *writer);
|
||||||
|
|
||||||
|
/* Used by the inline functions below to append raw data */
|
||||||
|
void rjsonwriter_raw(rjsonwriter_t *writer, const char *buf, int len);
|
||||||
|
void rjsonwriter_rawf(rjsonwriter_t *writer, const char *fmt, ...);
|
||||||
|
|
||||||
|
/* Add a UTF-8 encoded string
|
||||||
|
* Special and control characters are automatically escaped.
|
||||||
|
* If NULL is passed an empty string will be written (not JSON null). */
|
||||||
|
void rjsonwriter_add_string(rjsonwriter_t *writer, const char *value);
|
||||||
|
|
||||||
|
/* Add a signed or unsigned integer or a double number */
|
||||||
|
static INLINE void rjsonwriter_add_int(rjsonwriter_t *writer, int value)
|
||||||
|
{ rjsonwriter_rawf(writer, "%d", value); }
|
||||||
|
|
||||||
|
static INLINE void rjsonwriter_add_unsigned(rjsonwriter_t *writer, unsigned value)
|
||||||
|
{ rjsonwriter_rawf(writer, "%u", value); }
|
||||||
|
|
||||||
|
void rjsonwriter_add_double(rjsonwriter_t *writer, double value);
|
||||||
|
|
||||||
|
/* Functions to add JSON token characters */
|
||||||
|
static INLINE void rjsonwriter_add_start_object(rjsonwriter_t *writer)
|
||||||
|
{ rjsonwriter_raw(writer, "{", 1); }
|
||||||
|
|
||||||
|
static INLINE void rjsonwriter_add_end_object(rjsonwriter_t *writer)
|
||||||
|
{ rjsonwriter_raw(writer, "}", 1); }
|
||||||
|
|
||||||
|
static INLINE void rjsonwriter_add_start_array(rjsonwriter_t *writer)
|
||||||
|
{ rjsonwriter_raw(writer, "[", 1); }
|
||||||
|
|
||||||
|
static INLINE void rjsonwriter_add_end_array(rjsonwriter_t *writer)
|
||||||
|
{ rjsonwriter_raw(writer, "]", 1); }
|
||||||
|
|
||||||
|
static INLINE void rjsonwriter_add_colon(rjsonwriter_t *writer)
|
||||||
|
{ rjsonwriter_raw(writer, ":", 1); }
|
||||||
|
|
||||||
|
static INLINE void rjsonwriter_add_comma(rjsonwriter_t *writer)
|
||||||
|
{ rjsonwriter_raw(writer, ",", 1); }
|
||||||
|
|
||||||
|
/* Functions to add whitespace characters */
|
||||||
|
/* These do nothing with the option RJSONWRITER_OPTION_SKIP_WHITESPACE */
|
||||||
|
static INLINE void rjsonwriter_add_newline(rjsonwriter_t *writer)
|
||||||
|
{ rjsonwriter_raw(writer, "\n", 1); }
|
||||||
|
|
||||||
|
static INLINE void rjsonwriter_add_space(rjsonwriter_t *writer)
|
||||||
|
{ rjsonwriter_raw(writer, " ", 1); }
|
||||||
|
|
||||||
|
void rjsonwriter_add_spaces(rjsonwriter_t *writer, int count);
|
||||||
|
|
||||||
|
static INLINE void rjsonwriter_add_tab(rjsonwriter_t *writer)
|
||||||
|
{ rjsonwriter_raw(writer, "\t", 1); }
|
||||||
|
|
||||||
|
void rjsonwriter_add_tabs(rjsonwriter_t *writer, int count);
|
||||||
|
|
||||||
|
RETRO_END_DECLS
|
||||||
|
|
||||||
|
#endif
|
@ -43,11 +43,107 @@ typedef uint32_t pthread_cond_t;
|
|||||||
typedef int pthread_condattr_t;
|
typedef int pthread_condattr_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
#ifndef USE_CTRULIB_2
|
||||||
uint32_t semaphore;
|
/* Backported CondVar API from libctru 2.0, and under its license:
|
||||||
LightLock lock;
|
https://github.com/devkitPro/libctru
|
||||||
uint32_t waiting;
|
Slightly modified for compatibility with older libctru. */
|
||||||
} cond_t;
|
|
||||||
|
typedef s32 CondVar;
|
||||||
|
|
||||||
|
static INLINE Result syncArbitrateAddress(s32* addr, ArbitrationType type, s32 value)
|
||||||
|
{
|
||||||
|
return svcArbitrateAddress(__sync_get_arbiter(), (u32)addr, type, value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE Result syncArbitrateAddressWithTimeout(s32* addr, ArbitrationType type, s32 value, s64 timeout_ns)
|
||||||
|
{
|
||||||
|
return svcArbitrateAddress(__sync_get_arbiter(), (u32)addr, type, value, timeout_ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE void __dmb(void)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE void CondVar_BeginWait(CondVar* cv, LightLock* lock)
|
||||||
|
{
|
||||||
|
s32 val;
|
||||||
|
do
|
||||||
|
val = __ldrex(cv) - 1;
|
||||||
|
while (__strex(cv, val));
|
||||||
|
LightLock_Unlock(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE bool CondVar_EndWait(CondVar* cv, s32 num_threads)
|
||||||
|
{
|
||||||
|
bool hasWaiters;
|
||||||
|
s32 val;
|
||||||
|
|
||||||
|
do {
|
||||||
|
val = __ldrex(cv);
|
||||||
|
hasWaiters = val < 0;
|
||||||
|
if (hasWaiters)
|
||||||
|
{
|
||||||
|
if (num_threads < 0)
|
||||||
|
val = 0;
|
||||||
|
else if (val <= -num_threads)
|
||||||
|
val += num_threads;
|
||||||
|
else
|
||||||
|
val = 0;
|
||||||
|
}
|
||||||
|
} while (__strex(cv, val));
|
||||||
|
|
||||||
|
return hasWaiters;
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE void CondVar_Init(CondVar* cv)
|
||||||
|
{
|
||||||
|
*cv = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE void CondVar_Wait(CondVar* cv, LightLock* lock)
|
||||||
|
{
|
||||||
|
CondVar_BeginWait(cv, lock);
|
||||||
|
syncArbitrateAddress(cv, ARBITRATION_WAIT_IF_LESS_THAN, 0);
|
||||||
|
LightLock_Lock(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE int CondVar_WaitTimeout(CondVar* cv, LightLock* lock, s64 timeout_ns)
|
||||||
|
{
|
||||||
|
CondVar_BeginWait(cv, lock);
|
||||||
|
|
||||||
|
bool timedOut = false;
|
||||||
|
Result rc = syncArbitrateAddressWithTimeout(cv, ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT, 0, timeout_ns);
|
||||||
|
if (R_DESCRIPTION(rc) == RD_TIMEOUT)
|
||||||
|
{
|
||||||
|
timedOut = CondVar_EndWait(cv, 1);
|
||||||
|
__dmb();
|
||||||
|
}
|
||||||
|
|
||||||
|
LightLock_Lock(lock);
|
||||||
|
return timedOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE void CondVar_WakeUp(CondVar* cv, s32 num_threads)
|
||||||
|
{
|
||||||
|
__dmb();
|
||||||
|
if (CondVar_EndWait(cv, num_threads))
|
||||||
|
syncArbitrateAddress(cv, ARBITRATION_SIGNAL, num_threads);
|
||||||
|
else
|
||||||
|
__dmb();
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE void CondVar_Signal(CondVar* cv)
|
||||||
|
{
|
||||||
|
CondVar_WakeUp(cv, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE void CondVar_Broadcast(CondVar* cv)
|
||||||
|
{
|
||||||
|
CondVar_WakeUp(cv, ARBITRATION_SIGNAL_ALL);
|
||||||
|
}
|
||||||
|
/* End libctru 2.0 backport */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* libctru threads return void but pthreads return void pointer */
|
/* libctru threads return void but pthreads return void pointer */
|
||||||
static bool mutex_inited = false;
|
static bool mutex_inited = false;
|
||||||
@ -139,8 +235,7 @@ static INLINE void pthread_exit(void *retval)
|
|||||||
|
|
||||||
static INLINE int pthread_detach(pthread_t thread)
|
static INLINE int pthread_detach(pthread_t thread)
|
||||||
{
|
{
|
||||||
/* FIXME: pthread_detach equivalent missing? */
|
threadDetach((Thread)thread);
|
||||||
(void)thread;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,13 +258,7 @@ static INLINE int pthread_mutex_trylock(pthread_mutex_t *mutex)
|
|||||||
static INLINE int pthread_cond_wait(pthread_cond_t *cond,
|
static INLINE int pthread_cond_wait(pthread_cond_t *cond,
|
||||||
pthread_mutex_t *mutex)
|
pthread_mutex_t *mutex)
|
||||||
{
|
{
|
||||||
cond_t *cond_data = (cond_t *)*cond;
|
CondVar_Wait((CondVar *)cond, (LightLock *)mutex);
|
||||||
LightLock_Lock(&cond_data->lock);
|
|
||||||
cond_data->waiting++;
|
|
||||||
LightLock_Unlock(mutex);
|
|
||||||
LightLock_Unlock(&cond_data->lock);
|
|
||||||
svcWaitSynchronization(cond_data->semaphore, INT64_MAX);
|
|
||||||
LightLock_Lock(mutex);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,25 +270,22 @@ static INLINE int pthread_cond_timedwait(pthread_cond_t *cond,
|
|||||||
struct timeval tm;
|
struct timeval tm;
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
cond_t *cond_data = (cond_t *)*cond;
|
do {
|
||||||
LightLock_Lock(&cond_data->lock);
|
gettimeofday(&tm, NULL);
|
||||||
cond_data->waiting++;
|
now.tv_sec = tm.tv_sec;
|
||||||
|
now.tv_nsec = tm.tv_usec * 1000;
|
||||||
|
s64 timeout = (abstime->tv_sec - now.tv_sec) * 1000000000 + (abstime->tv_nsec - now.tv_nsec);
|
||||||
|
|
||||||
gettimeofday(&tm, NULL);
|
if (timeout < 0)
|
||||||
now.tv_sec = tm.tv_sec;
|
{
|
||||||
now.tv_nsec = tm.tv_usec * 1000;
|
|
||||||
s64 timeout = (abstime->tv_sec - now.tv_sec) * 1000000000 + (abstime->tv_nsec - now.tv_nsec);
|
|
||||||
|
|
||||||
if (timeout >= 0) {
|
|
||||||
LightLock_Unlock(mutex);
|
|
||||||
LightLock_Unlock(&cond_data->lock);
|
|
||||||
if (svcWaitSynchronization(cond_data->semaphore, timeout))
|
|
||||||
retval = ETIMEDOUT;
|
retval = ETIMEDOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
LightLock_Lock(mutex);
|
if (!CondVar_WaitTimeout((CondVar *)cond, (LightLock *)mutex, timeout)) {
|
||||||
} else {
|
break;
|
||||||
retval = ETIMEDOUT;
|
}
|
||||||
}
|
} while (1);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@ -207,65 +293,25 @@ static INLINE int pthread_cond_timedwait(pthread_cond_t *cond,
|
|||||||
static INLINE int pthread_cond_init(pthread_cond_t *cond,
|
static INLINE int pthread_cond_init(pthread_cond_t *cond,
|
||||||
const pthread_condattr_t *attr)
|
const pthread_condattr_t *attr)
|
||||||
{
|
{
|
||||||
cond_t *cond_data = calloc(1, sizeof(cond_t));
|
CondVar_Init((CondVar *)cond);
|
||||||
if (!cond_data)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (svcCreateSemaphore(&cond_data->semaphore, 0, 1))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
LightLock_Init(&cond_data->lock);
|
|
||||||
cond_data->waiting = 0;
|
|
||||||
*cond = (pthread_cond_t)cond_data;
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
|
||||||
svcCloseHandle(cond_data->semaphore);
|
|
||||||
if (cond_data)
|
|
||||||
free(cond_data);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static INLINE int pthread_cond_signal(pthread_cond_t *cond)
|
static INLINE int pthread_cond_signal(pthread_cond_t *cond)
|
||||||
{
|
{
|
||||||
int32_t count;
|
CondVar_Signal((CondVar *)cond);
|
||||||
cond_t *cond_data = (cond_t *)*cond;
|
|
||||||
LightLock_Lock(&cond_data->lock);
|
|
||||||
if (cond_data->waiting) {
|
|
||||||
cond_data->waiting--;
|
|
||||||
LightLock_Unlock(&cond_data->lock);
|
|
||||||
svcReleaseSemaphore(&count, cond_data->semaphore, 1);
|
|
||||||
} else {
|
|
||||||
LightLock_Unlock(&cond_data->lock);
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static INLINE int pthread_cond_broadcast(pthread_cond_t *cond)
|
static INLINE int pthread_cond_broadcast(pthread_cond_t *cond)
|
||||||
{
|
{
|
||||||
int32_t count;
|
CondVar_Broadcast((CondVar *)cond);
|
||||||
cond_t *cond_data = (cond_t *)*cond;
|
|
||||||
LightLock_Lock(&cond_data->lock);
|
|
||||||
while (cond_data->waiting) {
|
|
||||||
cond_data->waiting--;
|
|
||||||
LightLock_Unlock(&cond_data->lock);
|
|
||||||
svcReleaseSemaphore(&count, cond_data->semaphore, 1);
|
|
||||||
LightLock_Lock(&cond_data->lock);
|
|
||||||
}
|
|
||||||
LightLock_Unlock(&cond_data->lock);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static INLINE int pthread_cond_destroy(pthread_cond_t *cond)
|
static INLINE int pthread_cond_destroy(pthread_cond_t *cond)
|
||||||
{
|
{
|
||||||
if (*cond) {
|
/*Nothing to destroy*/
|
||||||
cond_t *cond_data = (cond_t *)*cond;
|
|
||||||
|
|
||||||
svcCloseHandle(cond_data->semaphore);
|
|
||||||
free(cond_data);
|
|
||||||
}
|
|
||||||
*cond = 0;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user