From 22e0b9821aa33a439f3cf23411c701b1ac0cad57 Mon Sep 17 00:00:00 2001 From: Vincent Batts Date: Wed, 18 Oct 2017 15:52:21 -0400 Subject: [PATCH] Submodules to latest upstream source Signed-off-by: Vincent Batts --- .gitmodules | 3 + unqlite | 1 + unqlite.c | 59960 +------------------------------------------------- unqlite.h | 950 +- 4 files changed, 6 insertions(+), 60908 deletions(-) create mode 100644 .gitmodules create mode 160000 unqlite mode change 100644 => 120000 unqlite.c mode change 100644 => 120000 unqlite.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e720ca7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "unqlite"] + path = unqlite + url = https://github.com/symisc/unqlite.git diff --git a/unqlite b/unqlite new file mode 160000 index 0000000..ca65e2b --- /dev/null +++ b/unqlite @@ -0,0 +1 @@ +Subproject commit ca65e2b9f690c7609bd0d062c142479b53074bd5 diff --git a/unqlite.c b/unqlite.c deleted file mode 100644 index d3cdb94..0000000 --- a/unqlite.c +++ /dev/null @@ -1,59959 +0,0 @@ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ -/* - * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * $SymiscID: unqlite.c v1.1.6 Unix|Win32/64 2013-05-21 22:39:15 stable $ - */ -/* This file is an amalgamation of many separate C source files from unqlite version 1.1.6 - * By combining all the individual C code files into this single large file, the entire code - * can be compiled as a single translation unit. This allows many compilers to do optimization's - * that would not be possible if the files were compiled separately. Performance improvements - * are commonly seen when unqlite is compiled as a single translation unit. - * - * This file is all you need to compile unqlite. To use unqlite in other programs, you need - * this file and the "unqlite.h" header file that defines the programming interface to the - * unqlite engine.(If you do not have the "unqlite.h" header file at hand, you will find - * a copy embedded within the text of this file.Search for "Header file: " to find - * the start of the embedded unqlite.h header file.) Additional code files may be needed if - * you want a wrapper to interface unqlite with your choice of programming language. - * To get the official documentation, please visit http://unqlite.org/ - */ - /* - * Make the sure the following directive is defined in the amalgamation build. - */ - #ifndef UNQLITE_AMALGAMATION - #define UNQLITE_AMALGAMATION - #define JX9_AMALGAMATION - /* Marker for routines not intended for external use */ - #define JX9_PRIVATE static - #endif /* UNQLITE_AMALGAMATION */ -/* - * Embedded header file for unqlite: - */ -/* - * ---------------------------------------------------------- - * File: unqlite.h - * MD5: df5da92eec0f513e6daaeee18dff981a - * ---------------------------------------------------------- - */ -/* This file was automatically generated. Do not edit (Except for compile time directives)! */ -#ifndef _UNQLITE_H_ -#define _UNQLITE_H_ -/* - * Symisc UnQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ -/* - * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - /* $SymiscID: unqlite.h v1.1 UNIX|WIN32/64 2012-11-02 02:10 stable $ */ -#include /* needed for the definition of va_list */ -/* - * Compile time engine version, signature, identification in the symisc source tree - * and copyright notice. - * Each macro have an equivalent C interface associated with it that provide the same - * information but are associated with the library instead of the header file. - * Refer to [unqlite_lib_version()], [unqlite_lib_signature()], [unqlite_lib_ident()] and - * [unqlite_lib_copyright()] for more information. - */ -/* - * The UNQLITE_VERSION C preprocessor macroevaluates to a string literal - * that is the unqlite version in the format "X.Y.Z" where X is the major - * version number and Y is the minor version number and Z is the release - * number. - */ -#define UNQLITE_VERSION "1.1.6" -/* - * The UNQLITE_VERSION_NUMBER C preprocessor macro resolves to an integer - * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same - * numbers used in [UNQLITE_VERSION]. - */ -#define UNQLITE_VERSION_NUMBER 1001006 -/* - * The UNQLITE_SIG C preprocessor macro evaluates to a string - * literal which is the public signature of the unqlite engine. - * This signature could be included for example in a host-application - * generated Server MIME header as follows: - * Server: YourWebServer/x.x unqlite/x.x.x \r\n - */ -#define UNQLITE_SIG "unqlite/1.1.6" -/* - * UnQLite identification in the Symisc source tree: - * Each particular check-in of a particular software released - * by symisc systems have an unique identifier associated with it. - * This macro hold the one associated with unqlite. - */ -#define UNQLITE_IDENT "unqlite:b172a1e2c3f62fb35c8e1fb2795121f82356cad6" -/* - * Copyright notice. - * If you have any questions about the licensing situation, please - * visit http://unqlite.org/licensing.html - * or contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - */ -#define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine ] 2012-2013, http://unqlite.org/" - -/* Forward declaration to public objects */ -typedef struct unqlite_io_methods unqlite_io_methods; -typedef struct unqlite_kv_methods unqlite_kv_methods; -typedef struct unqlite_kv_engine unqlite_kv_engine; -typedef struct jx9_io_stream unqlite_io_stream; -typedef struct jx9_context unqlite_context; -typedef struct jx9_value unqlite_value; -typedef struct unqlite_vfs unqlite_vfs; -typedef struct unqlite_vm unqlite_vm; -typedef struct unqlite unqlite; -/* - * ------------------------------ - * Compile time directives - * ------------------------------ - * For most purposes, UnQLite can be built just fine using the default compilation options. - * However, if required, the compile-time options documented below can be used to omit UnQLite - * features (resulting in a smaller compiled library size) or to change the default values - * of some parameters. - * Every effort has been made to ensure that the various combinations of compilation options - * work harmoniously and produce a working library. - * - * UNQLITE_ENABLE_THREADS - * This option controls whether or not code is included in UnQLite to enable it to operate - * safely in a multithreaded environment. The default is not. All mutexing code is omitted - * and it is unsafe to use UnQLite in a multithreaded program. When compiled with the - * UNQLITE_ENABLE_THREADS directive enabled, UnQLite can be used in a multithreaded program - * and it is safe to share the same virtual machine and engine handle between two or more threads. - * The value of UNQLITE_ENABLE_THREADS can be determined at run-time using the unqlite_lib_is_threadsafe() - * interface. - * When UnQLite has been compiled with threading support then the threading mode can be altered - * at run-time using the unqlite_lib_config() interface together with one of these verbs: - * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE - * UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI - * Platforms others than Windows and UNIX systems must install their own mutex subsystem via - * unqlite_lib_config() with a configuration verb set to UNQLITE_LIB_CONFIG_USER_MUTEX. - * Otherwise the library is not threadsafe. - * Note that you must link UnQLite with the POSIX threads library under UNIX systems (i.e: -lpthread). - * - * Options To Omit/Enable Features - * - * The following options can be used to reduce the size of the compiled library by omitting optional - * features. This is probably only useful in embedded systems where space is especially tight, as even - * with all features included the UnQLite library is relatively small. Don't forget to tell your - * compiler to optimize for binary size! (the -Os option if using GCC). Telling your compiler - * to optimize for size usually has a much larger impact on library footprint than employing - * any of these compile-time options. - * - * JX9_DISABLE_BUILTIN_FUNC - * Jx9 is shipped with more than 312 built-in functions suitable for most purposes like - * string and INI processing, ZIP extracting, Base64 encoding/decoding, JSON encoding/decoding - * and so forth. - * If this directive is enabled, then all built-in Jx9 functions are omitted from the build. - * Note that special functions such as db_create(), db_store(), db_fetch(), etc. are not omitted - * from the build and are not affected by this directive. - * - * JX9_ENABLE_MATH_FUNC - * If this directive is enabled, built-in math functions such as sqrt(), abs(), log(), ceil(), etc. - * are included in the build. Note that you may need to link UnQLite with the math library in same - * Linux/BSD flavor (i.e: -lm). - * - * JX9_DISABLE_DISK_IO - * If this directive is enabled, built-in VFS functions such as chdir(), mkdir(), chroot(), unlink(), - * sleep(), etc. are omitted from the build. - * - * UNQLITE_ENABLE_JX9_HASH_IO - * If this directive is enabled, built-in hash functions such as md5(), sha1(), md5_file(), crc32(), etc. - * are included in the build. - */ -/* Symisc public definitions */ -#if !defined(SYMISC_STANDARD_DEFS) -#define SYMISC_STANDARD_DEFS -#if defined (_WIN32) || defined (WIN32) || defined(__MINGW32__) || defined (_MSC_VER) || defined (_WIN32_WCE) -/* Windows Systems */ -#if !defined(__WINNT__) -#define __WINNT__ -#endif -/* - * Determine if we are dealing with WindowsCE - which has a much - * reduced API. - */ -#if defined(_WIN32_WCE) -#ifndef __WIN_CE__ -#define __WIN_CE__ -#endif /* __WIN_CE__ */ -#endif /* _WIN32_WCE */ -#else -/* - * By default we will assume that we are compiling on a UNIX systems. - * Otherwise the OS_OTHER directive must be defined. - */ -#if !defined(OS_OTHER) -#if !defined(__UNIXES__) -#define __UNIXES__ -#endif /* __UNIXES__ */ -#else -#endif /* OS_OTHER */ -#endif /* __WINNT__/__UNIXES__ */ -#if defined(_MSC_VER) || defined(__BORLANDC__) -typedef signed __int64 sxi64; /* 64 bits(8 bytes) signed int64 */ -typedef unsigned __int64 sxu64; /* 64 bits(8 bytes) unsigned int64 */ -#else -typedef signed long long int sxi64; /* 64 bits(8 bytes) signed int64 */ -typedef unsigned long long int sxu64; /* 64 bits(8 bytes) unsigned int64 */ -#endif /* _MSC_VER */ -/* Signature of the consumer routine */ -typedef int (*ProcConsumer)(const void *, unsigned int, void *); -/* Forward reference */ -typedef struct SyMutexMethods SyMutexMethods; -typedef struct SyMemMethods SyMemMethods; -typedef struct SyString SyString; -typedef struct syiovec syiovec; -typedef struct SyMutex SyMutex; -typedef struct Sytm Sytm; -/* Scatter and gather array. */ -struct syiovec -{ -#if defined (__WINNT__) - /* Same fields type and offset as WSABUF structure defined one winsock2 header */ - unsigned long nLen; - char *pBase; -#else - void *pBase; - unsigned long nLen; -#endif -}; -struct SyString -{ - const char *zString; /* Raw string (may not be null terminated) */ - unsigned int nByte; /* Raw string length */ -}; -/* Time structure. */ -struct Sytm -{ - int tm_sec; /* seconds (0 - 60) */ - int tm_min; /* minutes (0 - 59) */ - int tm_hour; /* hours (0 - 23) */ - int tm_mday; /* day of month (1 - 31) */ - int tm_mon; /* month of year (0 - 11) */ - int tm_year; /* year + 1900 */ - int tm_wday; /* day of week (Sunday = 0) */ - int tm_yday; /* day of year (0 - 365) */ - int tm_isdst; /* is summer time in effect? */ - char *tm_zone; /* abbreviation of timezone name */ - long tm_gmtoff; /* offset from UTC in seconds */ -}; -/* Convert a tm structure (struct tm *) found in to a Sytm structure */ -#define STRUCT_TM_TO_SYTM(pTM, pSYTM) \ - (pSYTM)->tm_hour = (pTM)->tm_hour;\ - (pSYTM)->tm_min = (pTM)->tm_min;\ - (pSYTM)->tm_sec = (pTM)->tm_sec;\ - (pSYTM)->tm_mon = (pTM)->tm_mon;\ - (pSYTM)->tm_mday = (pTM)->tm_mday;\ - (pSYTM)->tm_year = (pTM)->tm_year + 1900;\ - (pSYTM)->tm_yday = (pTM)->tm_yday;\ - (pSYTM)->tm_wday = (pTM)->tm_wday;\ - (pSYTM)->tm_isdst = (pTM)->tm_isdst;\ - (pSYTM)->tm_gmtoff = 0;\ - (pSYTM)->tm_zone = 0; - -/* Convert a SYSTEMTIME structure (LPSYSTEMTIME: Windows Systems only ) to a Sytm structure */ -#define SYSTEMTIME_TO_SYTM(pSYSTIME, pSYTM) \ - (pSYTM)->tm_hour = (pSYSTIME)->wHour;\ - (pSYTM)->tm_min = (pSYSTIME)->wMinute;\ - (pSYTM)->tm_sec = (pSYSTIME)->wSecond;\ - (pSYTM)->tm_mon = (pSYSTIME)->wMonth - 1;\ - (pSYTM)->tm_mday = (pSYSTIME)->wDay;\ - (pSYTM)->tm_year = (pSYSTIME)->wYear;\ - (pSYTM)->tm_yday = 0;\ - (pSYTM)->tm_wday = (pSYSTIME)->wDayOfWeek;\ - (pSYTM)->tm_gmtoff = 0;\ - (pSYTM)->tm_isdst = -1;\ - (pSYTM)->tm_zone = 0; - -/* Dynamic memory allocation methods. */ -struct SyMemMethods -{ - void * (*xAlloc)(unsigned int); /* [Required:] Allocate a memory chunk */ - void * (*xRealloc)(void *, unsigned int); /* [Required:] Re-allocate a memory chunk */ - void (*xFree)(void *); /* [Required:] Release a memory chunk */ - unsigned int (*xChunkSize)(void *); /* [Optional:] Return chunk size */ - int (*xInit)(void *); /* [Optional:] Initialization callback */ - void (*xRelease)(void *); /* [Optional:] Release callback */ - void *pUserData; /* [Optional:] First argument to xInit() and xRelease() */ -}; -/* Out of memory callback signature. */ -typedef int (*ProcMemError)(void *); -/* Mutex methods. */ -struct SyMutexMethods -{ - int (*xGlobalInit)(void); /* [Optional:] Global mutex initialization */ - void (*xGlobalRelease)(void); /* [Optional:] Global Release callback () */ - SyMutex * (*xNew)(int); /* [Required:] Request a new mutex */ - void (*xRelease)(SyMutex *); /* [Optional:] Release a mutex */ - void (*xEnter)(SyMutex *); /* [Required:] Enter mutex */ - int (*xTryEnter)(SyMutex *); /* [Optional:] Try to enter a mutex */ - void (*xLeave)(SyMutex *); /* [Required:] Leave a locked mutex */ -}; -#if defined (_MSC_VER) || defined (__MINGW32__) || defined (__GNUC__) && defined (__declspec) -#define SX_APIIMPORT __declspec(dllimport) -#define SX_APIEXPORT __declspec(dllexport) -#else -#define SX_APIIMPORT -#define SX_APIEXPORT -#endif -/* Standard return values from Symisc public interfaces */ -#define SXRET_OK 0 /* Not an error */ -#define SXERR_MEM (-1) /* Out of memory */ -#define SXERR_IO (-2) /* IO error */ -#define SXERR_EMPTY (-3) /* Empty field */ -#define SXERR_LOCKED (-4) /* Locked operation */ -#define SXERR_ORANGE (-5) /* Out of range value */ -#define SXERR_NOTFOUND (-6) /* Item not found */ -#define SXERR_LIMIT (-7) /* Limit reached */ -#define SXERR_MORE (-8) /* Need more input */ -#define SXERR_INVALID (-9) /* Invalid parameter */ -#define SXERR_ABORT (-10) /* User callback request an operation abort */ -#define SXERR_EXISTS (-11) /* Item exists */ -#define SXERR_SYNTAX (-12) /* Syntax error */ -#define SXERR_UNKNOWN (-13) /* Unknown error */ -#define SXERR_BUSY (-14) /* Busy operation */ -#define SXERR_OVERFLOW (-15) /* Stack or buffer overflow */ -#define SXERR_WILLBLOCK (-16) /* Operation will block */ -#define SXERR_NOTIMPLEMENTED (-17) /* Operation not implemented */ -#define SXERR_EOF (-18) /* End of input */ -#define SXERR_PERM (-19) /* Permission error */ -#define SXERR_NOOP (-20) /* No-op */ -#define SXERR_FORMAT (-21) /* Invalid format */ -#define SXERR_NEXT (-22) /* Not an error */ -#define SXERR_OS (-23) /* System call return an error */ -#define SXERR_CORRUPT (-24) /* Corrupted pointer */ -#define SXERR_CONTINUE (-25) /* Not an error: Operation in progress */ -#define SXERR_NOMATCH (-26) /* No match */ -#define SXERR_RESET (-27) /* Operation reset */ -#define SXERR_DONE (-28) /* Not an error */ -#define SXERR_SHORT (-29) /* Buffer too short */ -#define SXERR_PATH (-30) /* Path error */ -#define SXERR_TIMEOUT (-31) /* Timeout */ -#define SXERR_BIG (-32) /* Too big for processing */ -#define SXERR_RETRY (-33) /* Retry your call */ -#define SXERR_IGNORE (-63) /* Ignore */ -#endif /* SYMISC_PUBLIC_DEFS */ -/* - * Marker for exported interfaces. - */ -#define UNQLITE_APIEXPORT SX_APIEXPORT -/* - * If compiling for a processor that lacks floating point - * support, substitute integer for floating-point. - */ -#ifdef UNQLITE_OMIT_FLOATING_POINT -typedef sxi64 uqlite_real; -#else -typedef double unqlite_real; -#endif -typedef sxi64 unqlite_int64; -/* Standard UnQLite return values */ -#define UNQLITE_OK SXRET_OK /* Successful result */ -/* Beginning of error codes */ -#define UNQLITE_NOMEM SXERR_MEM /* Out of memory */ -#define UNQLITE_ABORT SXERR_ABORT /* Another thread have released this instance */ -#define UNQLITE_IOERR SXERR_IO /* IO error */ -#define UNQLITE_CORRUPT SXERR_CORRUPT /* Corrupt pointer */ -#define UNQLITE_LOCKED SXERR_LOCKED /* Forbidden Operation */ -#define UNQLITE_BUSY SXERR_BUSY /* The database file is locked */ -#define UNQLITE_DONE SXERR_DONE /* Operation done */ -#define UNQLITE_PERM SXERR_PERM /* Permission error */ -#define UNQLITE_NOTIMPLEMENTED SXERR_NOTIMPLEMENTED /* Method not implemented by the underlying Key/Value storage engine */ -#define UNQLITE_NOTFOUND SXERR_NOTFOUND /* No such record */ -#define UNQLITE_NOOP SXERR_NOOP /* No such method */ -#define UNQLITE_INVALID SXERR_INVALID /* Invalid parameter */ -#define UNQLITE_EOF SXERR_EOF /* End Of Input */ -#define UNQLITE_UNKNOWN SXERR_UNKNOWN /* Unknown configuration option */ -#define UNQLITE_LIMIT SXERR_LIMIT /* Database limit reached */ -#define UNQLITE_EXISTS SXERR_EXISTS /* Record exists */ -#define UNQLITE_EMPTY SXERR_EMPTY /* Empty record */ -#define UNQLITE_COMPILE_ERR (-70) /* Compilation error */ -#define UNQLITE_VM_ERR (-71) /* Virtual machine error */ -#define UNQLITE_FULL (-73) /* Full database (unlikely) */ -#define UNQLITE_CANTOPEN (-74) /* Unable to open the database file */ -#define UNQLITE_READ_ONLY (-75) /* Read only Key/Value storage engine */ -#define UNQLITE_LOCKERR (-76) /* Locking protocol error */ -/* end-of-error-codes */ -/* - * Database Handle Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure an UnQLite database handle. - * These constants must be passed as the second argument to [unqlite_config()]. - * - * Each options require a variable number of arguments. - * The [unqlite_config()] interface will return UNQLITE_OK on success, any other - * return value indicates failure. - * For a full discussion on the configuration verbs and their expected - * parameters, please refer to this page: - * http://unqlite.org/c_api/unqlite_config.html - */ -#define UNQLITE_CONFIG_JX9_ERR_LOG 1 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */ -#define UNQLITE_CONFIG_MAX_PAGE_CACHE 2 /* ONE ARGUMENT: int nMaxPage */ -#define UNQLITE_CONFIG_ERR_LOG 3 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */ -#define UNQLITE_CONFIG_KV_ENGINE 4 /* ONE ARGUMENT: const char *zKvName */ -#define UNQLITE_CONFIG_DISABLE_AUTO_COMMIT 5 /* NO ARGUMENTS */ -#define UNQLITE_CONFIG_GET_KV_NAME 6 /* ONE ARGUMENT: const char **pzPtr */ -/* - * UnQLite/Jx9 Virtual Machine Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the Jx9 (Via UnQLite) Virtual machine. - * These constants must be passed as the second argument to the [unqlite_vm_config()] - * interface. - * Each options require a variable number of arguments. - * The [unqlite_vm_config()] interface will return UNQLITE_OK on success, any other return - * value indicates failure. - * There are many options but the most importants are: UNQLITE_VM_CONFIG_OUTPUT which install - * a VM output consumer callback, UNQLITE_VM_CONFIG_HTTP_REQUEST which parse and register - * a HTTP request and UNQLITE_VM_CONFIG_ARGV_ENTRY which populate the $argv array. - * For a full discussion on the configuration verbs and their expected parameters, please - * refer to this page: - * http://unqlite.org/c_api/unqlite_vm_config.html - */ -#define UNQLITE_VM_CONFIG_OUTPUT 1 /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */ -#define UNQLITE_VM_CONFIG_IMPORT_PATH 2 /* ONE ARGUMENT: const char *zIncludePath */ -#define UNQLITE_VM_CONFIG_ERR_REPORT 3 /* NO ARGUMENTS: Report all run-time errors in the VM output */ -#define UNQLITE_VM_CONFIG_RECURSION_DEPTH 4 /* ONE ARGUMENT: int nMaxDepth */ -#define UNQLITE_VM_OUTPUT_LENGTH 5 /* ONE ARGUMENT: unsigned int *pLength */ -#define UNQLITE_VM_CONFIG_CREATE_VAR 6 /* TWO ARGUMENTS: const char *zName, unqlite_value *pValue */ -#define UNQLITE_VM_CONFIG_HTTP_REQUEST 7 /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */ -#define UNQLITE_VM_CONFIG_SERVER_ATTR 8 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */ -#define UNQLITE_VM_CONFIG_ENV_ATTR 9 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */ -#define UNQLITE_VM_CONFIG_EXEC_VALUE 10 /* ONE ARGUMENT: unqlite_value **ppValue */ -#define UNQLITE_VM_CONFIG_IO_STREAM 11 /* ONE ARGUMENT: const unqlite_io_stream *pStream */ -#define UNQLITE_VM_CONFIG_ARGV_ENTRY 12 /* ONE ARGUMENT: const char *zValue */ -#define UNQLITE_VM_CONFIG_EXTRACT_OUTPUT 13 /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */ -/* - * Storage engine configuration commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the underlying storage engine (i.e Hash, B+tree, R+tree). - * These constants must be passed as the first argument to [unqlite_kv_config()]. - * Each options require a variable number of arguments. - * The [unqlite_kv_config()] interface will return UNQLITE_OK on success, any other return - * value indicates failure. - * For a full discussion on the configuration verbs and their expected parameters, please - * refer to this page: - * http://unqlite.org/c_api/unqlite_kv_config.html - */ -#define UNQLITE_KV_CONFIG_HASH_FUNC 1 /* ONE ARGUMENT: unsigned int (*xHash)(const void *,unsigned int) */ -#define UNQLITE_KV_CONFIG_CMP_FUNC 2 /* ONE ARGUMENT: int (*xCmp)(const void *,const void *,unsigned int) */ -/* - * Global Library Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the whole library. - * These constants must be passed as the first argument to [unqlite_lib_config()]. - * - * Each options require a variable number of arguments. - * The [unqlite_lib_config()] interface will return UNQLITE_OK on success, any other return - * value indicates failure. - * Notes: - * The default configuration is recommended for most applications and so the call to - * [unqlite_lib_config()] is usually not necessary. It is provided to support rare - * applications with unusual needs. - * The [unqlite_lib_config()] interface is not threadsafe. The application must insure that - * no other [unqlite_*()] interfaces are invoked by other threads while [unqlite_lib_config()] - * is running. Furthermore, [unqlite_lib_config()] may only be invoked prior to library - * initialization using [unqlite_lib_init()] or [unqlite_init()] or after shutdown - * by [unqlite_lib_shutdown()]. If [unqlite_lib_config()] is called after [unqlite_lib_init()] - * or [unqlite_init()] and before [unqlite_lib_shutdown()] then it will return UNQLITE_LOCKED. - * For a full discussion on the configuration verbs and their expected parameters, please - * refer to this page: - * http://unqlite.org/c_api/unqlite_lib.html - */ -#define UNQLITE_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ -#define UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */ -#define UNQLITE_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ -#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */ -#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */ -#define UNQLITE_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const unqlite_vfs *pVfs */ -#define UNQLITE_LIB_CONFIG_STORAGE_ENGINE 7 /* ONE ARGUMENT: unqlite_kv_methods *pStorage */ -#define UNQLITE_LIB_CONFIG_PAGE_SIZE 8 /* ONE ARGUMENT: int iPageSize */ -/* - * These bit values are intended for use in the 3rd parameter to the [unqlite_open()] interface - * and in the 4th parameter to the xOpen method of the [unqlite_vfs] object. - */ -#define UNQLITE_OPEN_READONLY 0x00000001 /* Read only mode. Ok for [unqlite_open] */ -#define UNQLITE_OPEN_READWRITE 0x00000002 /* Ok for [unqlite_open] */ -#define UNQLITE_OPEN_CREATE 0x00000004 /* Ok for [unqlite_open] */ -#define UNQLITE_OPEN_EXCLUSIVE 0x00000008 /* VFS only */ -#define UNQLITE_OPEN_TEMP_DB 0x00000010 /* VFS only */ -#define UNQLITE_OPEN_NOMUTEX 0x00000020 /* Ok for [unqlite_open] */ -#define UNQLITE_OPEN_OMIT_JOURNALING 0x00000040 /* Omit journaling for this database. Ok for [unqlite_open] */ -#define UNQLITE_OPEN_IN_MEMORY 0x00000080 /* An in memory database. Ok for [unqlite_open]*/ -#define UNQLITE_OPEN_MMAP 0x00000100 /* Obtain a memory view of the whole file. Ok for [unqlite_open] */ -/* - * Synchronization Type Flags - * - * When UnQLite invokes the xSync() method of an [unqlite_io_methods] object it uses - * a combination of these integer values as the second argument. - * - * When the UNQLITE_SYNC_DATAONLY flag is used, it means that the sync operation only - * needs to flush data to mass storage. Inode information need not be flushed. - * If the lower four bits of the flag equal UNQLITE_SYNC_NORMAL, that means to use normal - * fsync() semantics. If the lower four bits equal UNQLITE_SYNC_FULL, that means to use - * Mac OS X style fullsync instead of fsync(). - */ -#define UNQLITE_SYNC_NORMAL 0x00002 -#define UNQLITE_SYNC_FULL 0x00003 -#define UNQLITE_SYNC_DATAONLY 0x00010 -/* - * File Locking Levels - * - * UnQLite uses one of these integer values as the second - * argument to calls it makes to the xLock() and xUnlock() methods - * of an [unqlite_io_methods] object. - */ -#define UNQLITE_LOCK_NONE 0 -#define UNQLITE_LOCK_SHARED 1 -#define UNQLITE_LOCK_RESERVED 2 -#define UNQLITE_LOCK_PENDING 3 -#define UNQLITE_LOCK_EXCLUSIVE 4 -/* - * CAPIREF: OS Interface: Open File Handle - * - * An [unqlite_file] object represents an open file in the [unqlite_vfs] OS interface - * layer. - * Individual OS interface implementations will want to subclass this object by appending - * additional fields for their own use. The pMethods entry is a pointer to an - * [unqlite_io_methods] object that defines methods for performing - * I/O operations on the open file. -*/ -typedef struct unqlite_file unqlite_file; -struct unqlite_file { - const unqlite_io_methods *pMethods; /* Methods for an open file. MUST BE FIRST */ -}; -/* - * CAPIREF: OS Interface: File Methods Object - * - * Every file opened by the [unqlite_vfs] xOpen method populates an - * [unqlite_file] object (or, more commonly, a subclass of the - * [unqlite_file] object) with a pointer to an instance of this object. - * This object defines the methods used to perform various operations - * against the open file represented by the [unqlite_file] object. - * - * If the xOpen method sets the unqlite_file.pMethods element - * to a non-NULL pointer, then the unqlite_io_methods.xClose method - * may be invoked even if the xOpen reported that it failed. The - * only way to prevent a call to xClose following a failed xOpen - * is for the xOpen to set the unqlite_file.pMethods element to NULL. - * - * The flags argument to xSync may be one of [UNQLITE_SYNC_NORMAL] or - * [UNQLITE_SYNC_FULL]. The first choice is the normal fsync(). - * The second choice is a Mac OS X style fullsync. The [UNQLITE_SYNC_DATAONLY] - * flag may be ORed in to indicate that only the data of the file - * and not its inode needs to be synced. - * - * The integer values to xLock() and xUnlock() are one of - * - * UNQLITE_LOCK_NONE - * UNQLITE_LOCK_SHARED - * UNQLITE_LOCK_RESERVED - * UNQLITE_LOCK_PENDING - * UNQLITE_LOCK_EXCLUSIVE - * - * xLock() increases the lock. xUnlock() decreases the lock. - * The xCheckReservedLock() method checks whether any database connection, - * either in this process or in some other process, is holding a RESERVED, - * PENDING, or EXCLUSIVE lock on the file. It returns true if such a lock exists - * and false otherwise. - * - * The xSectorSize() method returns the sector size of the device that underlies - * the file. The sector size is the minimum write that can be performed without - * disturbing other bytes in the file. - * - */ -struct unqlite_io_methods { - int iVersion; /* Structure version number (currently 1) */ - int (*xClose)(unqlite_file*); - int (*xRead)(unqlite_file*, void*, unqlite_int64 iAmt, unqlite_int64 iOfst); - int (*xWrite)(unqlite_file*, const void*, unqlite_int64 iAmt, unqlite_int64 iOfst); - int (*xTruncate)(unqlite_file*, unqlite_int64 size); - int (*xSync)(unqlite_file*, int flags); - int (*xFileSize)(unqlite_file*, unqlite_int64 *pSize); - int (*xLock)(unqlite_file*, int); - int (*xUnlock)(unqlite_file*, int); - int (*xCheckReservedLock)(unqlite_file*, int *pResOut); - int (*xSectorSize)(unqlite_file*); -}; -/* - * CAPIREF: OS Interface Object - * - * An instance of the unqlite_vfs object defines the interface between - * the UnQLite core and the underlying operating system. The "vfs" - * in the name of the object stands for "Virtual File System". - * - * Only a single vfs can be registered within the UnQLite core. - * Vfs registration is done using the [unqlite_lib_config()] interface - * with a configuration verb set to UNQLITE_LIB_CONFIG_VFS. - * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users - * does not have to worry about registering and installing a vfs since UnQLite - * come with a built-in vfs for these platforms that implements most the methods - * defined below. - * - * Clients running on exotic systems (ie: Other than Windows and UNIX systems) - * must register their own vfs in order to be able to use the UnQLite library. - * - * The value of the iVersion field is initially 1 but may be larger in - * future versions of UnQLite. - * - * The szOsFile field is the size of the subclassed [unqlite_file] structure - * used by this VFS. mxPathname is the maximum length of a pathname in this VFS. - * - * At least szOsFile bytes of memory are allocated by UnQLite to hold the [unqlite_file] - * structure passed as the third argument to xOpen. The xOpen method does not have to - * allocate the structure; it should just fill it in. Note that the xOpen method must - * set the unqlite_file.pMethods to either a valid [unqlite_io_methods] object or to NULL. - * xOpen must do this even if the open fails. UnQLite expects that the unqlite_file.pMethods - * element will be valid after xOpen returns regardless of the success or failure of the - * xOpen call. - * - */ -struct unqlite_vfs { - const char *zName; /* Name of this virtual file system [i.e: Windows, UNIX, etc.] */ - int iVersion; /* Structure version number (currently 1) */ - int szOsFile; /* Size of subclassed unqlite_file */ - int mxPathname; /* Maximum file pathname length */ - int (*xOpen)(unqlite_vfs*, const char *zName, unqlite_file*,unsigned int flags); - int (*xDelete)(unqlite_vfs*, const char *zName, int syncDir); - int (*xAccess)(unqlite_vfs*, const char *zName, int flags, int *pResOut); - int (*xFullPathname)(unqlite_vfs*, const char *zName,int buf_len,char *zBuf); - int (*xTmpDir)(unqlite_vfs*,char *zBuf,int buf_len); - int (*xSleep)(unqlite_vfs*, int microseconds); - int (*xCurrentTime)(unqlite_vfs*,Sytm *pOut); - int (*xGetLastError)(unqlite_vfs*, int, char *); -}; -/* - * Flags for the xAccess VFS method - * - * These integer constants can be used as the third parameter to - * the xAccess method of an [unqlite_vfs] object. They determine - * what kind of permissions the xAccess method is looking for. - * With UNQLITE_ACCESS_EXISTS, the xAccess method - * simply checks whether the file exists. - * With UNQLITE_ACCESS_READWRITE, the xAccess method - * checks whether the named directory is both readable and writable - * (in other words, if files can be added, removed, and renamed within - * the directory). - * The UNQLITE_ACCESS_READWRITE constant is currently used only by the - * [temp_store_directory pragma], though this could change in a future - * release of UnQLite. - * With UNQLITE_ACCESS_READ, the xAccess method - * checks whether the file is readable. The UNQLITE_ACCESS_READ constant is - * currently unused, though it might be used in a future release of - * UnQLite. - */ -#define UNQLITE_ACCESS_EXISTS 0 -#define UNQLITE_ACCESS_READWRITE 1 -#define UNQLITE_ACCESS_READ 2 -/* - * The type used to represent a page number. The first page in a file - * is called page 1. 0 is used to represent "not a page". - * A page number is an unsigned 64-bit integer. - */ -typedef sxu64 pgno; -/* - * A database disk page is represented by an instance - * of the follwoing structure. - */ -typedef struct unqlite_page unqlite_page; -struct unqlite_page -{ - unsigned char *zData; /* Content of this page */ - void *pUserData; /* Extra content */ - pgno pgno; /* Page number for this page */ -}; -/* - * UnQLite handle to the underlying Key/Value Storage Engine (See below). - */ -typedef void * unqlite_kv_handle; -/* - * UnQLite pager IO methods. - * - * An instance of the following structure define the exported methods of the UnQLite pager - * to the underlying Key/Value storage engine. - */ -typedef struct unqlite_kv_io unqlite_kv_io; -struct unqlite_kv_io -{ - unqlite_kv_handle pHandle; /* UnQLite handle passed as the first parameter to the - * method defined below. - */ - unqlite_kv_methods *pMethods; /* Underlying storage engine */ - /* Pager methods */ - int (*xGet)(unqlite_kv_handle,pgno,unqlite_page **); - int (*xLookup)(unqlite_kv_handle,pgno,unqlite_page **); - int (*xNew)(unqlite_kv_handle,unqlite_page **); - int (*xWrite)(unqlite_page *); - int (*xDontWrite)(unqlite_page *); - int (*xDontJournal)(unqlite_page *); - int (*xDontMkHot)(unqlite_page *); - int (*xPageRef)(unqlite_page *); - int (*xPageUnref)(unqlite_page *); - int (*xPageSize)(unqlite_kv_handle); - int (*xReadOnly)(unqlite_kv_handle); - unsigned char * (*xTmpPage)(unqlite_kv_handle); - void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *)); - void (*xSetReload)(unqlite_kv_handle,void (*xPageReload)(void *)); - void (*xErr)(unqlite_kv_handle,const char *); -}; -/* - * Key/Value Storage Engine Cursor Object - * - * An instance of a subclass of the following object defines a cursor - * used to scan through a key-value storage engine. - */ -typedef struct unqlite_kv_cursor unqlite_kv_cursor; -struct unqlite_kv_cursor -{ - unqlite_kv_engine *pStore; /* Must be first */ - /* Subclasses will typically add additional fields */ -}; -/* - * Possible seek positions. - */ -#define UNQLITE_CURSOR_MATCH_EXACT 1 -#define UNQLITE_CURSOR_MATCH_LE 2 -#define UNQLITE_CURSOR_MATCH_GE 3 -/* - * Key/Value Storage Engine. - * - * A Key-Value storage engine is defined by an instance of the following - * object. - * UnQLite works with run-time interchangeable storage engines (i.e. Hash, B+Tree, R+Tree, LSM, etc.). - * The storage engine works with key/value pairs where both the key - * and the value are byte arrays of arbitrary length and with no restrictions on content. - * UnQLite come with two built-in KV storage engine: A Virtual Linear Hash (VLH) storage - * engine is used for persistent on-disk databases with O(1) lookup time and an in-memory - * hash-table or Red-black tree storage engine is used for in-memory databases. - * Future versions of UnQLite might add other built-in storage engines (i.e. LSM). - * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()] - * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE. - */ -struct unqlite_kv_engine -{ - const unqlite_kv_io *pIo; /* IO methods: MUST be first */ - /* Subclasses will typically add additional fields */ -}; -/* - * Key/Value Storage Engine Virtual Method Table. - * - * Key/Value storage engine methods is defined by an instance of the following - * object. - * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()] - * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE. - */ -struct unqlite_kv_methods -{ - const char *zName; /* Storage engine name [i.e. Hash, B+tree, LSM, R-tree, Mem, etc.]*/ - int szKv; /* 'unqlite_kv_engine' subclass size */ - int szCursor; /* 'unqlite_kv_cursor' subclass size */ - int iVersion; /* Structure version, currently 1 */ - /* Storage engine methods */ - int (*xInit)(unqlite_kv_engine *,int iPageSize); - void (*xRelease)(unqlite_kv_engine *); - int (*xConfig)(unqlite_kv_engine *,int op,va_list ap); - int (*xOpen)(unqlite_kv_engine *,pgno); - int (*xReplace)( - unqlite_kv_engine *, - const void *pKey,int nKeyLen, - const void *pData,unqlite_int64 nDataLen - ); - int (*xAppend)( - unqlite_kv_engine *, - const void *pKey,int nKeyLen, - const void *pData,unqlite_int64 nDataLen - ); - void (*xCursorInit)(unqlite_kv_cursor *); - int (*xSeek)(unqlite_kv_cursor *,const void *pKey,int nByte,int iPos); /* Mandatory */ - int (*xFirst)(unqlite_kv_cursor *); - int (*xLast)(unqlite_kv_cursor *); - int (*xValid)(unqlite_kv_cursor *); - int (*xNext)(unqlite_kv_cursor *); - int (*xPrev)(unqlite_kv_cursor *); - int (*xDelete)(unqlite_kv_cursor *); - int (*xKeyLength)(unqlite_kv_cursor *,int *); - int (*xKey)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); - int (*xDataLength)(unqlite_kv_cursor *,unqlite_int64 *); - int (*xData)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); - void (*xReset)(unqlite_kv_cursor *); - void (*xCursorRelease)(unqlite_kv_cursor *); -}; -/* - * UnQLite journal file suffix. - */ -#ifndef UNQLITE_JOURNAL_FILE_SUFFIX -#define UNQLITE_JOURNAL_FILE_SUFFIX "_unqlite_journal" -#endif -/* - * Call Context - Error Message Serverity Level. - * - * The following constans are the allowed severity level that can - * passed as the second argument to the [unqlite_context_throw_error()] or - * [unqlite_context_throw_error_format()] interfaces. - * Refer to the official documentation for additional information. - */ -#define UNQLITE_CTX_ERR 1 /* Call context error such as unexpected number of arguments, invalid types and so on. */ -#define UNQLITE_CTX_WARNING 2 /* Call context Warning */ -#define UNQLITE_CTX_NOTICE 3 /* Call context Notice */ -/* - * C-API-REF: Please refer to the official documentation for interfaces - * purpose and expected parameters. - */ - -/* Database Engine Handle */ -UNQLITE_APIEXPORT int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode); -UNQLITE_APIEXPORT int unqlite_config(unqlite *pDb,int nOp,...); -UNQLITE_APIEXPORT int unqlite_close(unqlite *pDb); - -/* Key/Value (KV) Store Interfaces */ -UNQLITE_APIEXPORT int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen); -UNQLITE_APIEXPORT int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen); -UNQLITE_APIEXPORT int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...); -UNQLITE_APIEXPORT int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...); -UNQLITE_APIEXPORT int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 /* in|out */*pBufLen); -UNQLITE_APIEXPORT int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey, - int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen); -UNQLITE_APIEXPORT int unqlite_kv_config(unqlite *pDb,int iOp,...); - -/* Document (JSON) Store Interfaces powered by the Jx9 Scripting Language */ -UNQLITE_APIEXPORT int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut); -UNQLITE_APIEXPORT int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut); -UNQLITE_APIEXPORT int unqlite_vm_config(unqlite_vm *pVm,int iOp,...); -UNQLITE_APIEXPORT int unqlite_vm_exec(unqlite_vm *pVm); -UNQLITE_APIEXPORT int unqlite_vm_reset(unqlite_vm *pVm); -UNQLITE_APIEXPORT int unqlite_vm_release(unqlite_vm *pVm); -UNQLITE_APIEXPORT int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData); -UNQLITE_APIEXPORT unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname); - -/* Cursor Iterator Interfaces */ -UNQLITE_APIEXPORT int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut); -UNQLITE_APIEXPORT int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur); -UNQLITE_APIEXPORT int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos); -UNQLITE_APIEXPORT int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte); -UNQLITE_APIEXPORT int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnData); -UNQLITE_APIEXPORT int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor); - -/* Manual Transaction Manager */ -UNQLITE_APIEXPORT int unqlite_begin(unqlite *pDb); -UNQLITE_APIEXPORT int unqlite_commit(unqlite *pDb); -UNQLITE_APIEXPORT int unqlite_rollback(unqlite *pDb); - -/* Utility interfaces */ -UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize); -UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize); -UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size); -UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb); - -/* In-process extending interfaces */ -UNQLITE_APIEXPORT int unqlite_create_function(unqlite_vm *pVm,const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData); -UNQLITE_APIEXPORT int unqlite_delete_function(unqlite_vm *pVm, const char *zName); -UNQLITE_APIEXPORT int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_delete_constant(unqlite_vm *pVm, const char *zName); - -/* On Demand Object allocation interfaces */ -UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm); -UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm); -UNQLITE_APIEXPORT int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue); -UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx); -UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_array(unqlite_context *pCtx); -UNQLITE_APIEXPORT void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue); - -/* Dynamically Typed Value Object Management Interfaces */ -UNQLITE_APIEXPORT int unqlite_value_int(unqlite_value *pVal, int iValue); -UNQLITE_APIEXPORT int unqlite_value_int64(unqlite_value *pVal, unqlite_int64 iValue); -UNQLITE_APIEXPORT int unqlite_value_bool(unqlite_value *pVal, int iBool); -UNQLITE_APIEXPORT int unqlite_value_null(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_double(unqlite_value *pVal, double Value); -UNQLITE_APIEXPORT int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen); -UNQLITE_APIEXPORT int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...); -UNQLITE_APIEXPORT int unqlite_value_reset_string_cursor(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_resource(unqlite_value *pVal, void *pUserData); -UNQLITE_APIEXPORT int unqlite_value_release(unqlite_value *pVal); - -/* Foreign Function Parameter Values */ -UNQLITE_APIEXPORT int unqlite_value_to_int(unqlite_value *pValue); -UNQLITE_APIEXPORT int unqlite_value_to_bool(unqlite_value *pValue); -UNQLITE_APIEXPORT unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue); -UNQLITE_APIEXPORT double unqlite_value_to_double(unqlite_value *pValue); -UNQLITE_APIEXPORT const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen); -UNQLITE_APIEXPORT void * unqlite_value_to_resource(unqlite_value *pValue); -UNQLITE_APIEXPORT int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict); - -/* Setting The Result Of A Foreign Function */ -UNQLITE_APIEXPORT int unqlite_result_int(unqlite_context *pCtx, int iValue); -UNQLITE_APIEXPORT int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue); -UNQLITE_APIEXPORT int unqlite_result_bool(unqlite_context *pCtx, int iBool); -UNQLITE_APIEXPORT int unqlite_result_double(unqlite_context *pCtx, double Value); -UNQLITE_APIEXPORT int unqlite_result_null(unqlite_context *pCtx); -UNQLITE_APIEXPORT int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen); -UNQLITE_APIEXPORT int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...); -UNQLITE_APIEXPORT int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue); -UNQLITE_APIEXPORT int unqlite_result_resource(unqlite_context *pCtx, void *pUserData); - -/* Dynamically Typed Value Object Query Interfaces */ -UNQLITE_APIEXPORT int unqlite_value_is_int(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_float(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_bool(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_string(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_null(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_numeric(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_callable(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_scalar(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_json_array(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_json_object(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_resource(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_empty(unqlite_value *pVal); - -/* JSON Array/Object Management Interfaces */ -UNQLITE_APIEXPORT unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte); -UNQLITE_APIEXPORT int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData); -UNQLITE_APIEXPORT int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue); -UNQLITE_APIEXPORT int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue); -UNQLITE_APIEXPORT int unqlite_array_count(unqlite_value *pArray); - -/* Call Context Handling Interfaces */ -UNQLITE_APIEXPORT int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen); -UNQLITE_APIEXPORT int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...); -UNQLITE_APIEXPORT int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr); -UNQLITE_APIEXPORT int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...); -UNQLITE_APIEXPORT unsigned int unqlite_context_random_num(unqlite_context *pCtx); -UNQLITE_APIEXPORT int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen); -UNQLITE_APIEXPORT void * unqlite_context_user_data(unqlite_context *pCtx); -UNQLITE_APIEXPORT int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData); -UNQLITE_APIEXPORT void * unqlite_context_peek_aux_data(unqlite_context *pCtx); -UNQLITE_APIEXPORT unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx); -UNQLITE_APIEXPORT const char * unqlite_function_name(unqlite_context *pCtx); - -/* Call Context Memory Management Interfaces */ -UNQLITE_APIEXPORT void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease); -UNQLITE_APIEXPORT void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte); -UNQLITE_APIEXPORT void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk); - -/* Global Library Management Interfaces */ -UNQLITE_APIEXPORT int unqlite_lib_config(int nConfigOp,...); -UNQLITE_APIEXPORT int unqlite_lib_init(void); -UNQLITE_APIEXPORT int unqlite_lib_shutdown(void); -UNQLITE_APIEXPORT int unqlite_lib_is_threadsafe(void); -UNQLITE_APIEXPORT const char * unqlite_lib_version(void); -UNQLITE_APIEXPORT const char * unqlite_lib_signature(void); -UNQLITE_APIEXPORT const char * unqlite_lib_ident(void); -UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void); - -#endif /* _UNQLITE_H_ */ -/* - * ---------------------------------------------------------- - * File: jx9.h - * MD5: 8a8548429796b64f4afad5fab7312e93 - * ---------------------------------------------------------- - */ -/* This file was automatically generated. Do not edit (except for compile time directive)! */ -#ifndef _JX9H_ -#define _JX9H_ -/* - * Symisc Jx9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ -/* - * Copyright (C) 2012, 2013 Symisc Systems. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Redistributions in any form must be accompanied by information on - * how to obtain complete source code for the JX9 engine and any - * accompanying software that uses the JX9 engine software. - * The source code must either be included in the distribution - * or be available for no more than the cost of distribution plus - * a nominal fee, and must be freely redistributable under reasonable - * conditions. For an executable file, complete source code means - * the source code for all modules it contains.It does not include - * source code for modules or files that typically accompany the major - * components of the operating system on which the executable file runs. - * - * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - /* $SymiscID: jx9.h v2.1 UNIX|WIN32/64 2012-09-15 09:43 stable $ */ -#include "unqlite.h" -/* - * Compile time engine version, signature, identification in the symisc source tree - * and copyright notice. - * Each macro have an equivalent C interface associated with it that provide the same - * information but are associated with the library instead of the header file. - * Refer to [jx9_lib_version()], [jx9_lib_signature()], [jx9_lib_ident()] and - * [jx9_lib_copyright()] for more information. - */ -/* - * The JX9_VERSION C preprocessor macroevaluates to a string literal - * that is the jx9 version in the format "X.Y.Z" where X is the major - * version number and Y is the minor version number and Z is the release - * number. - */ -#define JX9_VERSION "1.7.2" -/* - * The JX9_VERSION_NUMBER C preprocessor macro resolves to an integer - * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same - * numbers used in [JX9_VERSION]. - */ -#define JX9_VERSION_NUMBER 1007002 -/* - * The JX9_SIG C preprocessor macro evaluates to a string - * literal which is the public signature of the jx9 engine. - * This signature could be included for example in a host-application - * generated Server MIME header as follows: - * Server: YourWebServer/x.x Jx9/x.x.x \r\n - */ -#define JX9_SIG "Jx9/1.7.2" -/* - * JX9 identification in the Symisc source tree: - * Each particular check-in of a particular software released - * by symisc systems have an unique identifier associated with it. - * This macro hold the one associated with jx9. - */ -#define JX9_IDENT "jx9:d217a6e8c7f10fb35a8becb2793101fd2036aeb7" -/* - * Copyright notice. - * If you have any questions about the licensing situation, please - * visit http://jx9.symisc.net/licensing.html - * or contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - */ -#define JX9_COPYRIGHT "Copyright (C) Symisc Systems 2012-2013, http://jx9.symisc.net/" -/* Make sure we can call this stuff from C++ */ -#ifdef __cplusplus -extern "C" { -#endif -/* Forward declaration to public objects */ -typedef struct jx9_io_stream jx9_io_stream; -typedef struct jx9_context jx9_context; -typedef struct jx9_value jx9_value; -typedef struct jx9_vfs jx9_vfs; -typedef struct jx9_vm jx9_vm; -typedef struct jx9 jx9; - -#include "unqlite.h" - -#if !defined( UNQLITE_ENABLE_JX9_HASH_FUNC ) -#define JX9_DISABLE_HASH_FUNC -#endif /* UNQLITE_ENABLE_JX9_HASH_FUNC */ -#ifdef UNQLITE_ENABLE_THREADS -#define JX9_ENABLE_THREADS -#endif /* UNQLITE_ENABLE_THREADS */ -/* Standard JX9 return values */ -#define JX9_OK SXRET_OK /* Successful result */ -/* beginning-of-error-codes */ -#define JX9_NOMEM UNQLITE_NOMEM /* Out of memory */ -#define JX9_ABORT UNQLITE_ABORT /* Foreign Function request operation abort/Another thread have released this instance */ -#define JX9_IO_ERR UNQLITE_IOERR /* IO error */ -#define JX9_CORRUPT UNQLITE_CORRUPT /* Corrupt pointer/Unknown configuration option */ -#define JX9_LOOKED UNQLITE_LOCKED /* Forbidden Operation */ -#define JX9_COMPILE_ERR UNQLITE_COMPILE_ERR /* Compilation error */ -#define JX9_VM_ERR UNQLITE_VM_ERR /* Virtual machine error */ -/* end-of-error-codes */ -/* - * If compiling for a processor that lacks floating point - * support, substitute integer for floating-point. - */ -#ifdef JX9_OMIT_FLOATING_POINT -typedef sxi64 jx9_real; -#else -typedef double jx9_real; -#endif -typedef sxi64 jx9_int64; -/* - * Engine Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the JX9 engine. - * These constants must be passed as the second argument to the [jx9_config()] - * interface. - * Each options require a variable number of arguments. - * The [jx9_config()] interface will return JX9_OK on success, any other - * return value indicates failure. - * For a full discussion on the configuration verbs and their expected - * parameters, please refer to this page: - * http://jx9.symisc.net/c_api_func.html#jx9_config - */ -#define JX9_CONFIG_ERR_ABORT 1 /* RESERVED FOR FUTURE USE */ -#define JX9_CONFIG_ERR_LOG 2 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */ -/* - * Virtual Machine Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the JX9 Virtual machine. - * These constants must be passed as the second argument to the [jx9_vm_config()] - * interface. - * Each options require a variable number of arguments. - * The [jx9_vm_config()] interface will return JX9_OK on success, any other return - * value indicates failure. - * There are many options but the most importants are: JX9_VM_CONFIG_OUTPUT which install - * a VM output consumer callback, JX9_VM_CONFIG_HTTP_REQUEST which parse and register - * a HTTP request and JX9_VM_CONFIG_ARGV_ENTRY which populate the $argv array. - * For a full discussion on the configuration verbs and their expected parameters, please - * refer to this page: - * http://jx9.symisc.net/c_api_func.html#jx9_vm_config - */ -#define JX9_VM_CONFIG_OUTPUT UNQLITE_VM_CONFIG_OUTPUT /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */ -#define JX9_VM_CONFIG_IMPORT_PATH UNQLITE_VM_CONFIG_IMPORT_PATH /* ONE ARGUMENT: const char *zIncludePath */ -#define JX9_VM_CONFIG_ERR_REPORT UNQLITE_VM_CONFIG_ERR_REPORT /* NO ARGUMENTS: Report all run-time errors in the VM output */ -#define JX9_VM_CONFIG_RECURSION_DEPTH UNQLITE_VM_CONFIG_RECURSION_DEPTH /* ONE ARGUMENT: int nMaxDepth */ -#define JX9_VM_OUTPUT_LENGTH UNQLITE_VM_OUTPUT_LENGTH /* ONE ARGUMENT: unsigned int *pLength */ -#define JX9_VM_CONFIG_CREATE_VAR UNQLITE_VM_CONFIG_CREATE_VAR /* TWO ARGUMENTS: const char *zName, jx9_value *pValue */ -#define JX9_VM_CONFIG_HTTP_REQUEST UNQLITE_VM_CONFIG_HTTP_REQUEST /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */ -#define JX9_VM_CONFIG_SERVER_ATTR UNQLITE_VM_CONFIG_SERVER_ATTR /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */ -#define JX9_VM_CONFIG_ENV_ATTR UNQLITE_VM_CONFIG_ENV_ATTR /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */ -#define JX9_VM_CONFIG_EXEC_VALUE UNQLITE_VM_CONFIG_EXEC_VALUE /* ONE ARGUMENT: jx9_value **ppValue */ -#define JX9_VM_CONFIG_IO_STREAM UNQLITE_VM_CONFIG_IO_STREAM /* ONE ARGUMENT: const jx9_io_stream *pStream */ -#define JX9_VM_CONFIG_ARGV_ENTRY UNQLITE_VM_CONFIG_ARGV_ENTRY /* ONE ARGUMENT: const char *zValue */ -#define JX9_VM_CONFIG_EXTRACT_OUTPUT UNQLITE_VM_CONFIG_EXTRACT_OUTPUT /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */ -/* - * Global Library Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the whole library. - * These constants must be passed as the first argument to the [jx9_lib_config()] - * interface. - * Each options require a variable number of arguments. - * The [jx9_lib_config()] interface will return JX9_OK on success, any other return - * value indicates failure. - * Notes: - * The default configuration is recommended for most applications and so the call to - * [jx9_lib_config()] is usually not necessary. It is provided to support rare - * applications with unusual needs. - * The [jx9_lib_config()] interface is not threadsafe. The application must insure that - * no other [jx9_*()] interfaces are invoked by other threads while [jx9_lib_config()] - * is running. Furthermore, [jx9_lib_config()] may only be invoked prior to library - * initialization using [jx9_lib_init()] or [jx9_init()] or after shutdown - * by [jx9_lib_shutdown()]. If [jx9_lib_config()] is called after [jx9_lib_init()] - * or [jx9_init()] and before [jx9_lib_shutdown()] then it will return jx9LOCKED. - * For a full discussion on the configuration verbs and their expected parameters, please - * refer to this page: - * http://jx9.symisc.net/c_api_func.html#Global_Library_Management_Interfaces - */ -#define JX9_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ -#define JX9_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */ -#define JX9_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ -#define JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */ -#define JX9_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */ -#define JX9_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const jx9_vfs *pVfs */ -/* - * Call Context - Error Message Serverity Level. - */ -#define JX9_CTX_ERR UNQLITE_CTX_ERR /* Call context error such as unexpected number of arguments, invalid types and so on. */ -#define JX9_CTX_WARNING UNQLITE_CTX_WARNING /* Call context Warning */ -#define JX9_CTX_NOTICE UNQLITE_CTX_NOTICE /* Call context Notice */ -/* Current VFS structure version*/ -#define JX9_VFS_VERSION 2 -/* - * JX9 Virtual File System (VFS). - * - * An instance of the jx9_vfs object defines the interface between the JX9 core - * and the underlying operating system. The "vfs" in the name of the object stands - * for "virtual file system". The vfs is used to implement JX9 system functions - * such as mkdir(), chdir(), stat(), get_user_name() and many more. - * The value of the iVersion field is initially 2 but may be larger in future versions - * of JX9. - * Additional fields may be appended to this object when the iVersion value is increased. - * Only a single vfs can be registered within the JX9 core. Vfs registration is done - * using the jx9_lib_config() interface with a configuration verb set to JX9_LIB_CONFIG_VFS. - * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users does not have to - * worry about registering and installing a vfs since JX9 come with a built-in vfs for these - * platforms which implement most the methods defined below. - * Host-application running on exotic systems (ie: Other than Windows and UNIX systems) must - * register their own vfs in order to be able to use and call JX9 system functions. - * Also note that the jx9_compile_file() interface depend on the xMmap() method of the underlying - * vfs which mean that this method must be available (Always the case using the built-in VFS) - * in order to use this interface. - * Developers wishing to implement their own vfs an contact symisc systems to obtain - * the JX9 VFS C/C++ Specification manual. - */ -struct jx9_vfs -{ - const char *zName; /* Underlying VFS name [i.e: FreeBSD/Linux/Windows...] */ - int iVersion; /* Current VFS structure version [default 2] */ - /* Directory functions */ - int (*xChdir)(const char *); /* Change directory */ - int (*xChroot)(const char *); /* Change the root directory */ - int (*xGetcwd)(jx9_context *); /* Get the current working directory */ - int (*xMkdir)(const char *, int, int); /* Make directory */ - int (*xRmdir)(const char *); /* Remove directory */ - int (*xIsdir)(const char *); /* Tells whether the filename is a directory */ - int (*xRename)(const char *, const char *); /* Renames a file or directory */ - int (*xRealpath)(const char *, jx9_context *); /* Return canonicalized absolute pathname*/ - /* Systems functions */ - int (*xSleep)(unsigned int); /* Delay execution in microseconds */ - int (*xUnlink)(const char *); /* Deletes a file */ - int (*xFileExists)(const char *); /* Checks whether a file or directory exists */ - int (*xChmod)(const char *, int); /* Changes file mode */ - int (*xChown)(const char *, const char *); /* Changes file owner */ - int (*xChgrp)(const char *, const char *); /* Changes file group */ - jx9_int64 (*xFreeSpace)(const char *); /* Available space on filesystem or disk partition */ - jx9_int64 (*xTotalSpace)(const char *); /* Total space on filesystem or disk partition */ - jx9_int64 (*xFileSize)(const char *); /* Gets file size */ - jx9_int64 (*xFileAtime)(const char *); /* Gets last access time of file */ - jx9_int64 (*xFileMtime)(const char *); /* Gets file modification time */ - jx9_int64 (*xFileCtime)(const char *); /* Gets inode change time of file */ - int (*xStat)(const char *, jx9_value *, jx9_value *); /* Gives information about a file */ - int (*xlStat)(const char *, jx9_value *, jx9_value *); /* Gives information about a file */ - int (*xIsfile)(const char *); /* Tells whether the filename is a regular file */ - int (*xIslink)(const char *); /* Tells whether the filename is a symbolic link */ - int (*xReadable)(const char *); /* Tells whether a file exists and is readable */ - int (*xWritable)(const char *); /* Tells whether the filename is writable */ - int (*xExecutable)(const char *); /* Tells whether the filename is executable */ - int (*xFiletype)(const char *, jx9_context *); /* Gets file type [i.e: fifo, dir, file..] */ - int (*xGetenv)(const char *, jx9_context *); /* Gets the value of an environment variable */ - int (*xSetenv)(const char *, const char *); /* Sets the value of an environment variable */ - int (*xTouch)(const char *, jx9_int64, jx9_int64); /* Sets access and modification time of file */ - int (*xMmap)(const char *, void **, jx9_int64 *); /* Read-only memory map of the whole file */ - void (*xUnmap)(void *, jx9_int64); /* Unmap a memory view */ - int (*xLink)(const char *, const char *, int); /* Create hard or symbolic link */ - int (*xUmask)(int); /* Change the current umask */ - void (*xTempDir)(jx9_context *); /* Get path of the temporary directory */ - unsigned int (*xProcessId)(void); /* Get running process ID */ - int (*xUid)(void); /* user ID of the process */ - int (*xGid)(void); /* group ID of the process */ - void (*xUsername)(jx9_context *); /* Running username */ - int (*xExec)(const char *, jx9_context *); /* Execute an external program */ -}; -/* Current JX9 IO stream structure version. */ -#define JX9_IO_STREAM_VERSION 1 -/* - * Possible open mode flags that can be passed to the xOpen() routine - * of the underlying IO stream device . - * Refer to the JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html) - * for additional information. - */ -#define JX9_IO_OPEN_RDONLY 0x001 /* Read-only open */ -#define JX9_IO_OPEN_WRONLY 0x002 /* Write-only open */ -#define JX9_IO_OPEN_RDWR 0x004 /* Read-write open. */ -#define JX9_IO_OPEN_CREATE 0x008 /* If the file does not exist it will be created */ -#define JX9_IO_OPEN_TRUNC 0x010 /* Truncate the file to zero length */ -#define JX9_IO_OPEN_APPEND 0x020 /* Append mode.The file offset is positioned at the end of the file */ -#define JX9_IO_OPEN_EXCL 0x040 /* Ensure that this call creates the file, the file must not exist before */ -#define JX9_IO_OPEN_BINARY 0x080 /* Simple hint: Data is binary */ -#define JX9_IO_OPEN_TEMP 0x100 /* Simple hint: Temporary file */ -#define JX9_IO_OPEN_TEXT 0x200 /* Simple hint: Data is textual */ -/* - * JX9 IO Stream Device. - * - * An instance of the jx9_io_stream object defines the interface between the JX9 core - * and the underlying stream device. - * A stream is a smart mechanism for generalizing file, network, data compression - * and other IO operations which share a common set of functions using an abstracted - * unified interface. - * A stream device is additional code which tells the stream how to handle specific - * protocols/encodings. For example, the http device knows how to translate a URL - * into an HTTP/1.1 request for a file on a remote server. - * JX9 come with two built-in IO streams device: - * The file:// stream which perform very efficient disk IO and the jx9:// stream - * which is a special stream that allow access various I/O streams (See the JX9 official - * documentation for more information on this stream). - * A stream is referenced as: scheme://target - * scheme(string) - The name of the wrapper to be used. Examples include: file, http, https, ftp, - * ftps, compress.zlib, compress.bz2, and jx9. If no wrapper is specified, the function default - * is used (typically file://). - * target - Depends on the device used. For filesystem related streams this is typically a path - * and filename of the desired file.For network related streams this is typically a hostname, often - * with a path appended. - * IO stream devices are registered using a call to jx9_vm_config() with a configuration verb - * set to JX9_VM_CONFIG_IO_STREAM. - * Currently the JX9 development team is working on the implementation of the http:// and ftp:// - * IO stream protocols. These devices will be available in the next major release of the JX9 engine. - * Developers wishing to implement their own IO stream devices must understand and follow - * The JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html). - */ -struct jx9_io_stream -{ - const char *zName; /* Underlying stream name [i.e: file/http/zip/jx9, ..] */ - int iVersion; /* IO stream structure version [default 1]*/ - int (*xOpen)(const char *, int, jx9_value *, void **); /* Open handle*/ - int (*xOpenDir)(const char *, jx9_value *, void **); /* Open directory handle */ - void (*xClose)(void *); /* Close file handle */ - void (*xCloseDir)(void *); /* Close directory handle */ - jx9_int64 (*xRead)(void *, void *, jx9_int64); /* Read from the open stream */ - int (*xReadDir)(void *, jx9_context *); /* Read entry from directory handle */ - jx9_int64 (*xWrite)(void *, const void *, jx9_int64); /* Write to the open stream */ - int (*xSeek)(void *, jx9_int64, int); /* Seek on the open stream */ - int (*xLock)(void *, int); /* Lock/Unlock the open stream */ - void (*xRewindDir)(void *); /* Rewind directory handle */ - jx9_int64 (*xTell)(void *); /* Current position of the stream read/write pointer */ - int (*xTrunc)(void *, jx9_int64); /* Truncates the open stream to a given length */ - int (*xSync)(void *); /* Flush open stream data */ - int (*xStat)(void *, jx9_value *, jx9_value *); /* Stat an open stream handle */ -}; -/* - * C-API-REF: Please refer to the official documentation for interfaces - * purpose and expected parameters. - */ -/* Engine Handling Interfaces */ -JX9_PRIVATE int jx9_init(jx9 **ppEngine); -/*JX9_PRIVATE int jx9_config(jx9 *pEngine, int nConfigOp, ...);*/ -JX9_PRIVATE int jx9_release(jx9 *pEngine); -/* Compile Interfaces */ -JX9_PRIVATE int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm); -JX9_PRIVATE int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm); -/* Virtual Machine Handling Interfaces */ -JX9_PRIVATE int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...); -/*JX9_PRIVATE int jx9_vm_exec(jx9_vm *pVm, int *pExitStatus);*/ -/*JX9_PRIVATE jx9_value * jx9_vm_extract_variable(jx9_vm *pVm,const char *zVarname);*/ -/*JX9_PRIVATE int jx9_vm_reset(jx9_vm *pVm);*/ -JX9_PRIVATE int jx9_vm_release(jx9_vm *pVm); -/*JX9_PRIVATE int jx9_vm_dump_v2(jx9_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);*/ -/* In-process Extending Interfaces */ -JX9_PRIVATE int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData); -/*JX9_PRIVATE int jx9_delete_function(jx9_vm *pVm, const char *zName);*/ -JX9_PRIVATE int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData); -/*JX9_PRIVATE int jx9_delete_constant(jx9_vm *pVm, const char *zName);*/ -/* Foreign Function Parameter Values */ -JX9_PRIVATE int jx9_value_to_int(jx9_value *pValue); -JX9_PRIVATE int jx9_value_to_bool(jx9_value *pValue); -JX9_PRIVATE jx9_int64 jx9_value_to_int64(jx9_value *pValue); -JX9_PRIVATE double jx9_value_to_double(jx9_value *pValue); -JX9_PRIVATE const char * jx9_value_to_string(jx9_value *pValue, int *pLen); -JX9_PRIVATE void * jx9_value_to_resource(jx9_value *pValue); -JX9_PRIVATE int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict); -/* Setting The Result Of A Foreign Function */ -JX9_PRIVATE int jx9_result_int(jx9_context *pCtx, int iValue); -JX9_PRIVATE int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue); -JX9_PRIVATE int jx9_result_bool(jx9_context *pCtx, int iBool); -JX9_PRIVATE int jx9_result_double(jx9_context *pCtx, double Value); -JX9_PRIVATE int jx9_result_null(jx9_context *pCtx); -JX9_PRIVATE int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen); -JX9_PRIVATE int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...); -JX9_PRIVATE int jx9_result_value(jx9_context *pCtx, jx9_value *pValue); -JX9_PRIVATE int jx9_result_resource(jx9_context *pCtx, void *pUserData); -/* Call Context Handling Interfaces */ -JX9_PRIVATE int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen); -/*JX9_PRIVATE int jx9_context_output_format(jx9_context *pCtx, const char *zFormat, ...);*/ -JX9_PRIVATE int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr); -JX9_PRIVATE int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...); -JX9_PRIVATE unsigned int jx9_context_random_num(jx9_context *pCtx); -JX9_PRIVATE int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen); -JX9_PRIVATE void * jx9_context_user_data(jx9_context *pCtx); -JX9_PRIVATE int jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData); -JX9_PRIVATE void * jx9_context_peek_aux_data(jx9_context *pCtx); -JX9_PRIVATE void * jx9_context_pop_aux_data(jx9_context *pCtx); -JX9_PRIVATE unsigned int jx9_context_result_buf_length(jx9_context *pCtx); -JX9_PRIVATE const char * jx9_function_name(jx9_context *pCtx); -/* Call Context Memory Management Interfaces */ -JX9_PRIVATE void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease); -JX9_PRIVATE void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte); -JX9_PRIVATE void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk); -/* On Demand Dynamically Typed Value Object allocation interfaces */ -JX9_PRIVATE jx9_value * jx9_new_scalar(jx9_vm *pVm); -JX9_PRIVATE jx9_value * jx9_new_array(jx9_vm *pVm); -JX9_PRIVATE int jx9_release_value(jx9_vm *pVm, jx9_value *pValue); -JX9_PRIVATE jx9_value * jx9_context_new_scalar(jx9_context *pCtx); -JX9_PRIVATE jx9_value * jx9_context_new_array(jx9_context *pCtx); -JX9_PRIVATE void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue); -/* Dynamically Typed Value Object Management Interfaces */ -JX9_PRIVATE int jx9_value_int(jx9_value *pVal, int iValue); -JX9_PRIVATE int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue); -JX9_PRIVATE int jx9_value_bool(jx9_value *pVal, int iBool); -JX9_PRIVATE int jx9_value_null(jx9_value *pVal); -JX9_PRIVATE int jx9_value_double(jx9_value *pVal, double Value); -JX9_PRIVATE int jx9_value_string(jx9_value *pVal, const char *zString, int nLen); -JX9_PRIVATE int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...); -JX9_PRIVATE int jx9_value_reset_string_cursor(jx9_value *pVal); -JX9_PRIVATE int jx9_value_resource(jx9_value *pVal, void *pUserData); -JX9_PRIVATE int jx9_value_release(jx9_value *pVal); -/* JSON Array/Object Management Interfaces */ -JX9_PRIVATE jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte); -JX9_PRIVATE int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData); -JX9_PRIVATE int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue); -JX9_PRIVATE int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue); -JX9_PRIVATE unsigned int jx9_array_count(jx9_value *pArray); -/* Dynamically Typed Value Object Query Interfaces */ -JX9_PRIVATE int jx9_value_is_int(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_float(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_bool(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_string(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_null(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_numeric(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_callable(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_scalar(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_json_array(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_json_object(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_resource(jx9_value *pVal); -JX9_PRIVATE int jx9_value_is_empty(jx9_value *pVal); -/* Global Library Management Interfaces */ -/*JX9_PRIVATE int jx9_lib_init(void);*/ -JX9_PRIVATE int jx9_lib_config(int nConfigOp, ...); -JX9_PRIVATE int jx9_lib_shutdown(void); -/*JX9_PRIVATE int jx9_lib_is_threadsafe(void);*/ -/*JX9_PRIVATE const char * jx9_lib_version(void);*/ -JX9_PRIVATE const char * jx9_lib_signature(void); -/*JX9_PRIVATE const char * jx9_lib_ident(void);*/ -/*JX9_PRIVATE const char * jx9_lib_copyright(void);*/ -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* _JX9H_ */ - -/* - * ---------------------------------------------------------- - * File: jx9Int.h - * MD5: 539aa6619fb06376acda88ba66311be9 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: jx9Int.h v1.9 FreeBSD 2012-08-13 23:25 devel $ */ -#ifndef __JX9INT_H__ -#define __JX9INT_H__ -/* Internal interface definitions for JX9. */ -#ifdef JX9_AMALGAMATION -#ifndef JX9_PRIVATE -/* Marker for routines not intended for external use */ -#define JX9_PRIVATE static -#endif /* JX9_PRIVATE */ -#else -#define JX9_PRIVATE -#include "jx9.h" -#endif -#ifndef JX9_PI -/* Value of PI */ -#define JX9_PI 3.1415926535898 -#endif -/* - * Constants for the largest and smallest possible 64-bit signed integers. - * These macros are designed to work correctly on both 32-bit and 64-bit - * compilers. - */ -#ifndef LARGEST_INT64 -#define LARGEST_INT64 (0xffffffff|(((sxi64)0x7fffffff)<<32)) -#endif -#ifndef SMALLEST_INT64 -#define SMALLEST_INT64 (((sxi64)-1) - LARGEST_INT64) -#endif -/* Forward declaration of private structures */ -typedef struct jx9_foreach_info jx9_foreach_info; -typedef struct jx9_foreach_step jx9_foreach_step; -typedef struct jx9_hashmap_node jx9_hashmap_node; -typedef struct jx9_hashmap jx9_hashmap; -/* Symisc Standard types */ -#if !defined(SYMISC_STD_TYPES) -#define SYMISC_STD_TYPES -#ifdef __WINNT__ -/* Disable nuisance warnings on Borland compilers */ -#if defined(__BORLANDC__) -#pragma warn -rch /* unreachable code */ -#pragma warn -ccc /* Condition is always true or false */ -#pragma warn -aus /* Assigned value is never used */ -#pragma warn -csu /* Comparing signed and unsigned */ -#pragma warn -spa /* Suspicious pointer arithmetic */ -#endif -#endif -typedef signed char sxi8; /* signed char */ -typedef unsigned char sxu8; /* unsigned char */ -typedef signed short int sxi16; /* 16 bits(2 bytes) signed integer */ -typedef unsigned short int sxu16; /* 16 bits(2 bytes) unsigned integer */ -typedef int sxi32; /* 32 bits(4 bytes) integer */ -typedef unsigned int sxu32; /* 32 bits(4 bytes) unsigned integer */ -typedef long sxptr; -typedef unsigned long sxuptr; -typedef long sxlong; -typedef unsigned long sxulong; -typedef sxi32 sxofft; -typedef sxi64 sxofft64; -typedef long double sxlongreal; -typedef double sxreal; -#define SXI8_HIGH 0x7F -#define SXU8_HIGH 0xFF -#define SXI16_HIGH 0x7FFF -#define SXU16_HIGH 0xFFFF -#define SXI32_HIGH 0x7FFFFFFF -#define SXU32_HIGH 0xFFFFFFFF -#define SXI64_HIGH 0x7FFFFFFFFFFFFFFF -#define SXU64_HIGH 0xFFFFFFFFFFFFFFFF -#if !defined(TRUE) -#define TRUE 1 -#endif -#if !defined(FALSE) -#define FALSE 0 -#endif -/* - * The following macros are used to cast pointers to integers and - * integers to pointers. - */ -#if defined(__PTRDIFF_TYPE__) -# define SX_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) -# define SX_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) -#elif !defined(__GNUC__) -# define SX_INT_TO_PTR(X) ((void*)&((char*)0)[X]) -# define SX_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) -#else -# define SX_INT_TO_PTR(X) ((void*)(X)) -# define SX_PTR_TO_INT(X) ((int)(X)) -#endif -#define SXMIN(a, b) ((a < b) ? (a) : (b)) -#define SXMAX(a, b) ((a < b) ? (b) : (a)) -#endif /* SYMISC_STD_TYPES */ -/* Symisc Run-time API private definitions */ -#if !defined(SYMISC_PRIVATE_DEFS) -#define SYMISC_PRIVATE_DEFS - -typedef sxi32 (*ProcRawStrCmp)(const SyString *, const SyString *); -#define SyStringData(RAW) ((RAW)->zString) -#define SyStringLength(RAW) ((RAW)->nByte) -#define SyStringInitFromBuf(RAW, ZBUF, NLEN){\ - (RAW)->zString = (const char *)ZBUF;\ - (RAW)->nByte = (sxu32)(NLEN);\ -} -#define SyStringUpdatePtr(RAW, NBYTES){\ - if( NBYTES > (RAW)->nByte ){\ - (RAW)->nByte = 0;\ - }else{\ - (RAW)->zString += NBYTES;\ - (RAW)->nByte -= NBYTES;\ - }\ -} -#define SyStringDupPtr(RAW1, RAW2)\ - (RAW1)->zString = (RAW2)->zString;\ - (RAW1)->nByte = (RAW2)->nByte; - -#define SyStringTrimLeadingChar(RAW, CHAR)\ - while((RAW)->nByte > 0 && (RAW)->zString[0] == CHAR ){\ - (RAW)->zString++;\ - (RAW)->nByte--;\ - } -#define SyStringTrimTrailingChar(RAW, CHAR)\ - while((RAW)->nByte > 0 && (RAW)->zString[(RAW)->nByte - 1] == CHAR){\ - (RAW)->nByte--;\ - } -#define SyStringCmp(RAW1, RAW2, xCMP)\ - (((RAW1)->nByte == (RAW2)->nByte) ? xCMP((RAW1)->zString, (RAW2)->zString, (RAW2)->nByte) : (sxi32)((RAW1)->nByte - (RAW2)->nByte)) - -#define SyStringCmp2(RAW1, RAW2, xCMP)\ - (((RAW1)->nByte >= (RAW2)->nByte) ? xCMP((RAW1)->zString, (RAW2)->zString, (RAW2)->nByte) : (sxi32)((RAW2)->nByte - (RAW1)->nByte)) - -#define SyStringCharCmp(RAW, CHAR) \ - (((RAW)->nByte == sizeof(char)) ? ((RAW)->zString[0] == CHAR ? 0 : CHAR - (RAW)->zString[0]) : ((RAW)->zString[0] == CHAR ? 0 : (RAW)->nByte - sizeof(char))) - -#define SX_ADDR(PTR) ((sxptr)PTR) -#define SX_ARRAYSIZE(X) (sizeof(X)/sizeof(X[0])) -#define SXUNUSED(P) (P = 0) -#define SX_EMPTY(PTR) (PTR == 0) -#define SX_EMPTY_STR(STR) (STR == 0 || STR[0] == 0 ) -typedef struct SyMemBackend SyMemBackend; -typedef struct SyBlob SyBlob; -typedef struct SySet SySet; -/* Standard function signatures */ -typedef sxi32 (*ProcCmp)(const void *, const void *, sxu32); -typedef sxi32 (*ProcPatternMatch)(const char *, sxu32, const char *, sxu32, sxu32 *); -typedef sxi32 (*ProcSearch)(const void *, sxu32, const void *, sxu32, ProcCmp, sxu32 *); -typedef sxu32 (*ProcHash)(const void *, sxu32); -typedef sxi32 (*ProcHashSum)(const void *, sxu32, unsigned char *, sxu32); -typedef sxi32 (*ProcSort)(void *, sxu32, sxu32, ProcCmp); -#define MACRO_LIST_PUSH(Head, Item)\ - Item->pNext = Head;\ - Head = Item; -#define MACRO_LD_PUSH(Head, Item)\ - if( Head == 0 ){\ - Head = Item;\ - }else{\ - Item->pNext = Head;\ - Head->pPrev = Item;\ - Head = Item;\ - } -#define MACRO_LD_REMOVE(Head, Item)\ - if( Head == Item ){\ - Head = Head->pNext;\ - }\ - if( Item->pPrev ){ Item->pPrev->pNext = Item->pNext;}\ - if( Item->pNext ){ Item->pNext->pPrev = Item->pPrev;} -/* - * A generic dynamic set. - */ -struct SySet -{ - SyMemBackend *pAllocator; /* Memory backend */ - void *pBase; /* Base pointer */ - sxu32 nUsed; /* Total number of used slots */ - sxu32 nSize; /* Total number of available slots */ - sxu32 eSize; /* Size of a single slot */ - sxu32 nCursor; /* Loop cursor */ - void *pUserData; /* User private data associated with this container */ -}; -#define SySetBasePtr(S) ((S)->pBase) -#define SySetBasePtrJump(S, OFFT) (&((char *)(S)->pBase)[OFFT*(S)->eSize]) -#define SySetUsed(S) ((S)->nUsed) -#define SySetSize(S) ((S)->nSize) -#define SySetElemSize(S) ((S)->eSize) -#define SySetCursor(S) ((S)->nCursor) -#define SySetGetAllocator(S) ((S)->pAllocator) -#define SySetSetUserData(S, DATA) ((S)->pUserData = DATA) -#define SySetGetUserData(S) ((S)->pUserData) -/* - * A variable length containers for generic data. - */ -struct SyBlob -{ - SyMemBackend *pAllocator; /* Memory backend */ - void *pBlob; /* Base pointer */ - sxu32 nByte; /* Total number of used bytes */ - sxu32 mByte; /* Total number of available bytes */ - sxu32 nFlags; /* Blob internal flags, see below */ -}; -#define SXBLOB_LOCKED 0x01 /* Blob is locked [i.e: Cannot auto grow] */ -#define SXBLOB_STATIC 0x02 /* Not allocated from heap */ -#define SXBLOB_RDONLY 0x04 /* Read-Only data */ - -#define SyBlobFreeSpace(BLOB) ((BLOB)->mByte - (BLOB)->nByte) -#define SyBlobLength(BLOB) ((BLOB)->nByte) -#define SyBlobData(BLOB) ((BLOB)->pBlob) -#define SyBlobCurData(BLOB) ((void*)(&((char*)(BLOB)->pBlob)[(BLOB)->nByte])) -#define SyBlobDataAt(BLOB, OFFT) ((void *)(&((char *)(BLOB)->pBlob)[OFFT])) -#define SyBlobGetAllocator(BLOB) ((BLOB)->pAllocator) - -#define SXMEM_POOL_INCR 3 -#define SXMEM_POOL_NBUCKETS 12 -#define SXMEM_BACKEND_MAGIC 0xBAC3E67D -#define SXMEM_BACKEND_CORRUPT(BACKEND) (BACKEND == 0 || BACKEND->nMagic != SXMEM_BACKEND_MAGIC) - -#define SXMEM_BACKEND_RETRY 3 -/* A memory backend subsystem is defined by an instance of the following structures */ -typedef union SyMemHeader SyMemHeader; -typedef struct SyMemBlock SyMemBlock; -struct SyMemBlock -{ - SyMemBlock *pNext, *pPrev; /* Chain of allocated memory blocks */ -#ifdef UNTRUST - sxu32 nGuard; /* magic number associated with each valid block, so we - * can detect misuse. - */ -#endif -}; -/* - * Header associated with each valid memory pool block. - */ -union SyMemHeader -{ - SyMemHeader *pNext; /* Next chunk of size 1 << (nBucket + SXMEM_POOL_INCR) in the list */ - sxu32 nBucket; /* Bucket index in aPool[] */ -}; -struct SyMemBackend -{ - const SyMutexMethods *pMutexMethods; /* Mutex methods */ - const SyMemMethods *pMethods; /* Memory allocation methods */ - SyMemBlock *pBlocks; /* List of valid memory blocks */ - sxu32 nBlock; /* Total number of memory blocks allocated so far */ - ProcMemError xMemError; /* Out-of memory callback */ - void *pUserData; /* First arg to xMemError() */ - SyMutex *pMutex; /* Per instance mutex */ - sxu32 nMagic; /* Sanity check against misuse */ - SyMemHeader *apPool[SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR]; /* Pool of memory chunks */ -}; -/* Mutex types */ -#define SXMUTEX_TYPE_FAST 1 -#define SXMUTEX_TYPE_RECURSIVE 2 -#define SXMUTEX_TYPE_STATIC_1 3 -#define SXMUTEX_TYPE_STATIC_2 4 -#define SXMUTEX_TYPE_STATIC_3 5 -#define SXMUTEX_TYPE_STATIC_4 6 -#define SXMUTEX_TYPE_STATIC_5 7 -#define SXMUTEX_TYPE_STATIC_6 8 - -#define SyMutexGlobalInit(METHOD){\ - if( (METHOD)->xGlobalInit ){\ - (METHOD)->xGlobalInit();\ - }\ -} -#define SyMutexGlobalRelease(METHOD){\ - if( (METHOD)->xGlobalRelease ){\ - (METHOD)->xGlobalRelease();\ - }\ -} -#define SyMutexNew(METHOD, TYPE) (METHOD)->xNew(TYPE) -#define SyMutexRelease(METHOD, MUTEX){\ - if( MUTEX && (METHOD)->xRelease ){\ - (METHOD)->xRelease(MUTEX);\ - }\ -} -#define SyMutexEnter(METHOD, MUTEX){\ - if( MUTEX ){\ - (METHOD)->xEnter(MUTEX);\ - }\ -} -#define SyMutexTryEnter(METHOD, MUTEX){\ - if( MUTEX && (METHOD)->xTryEnter ){\ - (METHOD)->xTryEnter(MUTEX);\ - }\ -} -#define SyMutexLeave(METHOD, MUTEX){\ - if( MUTEX ){\ - (METHOD)->xLeave(MUTEX);\ - }\ -} -/* Comparison, byte swap, byte copy macros */ -#define SX_MACRO_FAST_CMP(X1, X2, SIZE, RC){\ - register unsigned char *r1 = (unsigned char *)X1;\ - register unsigned char *r2 = (unsigned char *)X2;\ - register sxu32 LEN = SIZE;\ - for(;;){\ - if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\ - if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\ - if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\ - if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\ - }\ - RC = !LEN ? 0 : r1[0] - r2[0];\ -} -#define SX_MACRO_FAST_MEMCPY(SRC, DST, SIZ){\ - register unsigned char *xSrc = (unsigned char *)SRC;\ - register unsigned char *xDst = (unsigned char *)DST;\ - register sxu32 xLen = SIZ;\ - for(;;){\ - if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\ - if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\ - if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\ - if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\ - }\ -} -#define SX_MACRO_BYTE_SWAP(X, Y, Z){\ - register unsigned char *s = (unsigned char *)X;\ - register unsigned char *d = (unsigned char *)Y;\ - sxu32 ZLong = Z; \ - sxi32 c; \ - for(;;){\ - if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\ - if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\ - if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\ - if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\ - }\ -} -#define SX_MSEC_PER_SEC (1000) /* Millisec per seconds */ -#define SX_USEC_PER_SEC (1000000) /* Microsec per seconds */ -#define SX_NSEC_PER_SEC (1000000000) /* Nanosec per seconds */ -#endif /* SYMISC_PRIVATE_DEFS */ -/* Symisc Run-time API auxiliary definitions */ -#if !defined(SYMISC_PRIVATE_AUX_DEFS) -#define SYMISC_PRIVATE_AUX_DEFS - -typedef struct SyHashEntry_Pr SyHashEntry_Pr; -typedef struct SyHashEntry SyHashEntry; -typedef struct SyHash SyHash; -/* - * Each public hashtable entry is represented by an instance - * of the following structure. - */ -struct SyHashEntry -{ - const void *pKey; /* Hash key */ - sxu32 nKeyLen; /* Key length */ - void *pUserData; /* User private data */ -}; -#define SyHashEntryGetUserData(ENTRY) ((ENTRY)->pUserData) -#define SyHashEntryGetKey(ENTRY) ((ENTRY)->pKey) -/* Each active hashtable is identified by an instance of the following structure */ -struct SyHash -{ - SyMemBackend *pAllocator; /* Memory backend */ - ProcHash xHash; /* Hash function */ - ProcCmp xCmp; /* Comparison function */ - SyHashEntry_Pr *pList, *pCurrent; /* Linked list of hash entries user for linear traversal */ - sxu32 nEntry; /* Total number of entries */ - SyHashEntry_Pr **apBucket; /* Hash buckets */ - sxu32 nBucketSize; /* Current bucket size */ -}; -#define SXHASH_BUCKET_SIZE 16 /* Initial bucket size: must be a power of two */ -#define SXHASH_FILL_FACTOR 3 -/* Hash access macro */ -#define SyHashFunc(HASH) ((HASH)->xHash) -#define SyHashCmpFunc(HASH) ((HASH)->xCmp) -#define SyHashTotalEntry(HASH) ((HASH)->nEntry) -#define SyHashGetPool(HASH) ((HASH)->pAllocator) -/* - * An instance of the following structure define a single context - * for an Pseudo Random Number Generator. - * - * Nothing in this file or anywhere else in the library does any kind of - * encryption. The RC4 algorithm is being used as a PRNG (pseudo-random - * number generator) not as an encryption device. - * This implementation is taken from the SQLite3 source tree. - */ -typedef struct SyPRNGCtx SyPRNGCtx; -struct SyPRNGCtx -{ - sxu8 i, j; /* State variables */ - unsigned char s[256]; /* State variables */ - sxu16 nMagic; /* Sanity check */ - }; -typedef sxi32 (*ProcRandomSeed)(void *, unsigned int, void *); -/* High resolution timer.*/ -typedef struct sytime sytime; -struct sytime -{ - long tm_sec; /* seconds */ - long tm_usec; /* microseconds */ -}; -/* Forward declaration */ -typedef struct SyStream SyStream; -typedef struct SyToken SyToken; -typedef struct SyLex SyLex; -/* - * Tokenizer callback signature. - */ -typedef sxi32 (*ProcTokenizer)(SyStream *, SyToken *, void *, void *); -/* - * Each token in the input is represented by an instance - * of the following structure. - */ -struct SyToken -{ - SyString sData; /* Token text and length */ - sxu32 nType; /* Token type */ - sxu32 nLine; /* Token line number */ - void *pUserData; /* User private data associated with this token */ -}; -/* - * During tokenization, information about the state of the input - * stream is held in an instance of the following structure. - */ -struct SyStream -{ - const unsigned char *zInput; /* Complete text of the input */ - const unsigned char *zText; /* Current input we are processing */ - const unsigned char *zEnd; /* End of input marker */ - sxu32 nLine; /* Total number of processed lines */ - sxu32 nIgn; /* Total number of ignored tokens */ - SySet *pSet; /* Token containers */ -}; -/* - * Each lexer is represented by an instance of the following structure. - */ -struct SyLex -{ - SyStream sStream; /* Input stream */ - ProcTokenizer xTokenizer; /* Tokenizer callback */ - void * pUserData; /* Third argument to xTokenizer() */ - SySet *pTokenSet; /* Token set */ -}; -#define SyLexTotalToken(LEX) SySetTotalEntry(&(LEX)->aTokenSet) -#define SyLexTotalLines(LEX) ((LEX)->sStream.nLine) -#define SyLexTotalIgnored(LEX) ((LEX)->sStream.nIgn) -#define XLEX_IN_LEN(STREAM) (sxu32)(STREAM->zEnd - STREAM->zText) -#endif /* SYMISC_PRIVATE_AUX_DEFS */ -/* -** Notes on UTF-8 (According to SQLite3 authors): -** -** Byte-0 Byte-1 Byte-2 Byte-3 Value -** 0xxxxxxx 00000000 00000000 0xxxxxxx -** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx -** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx -** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx -** -*/ -/* -** Assuming zIn points to the first byte of a UTF-8 character, -** advance zIn to point to the first byte of the next UTF-8 character. -*/ -#define SX_JMP_UTF8(zIn, zEnd)\ - while(zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){ zIn++; } -#define SX_WRITE_UTF8(zOut, c) { \ - if( c<0x00080 ){ \ - *zOut++ = (sxu8)(c&0xFF); \ - }else if( c<0x00800 ){ \ - *zOut++ = 0xC0 + (sxu8)((c>>6)&0x1F); \ - *zOut++ = 0x80 + (sxu8)(c & 0x3F); \ - }else if( c<0x10000 ){ \ - *zOut++ = 0xE0 + (sxu8)((c>>12)&0x0F); \ - *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (sxu8)(c & 0x3F); \ - }else{ \ - *zOut++ = 0xF0 + (sxu8)((c>>18) & 0x07); \ - *zOut++ = 0x80 + (sxu8)((c>>12) & 0x3F); \ - *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (sxu8)(c & 0x3F); \ - } \ -} -/* Rely on the standard ctype */ -#include -#define SyToUpper(c) toupper(c) -#define SyToLower(c) tolower(c) -#define SyisUpper(c) isupper(c) -#define SyisLower(c) islower(c) -#define SyisSpace(c) isspace(c) -#define SyisBlank(c) isspace(c) -#define SyisAlpha(c) isalpha(c) -#define SyisDigit(c) isdigit(c) -#define SyisHex(c) isxdigit(c) -#define SyisPrint(c) isprint(c) -#define SyisPunct(c) ispunct(c) -#define SyisSpec(c) iscntrl(c) -#define SyisCtrl(c) iscntrl(c) -#define SyisAscii(c) isascii(c) -#define SyisAlphaNum(c) isalnum(c) -#define SyisGraph(c) isgraph(c) -#define SyDigToHex(c) "0123456789ABCDEF"[c & 0x0F] -#define SyDigToInt(c) ((c < 0xc0 && SyisDigit(c))? (c - '0') : 0 ) -#define SyCharToUpper(c) ((c < 0xc0 && SyisLower(c))? SyToUpper(c) : c) -#define SyCharToLower(c) ((c < 0xc0 && SyisUpper(c))? SyToLower(c) : c) -/* Remove white space/NUL byte from a raw string */ -#define SyStringLeftTrim(RAW)\ - while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && SyisSpace((RAW)->zString[0])){\ - (RAW)->nByte--;\ - (RAW)->zString++;\ - } -#define SyStringLeftTrimSafe(RAW)\ - while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && ((RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\ - (RAW)->nByte--;\ - (RAW)->zString++;\ - } -#define SyStringRightTrim(RAW)\ - while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\ - (RAW)->nByte--;\ - } -#define SyStringRightTrimSafe(RAW)\ - while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && \ - (( RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\ - (RAW)->nByte--;\ - } - -#define SyStringFullTrim(RAW)\ - while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && SyisSpace((RAW)->zString[0])){\ - (RAW)->nByte--;\ - (RAW)->zString++;\ - }\ - while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\ - (RAW)->nByte--;\ - } -#define SyStringFullTrimSafe(RAW)\ - while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && \ - ( (RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\ - (RAW)->nByte--;\ - (RAW)->zString++;\ - }\ - while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && \ - ( (RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\ - (RAW)->nByte--;\ - } -#ifndef JX9_DISABLE_BUILTIN_FUNC -/* - * An XML raw text, CDATA, tag name and son is parsed out and stored - * in an instance of the following structure. - */ -typedef struct SyXMLRawStr SyXMLRawStr; -struct SyXMLRawStr -{ - const char *zString; /* Raw text [UTF-8 ENCODED EXCEPT CDATA] [NOT NULL TERMINATED] */ - sxu32 nByte; /* Text length */ - sxu32 nLine; /* Line number this text occurs */ -}; -/* - * Event callback signatures. - */ -typedef sxi32 (*ProcXMLStartTagHandler)(SyXMLRawStr *, SyXMLRawStr *, sxu32, SyXMLRawStr *, void *); -typedef sxi32 (*ProcXMLTextHandler)(SyXMLRawStr *, void *); -typedef sxi32 (*ProcXMLEndTagHandler)(SyXMLRawStr *, SyXMLRawStr *, void *); -typedef sxi32 (*ProcXMLPIHandler)(SyXMLRawStr *, SyXMLRawStr *, void *); -typedef sxi32 (*ProcXMLDoctypeHandler)(SyXMLRawStr *, void *); -typedef sxi32 (*ProcXMLSyntaxErrorHandler)(const char *, int, SyToken *, void *); -typedef sxi32 (*ProcXMLStartDocument)(void *); -typedef sxi32 (*ProcXMLNameSpaceStart)(SyXMLRawStr *, SyXMLRawStr *, void *); -typedef sxi32 (*ProcXMLNameSpaceEnd)(SyXMLRawStr *, void *); -typedef sxi32 (*ProcXMLEndDocument)(void *); -/* XML processing control flags */ -#define SXML_ENABLE_NAMESPACE 0x01 /* Parse XML with namespace support enbaled */ -#define SXML_ENABLE_QUERY 0x02 /* Not used */ -#define SXML_OPTION_CASE_FOLDING 0x04 /* Controls whether case-folding is enabled for this XML parser */ -#define SXML_OPTION_SKIP_TAGSTART 0x08 /* Specify how many characters should be skipped in the beginning of a tag name.*/ -#define SXML_OPTION_SKIP_WHITE 0x10 /* Whether to skip values consisting of whitespace characters. */ -#define SXML_OPTION_TARGET_ENCODING 0x20 /* Default encoding: UTF-8 */ -/* XML error codes */ -enum xml_err_code{ - SXML_ERROR_NONE = 1, - SXML_ERROR_NO_MEMORY, - SXML_ERROR_SYNTAX, - SXML_ERROR_NO_ELEMENTS, - SXML_ERROR_INVALID_TOKEN, - SXML_ERROR_UNCLOSED_TOKEN, - SXML_ERROR_PARTIAL_CHAR, - SXML_ERROR_TAG_MISMATCH, - SXML_ERROR_DUPLICATE_ATTRIBUTE, - SXML_ERROR_JUNK_AFTER_DOC_ELEMENT, - SXML_ERROR_PARAM_ENTITY_REF, - SXML_ERROR_UNDEFINED_ENTITY, - SXML_ERROR_RECURSIVE_ENTITY_REF, - SXML_ERROR_ASYNC_ENTITY, - SXML_ERROR_BAD_CHAR_REF, - SXML_ERROR_BINARY_ENTITY_REF, - SXML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, - SXML_ERROR_MISPLACED_XML_PI, - SXML_ERROR_UNKNOWN_ENCODING, - SXML_ERROR_INCORRECT_ENCODING, - SXML_ERROR_UNCLOSED_CDATA_SECTION, - SXML_ERROR_EXTERNAL_ENTITY_HANDLING -}; -/* Each active XML SAX parser is represented by an instance - * of the following structure. - */ -typedef struct SyXMLParser SyXMLParser; -struct SyXMLParser -{ - SyMemBackend *pAllocator; /* Memory backend */ - void *pUserData; /* User private data forwarded varbatim by the XML parser - * as the last argument to the users callbacks. - */ - SyHash hns; /* Namespace hashtable */ - SySet sToken; /* XML tokens */ - SyLex sLex; /* Lexical analyzer */ - sxi32 nFlags; /* Control flags */ - /* User callbacks */ - ProcXMLStartTagHandler xStartTag; /* Start element handler */ - ProcXMLEndTagHandler xEndTag; /* End element handler */ - ProcXMLTextHandler xRaw; /* Raw text/CDATA handler */ - ProcXMLDoctypeHandler xDoctype; /* DOCTYPE handler */ - ProcXMLPIHandler xPi; /* Processing instruction (PI) handler*/ - ProcXMLSyntaxErrorHandler xError; /* Error handler */ - ProcXMLStartDocument xStartDoc; /* StartDoc handler */ - ProcXMLEndDocument xEndDoc; /* EndDoc handler */ - ProcXMLNameSpaceStart xNameSpace; /* Namespace declaration handler */ - ProcXMLNameSpaceEnd xNameSpaceEnd; /* End namespace declaration handler */ -}; -/* - * -------------- - * Archive extractor: - * -------------- - * Each open ZIP/TAR archive is identified by an instance of the following structure. - * That is, a process can open one or more archives and manipulates them in thread safe - * way by simply working with pointers to the following structure. - * Each entry in the archive is remembered in a hashtable. - * Lookup is very fast and entry with the same name are chained together. - */ - typedef struct SyArchiveEntry SyArchiveEntry; - typedef struct SyArchive SyArchive; - struct SyArchive - { - SyMemBackend *pAllocator; /* Memory backend */ - SyArchiveEntry *pCursor; /* Cursor for linear traversal of archive entries */ - SyArchiveEntry *pList; /* Pointer to the List of the loaded archive */ - SyArchiveEntry **apHash; /* Hashtable for archive entry */ - ProcRawStrCmp xCmp; /* Hash comparison function */ - ProcHash xHash; /* Hash Function */ - sxu32 nSize; /* Hashtable size */ - sxu32 nEntry; /* Total number of entries in the zip/tar archive */ - sxu32 nLoaded; /* Total number of entries loaded in memory */ - sxu32 nCentralOfft; /* Central directory offset(ZIP only. Otherwise Zero) */ - sxu32 nCentralSize; /* Central directory size(ZIP only. Otherwise Zero) */ - void *pUserData; /* Upper layer private data */ - sxu32 nMagic; /* Sanity check */ - - }; -#define SXARCH_MAGIC 0xDEAD635A -#define SXARCH_INVALID(ARCH) (ARCH == 0 || ARCH->nMagic != SXARCH_MAGIC) -#define SXARCH_ENTRY_INVALID(ENTRY) (ENTRY == 0 || ENTRY->nMagic != SXARCH_MAGIC) -#define SyArchiveHashFunc(ARCH) (ARCH)->xHash -#define SyArchiveCmpFunc(ARCH) (ARCH)->xCmp -#define SyArchiveUserData(ARCH) (ARCH)->pUserData -#define SyArchiveSetUserData(ARCH, DATA) (ARCH)->pUserData = DATA -/* - * Each loaded archive record is identified by an instance - * of the following structure. - */ - struct SyArchiveEntry - { - sxu32 nByte; /* Contents size before compression */ - sxu32 nByteCompr; /* Contents size after compression */ - sxu32 nReadCount; /* Read counter */ - sxu32 nCrc; /* Contents CRC32 */ - Sytm sFmt; /* Last-modification time */ - sxu32 nOfft; /* Data offset. */ - sxu16 nComprMeth; /* Compression method 0 == stored/8 == deflated and so on (see appnote.txt)*/ - sxu16 nExtra; /* Extra size if any */ - SyString sFileName; /* entry name & length */ - sxu32 nDup; /* Total number of entries with the same name */ - SyArchiveEntry *pNextHash, *pPrevHash; /* Hash collision chains */ - SyArchiveEntry *pNextName; /* Next entry with the same name */ - SyArchiveEntry *pNext, *pPrev; /* Next and previous entry in the list */ - sxu32 nHash; /* Hash of the entry name */ - void *pUserData; /* User data */ - sxu32 nMagic; /* Sanity check */ - }; - /* - * Extra flags for extending the file local header - */ -#define SXZIP_EXTRA_TIMESTAMP 0x001 /* Extended UNIX timestamp */ -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -#ifndef JX9_DISABLE_HASH_FUNC -/* MD5 context */ -typedef struct MD5Context MD5Context; -struct MD5Context { - sxu32 buf[4]; - sxu32 bits[2]; - unsigned char in[64]; -}; -/* SHA1 context */ -typedef struct SHA1Context SHA1Context; -struct SHA1Context { - unsigned int state[5]; - unsigned int count[2]; - unsigned char buffer[64]; -}; -#endif /* JX9_DISABLE_HASH_FUNC */ -/* JX9 private declaration */ -/* - * Memory Objects. - * Internally, the JX9 virtual machine manipulates nearly all JX9 values - * [i.e: string, int, float, resource, object, bool, null] as jx9_values structures. - * Each jx9_values struct may cache multiple representations (string, integer etc.) - * of the same value. - */ -struct jx9_value -{ - union{ - jx9_real rVal; /* Real value */ - sxi64 iVal; /* Integer value */ - void *pOther; /* Other values (Object, Array, Resource, Namespace, etc.) */ - }x; - sxi32 iFlags; /* Control flags (see below) */ - jx9_vm *pVm; /* VM this instance belong */ - SyBlob sBlob; /* String values */ - sxu32 nIdx; /* Object index in the global pool */ -}; -/* Allowed value types. - */ -#define MEMOBJ_STRING 0x001 /* Memory value is a UTF-8 string */ -#define MEMOBJ_INT 0x002 /* Memory value is an integer */ -#define MEMOBJ_REAL 0x004 /* Memory value is a real number */ -#define MEMOBJ_BOOL 0x008 /* Memory value is a boolean */ -#define MEMOBJ_NULL 0x020 /* Memory value is NULL */ -#define MEMOBJ_HASHMAP 0x040 /* Memory value is a hashmap (JSON representation of Array and Objects) */ -#define MEMOBJ_RES 0x100 /* Memory value is a resource [User private data] */ -/* Mask of all known types */ -#define MEMOBJ_ALL (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES) -/* Scalar variables - * According to the JX9 language reference manual - * Scalar variables are those containing an integer, float, string or boolean. - * Types array, object and resource are not scalar. - */ -#define MEMOBJ_SCALAR (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) -/* - * The following macro clear the current jx9_value type and replace - * it with the given one. - */ -#define MemObjSetType(OBJ, TYPE) ((OBJ)->iFlags = ((OBJ)->iFlags&~MEMOBJ_ALL)|TYPE) -/* jx9_value cast method signature */ -typedef sxi32 (*ProcMemObjCast)(jx9_value *); -/* Forward reference */ -typedef struct jx9_output_consumer jx9_output_consumer; -typedef struct jx9_user_func jx9_user_func; -typedef struct jx9_conf jx9_conf; -/* - * An instance of the following structure store the default VM output - * consumer and it's private data. - * Client-programs can register their own output consumer callback - * via the [JX9_VM_CONFIG_OUTPUT] configuration directive. - * Please refer to the official documentation for more information - * on how to register an output consumer callback. - */ -struct jx9_output_consumer -{ - ProcConsumer xConsumer; /* VM output consumer routine */ - void *pUserData; /* Third argument to xConsumer() */ - ProcConsumer xDef; /* Default output consumer routine */ - void *pDefData; /* Third argument to xDef() */ -}; -/* - * JX9 engine [i.e: jx9 instance] configuration is stored in - * an instance of the following structure. - * Please refer to the official documentation for more information - * on how to configure your jx9 engine instance. - */ -struct jx9_conf -{ - ProcConsumer xErr; /* Compile-time error consumer callback */ - void *pErrData; /* Third argument to xErr() */ - SyBlob sErrConsumer; /* Default error consumer */ -}; -/* - * Signature of the C function responsible of expanding constant values. - */ -typedef void (*ProcConstant)(jx9_value *, void *); -/* - * Each registered constant [i.e: __TIME__, __DATE__, JX9_OS, INT_MAX, etc.] is stored - * in an instance of the following structure. - * Please refer to the official documentation for more information - * on how to create/install foreign constants. - */ -typedef struct jx9_constant jx9_constant; -struct jx9_constant -{ - SyString sName; /* Constant name */ - ProcConstant xExpand; /* Function responsible of expanding constant value */ - void *pUserData; /* Last argument to xExpand() */ -}; -typedef struct jx9_aux_data jx9_aux_data; -/* - * Auxiliary data associated with each foreign function is stored - * in a stack of the following structure. - * Note that automatic tracked chunks are also stored in an instance - * of this structure. - */ -struct jx9_aux_data -{ - void *pAuxData; /* Aux data */ -}; -/* Foreign functions signature */ -typedef int (*ProcHostFunction)(jx9_context *, int, jx9_value **); -/* - * Each installed foreign function is recored in an instance of the following - * structure. - * Please refer to the official documentation for more information on how - * to create/install foreign functions. - */ -struct jx9_user_func -{ - jx9_vm *pVm; /* VM that own this instance */ - SyString sName; /* Foreign function name */ - ProcHostFunction xFunc; /* Implementation of the foreign function */ - void *pUserData; /* User private data [Refer to the official documentation for more information]*/ - SySet aAux; /* Stack of auxiliary data [Refer to the official documentation for more information]*/ -}; -/* - * The 'context' argument for an installable function. A pointer to an - * instance of this structure is the first argument to the routines used - * implement the foreign functions. - */ -struct jx9_context -{ - jx9_user_func *pFunc; /* Function information. */ - jx9_value *pRet; /* Return value is stored here. */ - SySet sVar; /* Container of dynamically allocated jx9_values - * [i.e: Garbage collection purposes.] - */ - SySet sChunk; /* Track dynamically allocated chunks [jx9_aux_data instance]. - * [i.e: Garbage collection purposes.] - */ - jx9_vm *pVm; /* Virtual machine that own this context */ - sxi32 iFlags; /* Call flags */ -}; -/* Hashmap control flags */ -#define HASHMAP_JSON_OBJECT 0x001 /* Hashmap represent JSON Object*/ -/* - * Each hashmap entry [i.e: array(4, 5, 6)] is recorded in an instance - * of the following structure. - */ -struct jx9_hashmap_node -{ - jx9_hashmap *pMap; /* Hashmap that own this instance */ - sxi32 iType; /* Node type */ - union{ - sxi64 iKey; /* Int key */ - SyBlob sKey; /* Blob key */ - }xKey; - sxi32 iFlags; /* Control flags */ - sxu32 nHash; /* Key hash value */ - sxu32 nValIdx; /* Value stored in this node */ - jx9_hashmap_node *pNext, *pPrev; /* Link to other entries [i.e: linear traversal] */ - jx9_hashmap_node *pNextCollide, *pPrevCollide; /* Collision chain */ -}; -/* - * Each active hashmap aka array in the JX9 jargon is represented - * by an instance of the following structure. - */ -struct jx9_hashmap -{ - jx9_vm *pVm; /* VM that own this instance */ - jx9_hashmap_node **apBucket; /* Hash bucket */ - jx9_hashmap_node *pFirst; /* First inserted entry */ - jx9_hashmap_node *pLast; /* Last inserted entry */ - jx9_hashmap_node *pCur; /* Current entry */ - sxu32 nSize; /* Bucket size */ - sxu32 nEntry; /* Total number of inserted entries */ - sxu32 (*xIntHash)(sxi64); /* Hash function for int_keys */ - sxu32 (*xBlobHash)(const void *, sxu32); /* Hash function for blob_keys */ - sxi32 iFlags; /* Hashmap control flags */ - sxi64 iNextIdx; /* Next available automatically assigned index */ - sxi32 iRef; /* Reference count */ -}; -/* An instance of the following structure is the context - * for the FOREACH_STEP/FOREACH_INIT VM instructions. - * Those instructions are used to implement the 'foreach' - * statement. - * This structure is made available to these instructions - * as the P3 operand. - */ -struct jx9_foreach_info -{ - SyString sKey; /* Key name. Empty otherwise*/ - SyString sValue; /* Value name */ - sxi32 iFlags; /* Control flags */ - SySet aStep; /* Stack of steps [i.e: jx9_foreach_step instance] */ -}; -struct jx9_foreach_step -{ - sxi32 iFlags; /* Control flags (see below) */ - /* Iterate on this map*/ - jx9_hashmap *pMap; /* Hashmap [i.e: array in the JX9 jargon] iteration - * Ex: foreach(array(1, 2, 3) as $key=>$value){} - */ - -}; -/* Foreach step control flags */ -#define JX9_4EACH_STEP_KEY 0x001 /* Make Key available */ -/* - * Each JX9 engine is identified by an instance of the following structure. - * Please refer to the official documentation for more information - * on how to configure your JX9 engine instance. - */ -struct jx9 -{ - SyMemBackend sAllocator; /* Low level memory allocation subsystem */ - const jx9_vfs *pVfs; /* Underlying Virtual File System */ - jx9_conf xConf; /* Configuration */ -#if defined(JX9_ENABLE_THREADS) - SyMutex *pMutex; /* Per-engine mutex */ -#endif - jx9_vm *pVms; /* List of active VM */ - sxi32 iVm; /* Total number of active VM */ - jx9 *pNext, *pPrev; /* List of active engines */ - sxu32 nMagic; /* Sanity check against misuse */ -}; -/* Code generation data structures */ -typedef sxi32 (*ProcErrorGen)(void *, sxi32, sxu32, const char *, ...); -typedef struct jx9_expr_node jx9_expr_node; -typedef struct jx9_expr_op jx9_expr_op; -typedef struct jx9_gen_state jx9_gen_state; -typedef struct GenBlock GenBlock; -typedef sxi32 (*ProcLangConstruct)(jx9_gen_state *); -typedef sxi32 (*ProcNodeConstruct)(jx9_gen_state *, sxi32); -/* - * Each supported operator [i.e: +, -, ==, *, %, >>, >=, new, etc.] is represented - * by an instance of the following structure. - * The JX9 parser does not use any external tools and is 100% handcoded. - * That is, the JX9 parser is thread-safe , full reentrant, produce consistant - * compile-time errrors and at least 7 times faster than the standard JX9 parser. - */ -struct jx9_expr_op -{ - SyString sOp; /* String representation of the operator [i.e: "+", "*", "=="...] */ - sxi32 iOp; /* Operator ID */ - sxi32 iPrec; /* Operator precedence: 1 == Highest */ - sxi32 iAssoc; /* Operator associativity (either left, right or non-associative) */ - sxi32 iVmOp; /* VM OP code for this operator [i.e: JX9_OP_EQ, JX9_OP_LT, JX9_OP_MUL...]*/ -}; -/* - * Each expression node is parsed out and recorded - * in an instance of the following structure. - */ -struct jx9_expr_node -{ - const jx9_expr_op *pOp; /* Operator ID or NULL if literal, constant, variable, function or object method call */ - jx9_expr_node *pLeft; /* Left expression tree */ - jx9_expr_node *pRight; /* Right expression tree */ - SyToken *pStart; /* Stream of tokens that belong to this node */ - SyToken *pEnd; /* End of token stream */ - sxi32 iFlags; /* Node construct flags */ - ProcNodeConstruct xCode; /* C routine responsible of compiling this node */ - SySet aNodeArgs; /* Node arguments. Only used by postfix operators [i.e: function call]*/ - jx9_expr_node *pCond; /* Condition: Only used by the ternary operator '?:' */ -}; -/* Node Construct flags */ -#define EXPR_NODE_PRE_INCR 0x01 /* Pre-icrement/decrement [i.e: ++$i, --$j] node */ -/* - * A block of instructions is recorded in an instance of the following structure. - * This structure is used only during compile-time and have no meaning - * during bytecode execution. - */ -struct GenBlock -{ - jx9_gen_state *pGen; /* State of the code generator */ - GenBlock *pParent; /* Upper block or NULL if global */ - sxu32 nFirstInstr; /* First instruction to execute */ - sxi32 iFlags; /* Block control flags (see below) */ - SySet aJumpFix; /* Jump fixup (JumpFixup instance) */ - void *pUserData; /* Upper layer private data */ - /* The following two fields are used only when compiling - * the 'do..while()' language construct. - */ - sxu8 bPostContinue; /* TRUE when compiling the do..while() statement */ - SySet aPostContFix; /* Post-continue jump fix */ -}; -/* - * Code generator state is remembered in an instance of the following - * structure. We put the information in this structure and pass around - * a pointer to this structure, rather than pass around all of the - * information separately. This helps reduce the number of arguments - * to generator functions. - * This structure is used only during compile-time and have no meaning - * during bytecode execution. - */ -struct jx9_gen_state -{ - jx9_vm *pVm; /* VM that own this instance */ - SyHash hLiteral; /* Constant string Literals table */ - SyHash hNumLiteral; /* Numeric literals table */ - SyHash hVar; /* Collected variable hashtable */ - GenBlock *pCurrent; /* Current processed block */ - GenBlock sGlobal; /* Global block */ - ProcConsumer xErr; /* Error consumer callback */ - void *pErrData; /* Third argument to xErr() */ - SyToken *pIn; /* Current processed token */ - SyToken *pEnd; /* Last token in the stream */ - sxu32 nErr; /* Total number of compilation error */ -}; -/* Forward references */ -typedef struct jx9_vm_func_static_var jx9_vm_func_static_var; -typedef struct jx9_vm_func_arg jx9_vm_func_arg; -typedef struct jx9_vm_func jx9_vm_func; -typedef struct VmFrame VmFrame; -/* - * Each collected function argument is recorded in an instance - * of the following structure. - * Note that as an extension, JX9 implements full type hinting - * which mean that any function can have it's own signature. - * Example: - * function foo(int $a, string $b, float $c, ClassInstance $d){} - * This is how the powerful function overloading mechanism is - * implemented. - * Note that as an extension, JX9 allow function arguments to have - * any complex default value associated with them unlike the standard - * JX9 engine. - * Example: - * function foo(int $a = rand() & 1023){} - * now, when foo is called without arguments [i.e: foo()] the - * $a variable (first parameter) will be set to a random number - * between 0 and 1023 inclusive. - * Refer to the official documentation for more information on this - * mechanism and other extension introduced by the JX9 engine. - */ -struct jx9_vm_func_arg -{ - SyString sName; /* Argument name */ - SySet aByteCode; /* Compiled default value associated with this argument */ - sxu32 nType; /* Type of this argument [i.e: array, int, string, float, object, etc.] */ - sxi32 iFlags; /* Configuration flags */ -}; -/* - * Each static variable is parsed out and remembered in an instance - * of the following structure. - * Note that as an extension, JX9 allow static variable have - * any complex default value associated with them unlike the standard - * JX9 engine. - * Example: - * static $rand_str = 'JX9'.rand_str(3); // Concatenate 'JX9' with - * // a random three characters(English alphabet) - * dump($rand_str); - * //You should see something like this - * string(6 'JX9awt'); - */ -struct jx9_vm_func_static_var -{ - SyString sName; /* Static variable name */ - SySet aByteCode; /* Compiled initialization expression */ - sxu32 nIdx; /* Object index in the global memory object container */ -}; -/* Function configuration flags */ -#define VM_FUNC_ARG_HAS_DEF 0x001 /* Argument has default value associated with it */ -#define VM_FUNC_ARG_IGNORE 0x002 /* Do not install argument in the current frame */ -/* - * Each user defined function is parsed out and stored in an instance - * of the following structure. - * JX9 introduced some powerfull extensions to the JX9 5 programming - * language like function overloading, type hinting, complex default - * arguments values and many more. - * Please refer to the official documentation for more information. - */ -struct jx9_vm_func -{ - SySet aArgs; /* Expected arguments (jx9_vm_func_arg instance) */ - SySet aStatic; /* Static variable (jx9_vm_func_static_var instance) */ - SyString sName; /* Function name */ - SySet aByteCode; /* Compiled function body */ - sxi32 iFlags; /* VM function configuration */ - SyString sSignature; /* Function signature used to implement function overloading - * (Refer to the official docuemntation for more information - * on this powerfull feature) - */ - void *pUserData; /* Upper layer private data associated with this instance */ - jx9_vm_func *pNextName; /* Next VM function with the same name as this one */ -}; -/* Forward reference */ -typedef struct jx9_builtin_constant jx9_builtin_constant; -typedef struct jx9_builtin_func jx9_builtin_func; -/* - * Each built-in foreign function (C function) is stored in an - * instance of the following structure. - * Please refer to the official documentation for more information - * on how to create/install foreign functions. - */ -struct jx9_builtin_func -{ - const char *zName; /* Function name [i.e: strlen(), rand(), array_merge(), etc.]*/ - ProcHostFunction xFunc; /* C routine performing the computation */ -}; -/* - * Each built-in foreign constant is stored in an instance - * of the following structure. - * Please refer to the official documentation for more information - * on how to create/install foreign constants. - */ -struct jx9_builtin_constant -{ - const char *zName; /* Constant name */ - ProcConstant xExpand; /* C routine responsible of expanding constant value*/ -}; -/* - * A single instruction of the virtual machine has an opcode - * and as many as three operands. - * Each VM instruction resulting from compiling a JX9 script - * is stored in an instance of the following structure. - */ -typedef struct VmInstr VmInstr; -struct VmInstr -{ - sxu8 iOp; /* Operation to preform */ - sxi32 iP1; /* First operand */ - sxu32 iP2; /* Second operand (Often the jump destination) */ - void *p3; /* Third operand (Often Upper layer private data) */ -}; -/* Forward reference */ -typedef struct jx9_case_expr jx9_case_expr; -typedef struct jx9_switch jx9_switch; -/* - * Each compiled case block in a swicth statement is compiled - * and stored in an instance of the following structure. - */ -struct jx9_case_expr -{ - SySet aByteCode; /* Compiled body of the case block */ - sxu32 nStart; /* First instruction to execute */ -}; -/* - * Each compiled switch statement is parsed out and stored - * in an instance of the following structure. - */ -struct jx9_switch -{ - SySet aCaseExpr; /* Compile case block */ - sxu32 nOut; /* First instruction to execute after this statement */ - sxu32 nDefault; /* First instruction to execute in the default block */ -}; -/* Assertion flags */ -#define JX9_ASSERT_DISABLE 0x01 /* Disable assertion */ -#define JX9_ASSERT_WARNING 0x02 /* Issue a warning for each failed assertion */ -#define JX9_ASSERT_BAIL 0x04 /* Terminate execution on failed assertions */ -#define JX9_ASSERT_QUIET_EVAL 0x08 /* Not used */ -#define JX9_ASSERT_CALLBACK 0x10 /* Callback to call on failed assertions */ -/* - * An instance of the following structure hold the bytecode instructions - * resulting from compiling a JX9 script. - * This structure contains the complete state of the virtual machine. - */ -struct jx9_vm -{ - SyMemBackend sAllocator; /* Memory backend */ -#if defined(JX9_ENABLE_THREADS) - SyMutex *pMutex; /* Recursive mutex associated with this VM. */ -#endif - jx9 *pEngine; /* Interpreter that own this VM */ - SySet aByteCode; /* Default bytecode container */ - SySet *pByteContainer; /* Current bytecode container */ - VmFrame *pFrame; /* Stack of active frames */ - SyPRNGCtx sPrng; /* PRNG context */ - SySet aMemObj; /* Object allocation table */ - SySet aLitObj; /* Literals allocation table */ - jx9_value *aOps; /* Operand stack */ - SySet aFreeObj; /* Stack of free memory objects */ - SyHash hConstant; /* Host-application and user defined constants container */ - SyHash hHostFunction; /* Host-application installable functions */ - SyHash hFunction; /* Compiled functions */ - SyHash hSuper; /* Global variable */ - SyBlob sConsumer; /* Default VM consumer [i.e Redirect all VM output to this blob] */ - SyBlob sWorker; /* General purpose working buffer */ - SyBlob sArgv; /* $argv[] collector [refer to the [getopt()] implementation for more information] */ - SySet aFiles; /* Stack of processed files */ - SySet aPaths; /* Set of import paths */ - SySet aIncluded; /* Set of included files */ - SySet aIOstream; /* Installed IO stream container */ - const jx9_io_stream *pDefStream; /* Default IO stream [i.e: typically this is the 'file://' stream] */ - jx9_value sExec; /* Compiled script return value [Can be extracted via the JX9_VM_CONFIG_EXEC_VALUE directive]*/ - void *pStdin; /* STDIN IO stream */ - void *pStdout; /* STDOUT IO stream */ - void *pStderr; /* STDERR IO stream */ - int bErrReport; /* TRUE to report all runtime Error/Warning/Notice */ - int nRecursionDepth; /* Current recursion depth */ - int nMaxDepth; /* Maximum allowed recusion depth */ - sxu32 nOutputLen; /* Total number of generated output */ - jx9_output_consumer sVmConsumer; /* Registered output consumer callback */ - int iAssertFlags; /* Assertion flags */ - jx9_value sAssertCallback; /* Callback to call on failed assertions */ - sxi32 iExitStatus; /* Script exit status */ - jx9_gen_state sCodeGen; /* Code generator module */ - jx9_vm *pNext, *pPrev; /* List of active VM's */ - sxu32 nMagic; /* Sanity check against misuse */ -}; -/* - * Allowed value for jx9_vm.nMagic - */ -#define JX9_VM_INIT 0xEA12CD72 /* VM correctly initialized */ -#define JX9_VM_RUN 0xBA851227 /* VM ready to execute JX9 bytecode */ -#define JX9_VM_EXEC 0xCDFE1DAD /* VM executing JX9 bytecode */ -#define JX9_VM_STALE 0xDEAD2BAD /* Stale VM */ -/* - * Error codes according to the JX9 language reference manual. - */ -enum iErrCode -{ - E_ERROR = 1, /* Fatal run-time errors. These indicate errors that can not be recovered - * from, such as a memory allocation problem. Execution of the script is - * halted. - * The only fatal error under JX9 is an out-of-memory. All others erros - * even a call to undefined function will not halt script execution. - */ - E_WARNING , /* Run-time warnings (non-fatal errors). Execution of the script is not halted. */ - E_PARSE , /* Compile-time parse errors. Parse errors should only be generated by the parser.*/ - E_NOTICE , /* Run-time notices. Indicate that the script encountered something that could - * indicate an error, but could also happen in the normal course of running a script. - */ -}; -/* - * Each VM instruction resulting from compiling a JX9 script is represented - * by one of the following OP codes. - * The program consists of a linear sequence of operations. Each operation - * has an opcode and 3 operands.Operands P1 is an integer. - * Operand P2 is an unsigned integer and operand P3 is a memory address. - * Few opcodes use all 3 operands. - */ -enum jx9_vm_op { - JX9_OP_DONE = 1, /* Done */ - JX9_OP_HALT, /* Halt */ - JX9_OP_LOAD, /* Load memory object */ - JX9_OP_LOADC, /* Load constant */ - JX9_OP_LOAD_IDX, /* Load array entry */ - JX9_OP_LOAD_MAP, /* Load hashmap('array') */ - JX9_OP_NOOP, /* NOOP */ - JX9_OP_JMP, /* Unconditional jump */ - JX9_OP_JZ, /* Jump on zero (FALSE jump) */ - JX9_OP_JNZ, /* Jump on non-zero (TRUE jump) */ - JX9_OP_POP, /* Stack POP */ - JX9_OP_CAT, /* Concatenation */ - JX9_OP_CVT_INT, /* Integer cast */ - JX9_OP_CVT_STR, /* String cast */ - JX9_OP_CVT_REAL, /* Float cast */ - JX9_OP_CALL, /* Function call */ - JX9_OP_UMINUS, /* Unary minus '-'*/ - JX9_OP_UPLUS, /* Unary plus '+'*/ - JX9_OP_BITNOT, /* Bitwise not '~' */ - JX9_OP_LNOT, /* Logical not '!' */ - JX9_OP_MUL, /* Multiplication '*' */ - JX9_OP_DIV, /* Division '/' */ - JX9_OP_MOD, /* Modulus '%' */ - JX9_OP_ADD, /* Add '+' */ - JX9_OP_SUB, /* Sub '-' */ - JX9_OP_SHL, /* Left shift '<<' */ - JX9_OP_SHR, /* Right shift '>>' */ - JX9_OP_LT, /* Less than '<' */ - JX9_OP_LE, /* Less or equal '<=' */ - JX9_OP_GT, /* Greater than '>' */ - JX9_OP_GE, /* Greater or equal '>=' */ - JX9_OP_EQ, /* Equal '==' */ - JX9_OP_NEQ, /* Not equal '!=' */ - JX9_OP_TEQ, /* Type equal '===' */ - JX9_OP_TNE, /* Type not equal '!==' */ - JX9_OP_BAND, /* Bitwise and '&' */ - JX9_OP_BXOR, /* Bitwise xor '^' */ - JX9_OP_BOR, /* Bitwise or '|' */ - JX9_OP_LAND, /* Logical and '&&','and' */ - JX9_OP_LOR, /* Logical or '||','or' */ - JX9_OP_LXOR, /* Logical xor 'xor' */ - JX9_OP_STORE, /* Store Object */ - JX9_OP_STORE_IDX, /* Store indexed object */ - JX9_OP_PULL, /* Stack pull */ - JX9_OP_SWAP, /* Stack swap */ - JX9_OP_YIELD, /* Stack yield */ - JX9_OP_CVT_BOOL, /* Boolean cast */ - JX9_OP_CVT_NUMC, /* Numeric (integer, real or both) type cast */ - JX9_OP_INCR, /* Increment ++ */ - JX9_OP_DECR, /* Decrement -- */ - JX9_OP_ADD_STORE, /* Add and store '+=' */ - JX9_OP_SUB_STORE, /* Sub and store '-=' */ - JX9_OP_MUL_STORE, /* Mul and store '*=' */ - JX9_OP_DIV_STORE, /* Div and store '/=' */ - JX9_OP_MOD_STORE, /* Mod and store '%=' */ - JX9_OP_CAT_STORE, /* Cat and store '.=' */ - JX9_OP_SHL_STORE, /* Shift left and store '>>=' */ - JX9_OP_SHR_STORE, /* Shift right and store '<<=' */ - JX9_OP_BAND_STORE, /* Bitand and store '&=' */ - JX9_OP_BOR_STORE, /* Bitor and store '|=' */ - JX9_OP_BXOR_STORE, /* Bitxor and store '^=' */ - JX9_OP_CONSUME, /* Consume VM output */ - JX9_OP_MEMBER, /* Object member run-time access */ - JX9_OP_UPLINK, /* Run-Time frame link */ - JX9_OP_CVT_NULL, /* NULL cast */ - JX9_OP_CVT_ARRAY, /* Array cast */ - JX9_OP_FOREACH_INIT, /* For each init */ - JX9_OP_FOREACH_STEP, /* For each step */ - JX9_OP_SWITCH /* Switch operation */ -}; -/* -- END-OF INSTRUCTIONS -- */ -/* - * Expression Operators ID. - */ -enum jx9_expr_id { - EXPR_OP_DOT, /* Member access */ - EXPR_OP_DC, /* :: */ - EXPR_OP_SUBSCRIPT, /* []: Subscripting */ - EXPR_OP_FUNC_CALL, /* func_call() */ - EXPR_OP_INCR, /* ++ */ - EXPR_OP_DECR, /* -- */ - EXPR_OP_BITNOT, /* ~ */ - EXPR_OP_UMINUS, /* Unary minus */ - EXPR_OP_UPLUS, /* Unary plus */ - EXPR_OP_TYPECAST, /* Type cast [i.e: (int), (float), (string)...] */ - EXPR_OP_ALT, /* @ */ - EXPR_OP_INSTOF, /* instanceof */ - EXPR_OP_LOGNOT, /* logical not ! */ - EXPR_OP_MUL, /* Multiplication */ - EXPR_OP_DIV, /* division */ - EXPR_OP_MOD, /* Modulus */ - EXPR_OP_ADD, /* Addition */ - EXPR_OP_SUB, /* Substraction */ - EXPR_OP_DDOT, /* Concatenation */ - EXPR_OP_SHL, /* Left shift */ - EXPR_OP_SHR, /* Right shift */ - EXPR_OP_LT, /* Less than */ - EXPR_OP_LE, /* Less equal */ - EXPR_OP_GT, /* Greater than */ - EXPR_OP_GE, /* Greater equal */ - EXPR_OP_EQ, /* Equal == */ - EXPR_OP_NE, /* Not equal != <> */ - EXPR_OP_TEQ, /* Type equal === */ - EXPR_OP_TNE, /* Type not equal !== */ - EXPR_OP_SEQ, /* String equal 'eq' */ - EXPR_OP_SNE, /* String not equal 'ne' */ - EXPR_OP_BAND, /* Biwise and '&' */ - EXPR_OP_REF, /* Reference operator '&' */ - EXPR_OP_XOR, /* bitwise xor '^' */ - EXPR_OP_BOR, /* bitwise or '|' */ - EXPR_OP_LAND, /* Logical and '&&','and' */ - EXPR_OP_LOR, /* Logical or '||','or'*/ - EXPR_OP_LXOR, /* Logical xor 'xor' */ - EXPR_OP_QUESTY, /* Ternary operator '?' */ - EXPR_OP_ASSIGN, /* Assignment '=' */ - EXPR_OP_ADD_ASSIGN, /* Combined operator: += */ - EXPR_OP_SUB_ASSIGN, /* Combined operator: -= */ - EXPR_OP_MUL_ASSIGN, /* Combined operator: *= */ - EXPR_OP_DIV_ASSIGN, /* Combined operator: /= */ - EXPR_OP_MOD_ASSIGN, /* Combined operator: %= */ - EXPR_OP_DOT_ASSIGN, /* Combined operator: .= */ - EXPR_OP_AND_ASSIGN, /* Combined operator: &= */ - EXPR_OP_OR_ASSIGN, /* Combined operator: |= */ - EXPR_OP_XOR_ASSIGN, /* Combined operator: ^= */ - EXPR_OP_SHL_ASSIGN, /* Combined operator: <<= */ - EXPR_OP_SHR_ASSIGN, /* Combined operator: >>= */ - EXPR_OP_COMMA /* Comma expression */ -}; -/* - * Lexer token codes - * The following set of constants are the tokens recognized - * by the lexer when processing JX9 input. - * Important: Token values MUST BE A POWER OF TWO. - */ -#define JX9_TK_INTEGER 0x0000001 /* Integer */ -#define JX9_TK_REAL 0x0000002 /* Real number */ -#define JX9_TK_NUM (JX9_TK_INTEGER|JX9_TK_REAL) /* Numeric token, either integer or real */ -#define JX9_TK_KEYWORD 0x0000004 /* Keyword [i.e: while, for, if, foreach...] */ -#define JX9_TK_ID 0x0000008 /* Alphanumeric or UTF-8 stream */ -#define JX9_TK_DOLLAR 0x0000010 /* '$' Dollar sign */ -#define JX9_TK_OP 0x0000020 /* Operator [i.e: +, *, /...] */ -#define JX9_TK_OCB 0x0000040 /* Open curly brace'{' */ -#define JX9_TK_CCB 0x0000080 /* Closing curly brace'}' */ -#define JX9_TK_DOT 0x0000100 /* Dot . */ -#define JX9_TK_LPAREN 0x0000200 /* Left parenthesis '(' */ -#define JX9_TK_RPAREN 0x0000400 /* Right parenthesis ')' */ -#define JX9_TK_OSB 0x0000800 /* Open square bracket '[' */ -#define JX9_TK_CSB 0x0001000 /* Closing square bracket ']' */ -#define JX9_TK_DSTR 0x0002000 /* Double quoted string "$str" */ -#define JX9_TK_SSTR 0x0004000 /* Single quoted string 'str' */ -#define JX9_TK_NOWDOC 0x0010000 /* Nowdoc <<< */ -#define JX9_TK_COMMA 0x0020000 /* Comma ',' */ -#define JX9_TK_SEMI 0x0040000 /* Semi-colon ";" */ -#define JX9_TK_BSTR 0x0080000 /* Backtick quoted string [i.e: Shell command `date`] */ -#define JX9_TK_COLON 0x0100000 /* single Colon ':' */ -#define JX9_TK_AMPER 0x0200000 /* Ampersand '&' */ -#define JX9_TK_EQUAL 0x0400000 /* Equal '=' */ -#define JX9_TK_OTHER 0x1000000 /* Other symbols */ -/* - * JX9 keyword. - * These words have special meaning in JX9. Some of them represent things which look like - * functions, some look like constants, and so on, but they're not, really: they are language constructs. - * You cannot use any of the following words as constants, object names, function or method names. - * Using them as variable names is generally OK, but could lead to confusion. - */ -#define JX9_TKWRD_SWITCH 1 /* switch */ -#define JX9_TKWRD_PRINT 2 /* print */ -#define JX9_TKWRD_ELIF 0x4000000 /* elseif: MUST BE A POWER OF TWO */ -#define JX9_TKWRD_ELSE 0x8000000 /* else: MUST BE A POWER OF TWO */ -#define JX9_TKWRD_IF 3 /* if */ -#define JX9_TKWRD_STATIC 4 /* static */ -#define JX9_TKWRD_CASE 5 /* case */ -#define JX9_TKWRD_FUNCTION 6 /* function */ -#define JX9_TKWRD_CONST 7 /* const */ -/* The number '8' is reserved for JX9_TK_ID */ -#define JX9_TKWRD_WHILE 9 /* while */ -#define JX9_TKWRD_DEFAULT 10 /* default */ -#define JX9_TKWRD_AS 11 /* as */ -#define JX9_TKWRD_CONTINUE 12 /* continue */ -#define JX9_TKWRD_EXIT 13 /* exit */ -#define JX9_TKWRD_DIE 14 /* die */ -#define JX9_TKWRD_IMPORT 15 /* import */ -#define JX9_TKWRD_INCLUDE 16 /* include */ -#define JX9_TKWRD_FOR 17 /* for */ -#define JX9_TKWRD_FOREACH 18 /* foreach */ -#define JX9_TKWRD_RETURN 19 /* return */ -#define JX9_TKWRD_BREAK 20 /* break */ -#define JX9_TKWRD_UPLINK 21 /* uplink */ -#define JX9_TKWRD_BOOL 0x8000 /* bool: MUST BE A POWER OF TWO */ -#define JX9_TKWRD_INT 0x10000 /* int: MUST BE A POWER OF TWO */ -#define JX9_TKWRD_FLOAT 0x20000 /* float: MUST BE A POWER OF TWO */ -#define JX9_TKWRD_STRING 0x40000 /* string: MUST BE A POWER OF TWO */ - -/* api.c */ -JX9_PRIVATE sxi32 jx9EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap); -JX9_PRIVATE int jx9DeleteFunction(jx9_vm *pVm,const char *zName); -JX9_PRIVATE int Jx9DeleteConstant(jx9_vm *pVm,const char *zName); -/* json.c function prototypes */ -JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut); -JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte); -/* memobj.c function prototypes */ -JX9_PRIVATE sxi32 jx9MemObjDump(SyBlob *pOut, jx9_value *pObj); -JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal); -JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore); -JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest); -JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal); -JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray); -#if 0 -/* Not used in the current release of the JX9 engine */ -JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal); -#endif -JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal); -JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal); -JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj); -JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen); -#if 0 -/* Not used in the current release of the JX9 engine */ -JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap); -#endif -JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest); -JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest); -JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj); -JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj); -JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj); -JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags); -JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj); -JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj); -JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj); -JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj); -JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj); -JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj); -JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj); -JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj); -JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pData); -/* lex.c function prototypes */ -JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput, sxu32 nLen, SySet *pOut); -/* vm.c function prototypes */ -JX9_PRIVATE void jx9VmReleaseContextValue(jx9_context *pCtx, jx9_value *pValue); -JX9_PRIVATE sxi32 jx9VmInitFuncState(jx9_vm *pVm, jx9_vm_func *pFunc, const char *zName, sxu32 nByte, - sxi32 iFlags, void *pUserData); -JX9_PRIVATE sxi32 jx9VmInstallUserFunction(jx9_vm *pVm, jx9_vm_func *pFunc, SyString *pName); -JX9_PRIVATE sxi32 jx9VmRegisterConstant(jx9_vm *pVm, const SyString *pName, ProcConstant xExpand, void *pUserData); -JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(jx9_vm *pVm, const SyString *pName, ProcHostFunction xFunc, void *pUserData); -JX9_PRIVATE sxi32 jx9VmBlobConsumer(const void *pSrc, unsigned int nLen, void *pUserData); -JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIndex); -JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex); -JX9_PRIVATE sxi32 jx9VmOutputConsume(jx9_vm *pVm, SyString *pString); -JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(jx9_vm *pVm, const char *zFormat, va_list ap); -JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap); -JX9_PRIVATE sxi32 jx9VmThrowError(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zMessage); -JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData); -JX9_PRIVATE sxi32 jx9VmDump(jx9_vm *pVm, ProcConsumer xConsumer, void *pUserData); -JX9_PRIVATE sxi32 jx9VmInit(jx9_vm *pVm, jx9 *pEngine); -JX9_PRIVATE sxi32 jx9VmConfigure(jx9_vm *pVm, sxi32 nOp, va_list ap); -JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm); -JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar); -JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm); -JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm); -JX9_PRIVATE sxi32 jx9VmMakeReady(jx9_vm *pVm); -JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm); -JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm); -JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm); -JX9_PRIVATE VmInstr *jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex); -JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm); -JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer); -JX9_PRIVATE sxi32 jx9VmEmitInstr(jx9_vm *pVm, sxi32 iOp, sxi32 iP1, sxu32 iP2, void *p3, sxu32 *pIndex); -JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm); -JX9_PRIVATE sxi32 jx9VmCallUserFunction(jx9_vm *pVm, jx9_value *pFunc, int nArg, jx9_value **apArg, jx9_value *pResult); -JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(jx9_vm *pVm, jx9_value *pFunc, jx9_value *pResult, ...); -JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm, sxu32 nObjIdx); -JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen); -JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue); -JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew); -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(jx9_vm *pVm, const char **pzDevice, int nByte); -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -JX9_PRIVATE int jx9Utf8Read( - const unsigned char *z, /* First byte of UTF-8 character */ - const unsigned char *zTerm, /* Pretend this byte is 0x00 */ - const unsigned char **pzNext /* Write first byte past UTF-8 char here */ -); -/* parse.c function prototypes */ -JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID); -JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot); -JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart, SyToken *pEnd, SyToken **ppNext); -JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn, SyToken *pEnd, sxu32 nTokStart, sxu32 nTokEnd, SyToken **ppEnd); -JX9_PRIVATE const jx9_expr_op * jx9ExprExtractOperator(SyString *pStr, SyToken *pLast); -JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet); -/* compile.c function prototypes */ -JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType); -JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen, sxi32 iCompileFlag); -JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag); -JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag); -JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen, sxi32 iCompileFlag); -JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen, sxi32 iCompileFlag); -JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag); -JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag); -JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen, sxi32 iCompileFlag); -JX9_PRIVATE sxi32 jx9InitCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData); -JX9_PRIVATE sxi32 jx9ResetCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData); -JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen, sxi32 nErrType, sxu32 nLine, const char *zFormat, ...); -JX9_PRIVATE sxi32 jx9CompileScript(jx9_vm *pVm, SyString *pScript, sxi32 iFlags); -/* constant.c function prototypes */ -JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm); -/* builtin.c function prototypes */ -JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm); -/* hashmap.c function prototypes */ -JX9_PRIVATE jx9_hashmap * jx9NewHashmap(jx9_vm *pVm, sxu32 (*xIntHash)(sxi64), sxu32 (*xBlobHash)(const void *, sxu32)); -JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm); -JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS); -JX9_PRIVATE void jx9HashmapUnref(jx9_hashmap *pMap); -JX9_PRIVATE sxi32 jx9HashmapLookup(jx9_hashmap *pMap, jx9_value *pKey, jx9_hashmap_node **ppNode); -JX9_PRIVATE sxi32 jx9HashmapInsert(jx9_hashmap *pMap, jx9_value *pKey, jx9_value *pVal); -JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight); -JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest); -JX9_PRIVATE sxi32 jx9HashmapCmp(jx9_hashmap *pLeft, jx9_hashmap *pRight, int bStrict); -JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap); -JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap); -JX9_PRIVATE jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode); -JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore); -JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode, jx9_value *pKey); -JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm); -JX9_PRIVATE sxi32 jx9HashmapWalk(jx9_hashmap *pMap, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData); -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut); -/* builtin.c function prototypes */ -JX9_PRIVATE sxi32 jx9InputFormat(int (*xConsumer)(jx9_context *, const char *, int, void *), - jx9_context *pCtx, const char *zIn, int nByte, int nArg, jx9_value **apArg, void *pUserData, int vf); -JX9_PRIVATE sxi32 jx9ProcessCsv(const char *zInput, int nByte, int delim, int encl, - int escape, sxi32 (*xConsumer)(const char *, int, void *), void *pUserData); -JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData); -JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen); -JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection); -#endif -/* vfs.c */ -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile, - int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew); -JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut); -JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle); -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen); -JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm); -JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void); -JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm); -JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm); -JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm); -/* lib.c function prototypes */ -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp); -JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch); -JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch); -JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry); -JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen); -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData); -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -#ifndef JX9_DISABLE_BUILTIN_FUNC -#ifndef JX9_DISABLE_HASH_FUNC -JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen); -JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len); -JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx); -JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx); -JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16]); -JX9_PRIVATE void SHA1Init(SHA1Context *context); -JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len); -JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]); -JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20]); -#endif -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen); -JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void *pUserData); -JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...); -JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap); -JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...); -JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...); -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE const char *SyTimeGetMonth(sxi32 iMonth); -JX9_PRIVATE const char *SyTimeGetDay(sxi32 iDay); -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8); -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData); -#endif -JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex); -JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp); -JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData); -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData); -JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData); -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -JX9_PRIVATE sxu32 SyBinHash(const void *pSrc, sxu32 nLen); -JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest); -JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest); -JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest); -JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest); -JX9_PRIVATE sxi32 SyHexToint(sxi32 c); -JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest); -JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest); -JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char **pzTail); -JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData); -JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32(*xStep)(SyHashEntry *, void *), void *pUserData); -JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData); -JX9_PRIVATE SyHashEntry *SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen); -JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash); -JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp); -JX9_PRIVATE void *SySetAt(SySet *pSet, sxu32 nIdx); -JX9_PRIVATE void *SySetPop(SySet *pSet); -JX9_PRIVATE void *SySetPeek(SySet *pSet); -JX9_PRIVATE sxi32 SySetRelease(SySet *pSet); -JX9_PRIVATE sxi32 SySetReset(SySet *pSet); -JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet); -JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry); -JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem); -JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem); -JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize); -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft); -#endif -JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob); -JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob); -JX9_PRIVATE sxi32 SyBlobTruncate(SyBlob *pBlob,sxu32 nNewLen); -JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest); -JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob); -JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize); -JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte); -JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator); -JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize); -JX9_PRIVATE char *SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize); -JX9_PRIVATE void *SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize); -JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend); -JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void *pUserData); -JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void *pUserData); -JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,const SyMemBackend *pParent); -#if 0 -/* Not used in the current release of the JX9 engine */ -JX9_PRIVATE void *SyMemBackendPoolRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte); -#endif -JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void *pChunk); -JX9_PRIVATE void *SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte); -JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void *pChunk); -JX9_PRIVATE void *SyMemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte); -JX9_PRIVATE void *SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte); -#if defined(JX9_ENABLE_THREADS) -JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods); -JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend); -#endif -JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen); -JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize); -JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize); -JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen); -JX9_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen); -#if !defined(JX9_DISABLE_BUILTIN_FUNC) || defined(__APPLE__) -JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen); -#endif -JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos); -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos); -#endif -JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos); -JX9_PRIVATE sxu32 SyStrlen(const char *zSrc); -#if defined(JX9_ENABLE_THREADS) -JX9_PRIVATE const SyMutexMethods *SyMutexExportMethods(void); -JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods); -JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend); -#endif -JX9_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb); -JX9_PRIVATE void SyBigEndianUnpack32(const unsigned char *buf,sxu32 *uNB); -JX9_PRIVATE void SyBigEndianPack16(unsigned char *buf,sxu16 nb); -JX9_PRIVATE void SyBigEndianUnpack16(const unsigned char *buf,sxu16 *uNB); -JX9_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64); -JX9_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64); -JX9_PRIVATE sxi32 SyBlobAppendBig64(SyBlob *pBlob,sxu64 n64); -JX9_PRIVATE sxi32 SyBlobAppendBig32(SyBlob *pBlob,sxu32 n32); -JX9_PRIVATE sxi32 SyBlobAppendBig16(SyBlob *pBlob,sxu16 n16); -JX9_PRIVATE void SyTimeFormatToDos(Sytm *pFmt,sxu32 *pOut); -JX9_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut); -#endif /* __JX9INT_H__ */ - -/* - * ---------------------------------------------------------- - * File: unqliteInt.h - * MD5: 325816ce05f6adbaab2c39a41875dedd - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: unqliteInt.h v1.7 FreeBSD 2012-11-02 11:25 devel $ */ -#ifndef __UNQLITEINT_H__ -#define __UNQLITEINT_H__ -/* Internal interface definitions for UnQLite. */ -#ifdef UNQLITE_AMALGAMATION -/* Marker for routines not intended for external use */ -#define UNQLITE_PRIVATE static -#define JX9_AMALGAMATION -#else -#define UNQLITE_PRIVATE -#include "unqlite.h" -#include "jx9Int.h" -#endif -/* forward declaration */ -typedef struct unqlite_db unqlite_db; -/* -** The following values may be passed as the second argument to -** UnqliteOsLock(). The various locks exhibit the following semantics: -** -** SHARED: Any number of processes may hold a SHARED lock simultaneously. -** RESERVED: A single process may hold a RESERVED lock on a file at -** any time. Other processes may hold and obtain new SHARED locks. -** PENDING: A single process may hold a PENDING lock on a file at -** any one time. Existing SHARED locks may persist, but no new -** SHARED locks may be obtained by other processes. -** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. -** -** PENDING_LOCK may not be passed directly to UnqliteOsLock(). Instead, a -** process that requests an EXCLUSIVE lock may actually obtain a PENDING -** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to -** UnqliteOsLock(). -*/ -#define NO_LOCK 0 -#define SHARED_LOCK 1 -#define RESERVED_LOCK 2 -#define PENDING_LOCK 3 -#define EXCLUSIVE_LOCK 4 -/* - * UnQLite Locking Strategy (Same as SQLite3) - * - * The following #defines specify the range of bytes used for locking. - * SHARED_SIZE is the number of bytes available in the pool from which - * a random byte is selected for a shared lock. The pool of bytes for - * shared locks begins at SHARED_FIRST. - * - * The same locking strategy and byte ranges are used for Unix and Windows. - * This leaves open the possiblity of having clients on winNT, and - * unix all talking to the same shared file and all locking correctly. - * To do so would require that samba (or whatever - * tool is being used for file sharing) implements locks correctly between - * windows and unix. I'm guessing that isn't likely to happen, but by - * using the same locking range we are at least open to the possibility. - * - * Locking in windows is mandatory. For this reason, we cannot store - * actual data in the bytes used for locking. The pager never allocates - * the pages involved in locking therefore. SHARED_SIZE is selected so - * that all locks will fit on a single page even at the minimum page size. - * PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE - * is set high so that we don't have to allocate an unused page except - * for very large databases. But one should test the page skipping logic - * by setting PENDING_BYTE low and running the entire regression suite. - * - * Changing the value of PENDING_BYTE results in a subtly incompatible - * file format. Depending on how it is changed, you might not notice - * the incompatibility right away, even running a full regression test. - * The default location of PENDING_BYTE is the first byte past the - * 1GB boundary. - */ -#define PENDING_BYTE (0x40000000) -#define RESERVED_BYTE (PENDING_BYTE+1) -#define SHARED_FIRST (PENDING_BYTE+2) -#define SHARED_SIZE 510 -/* - * The default size of a disk sector in bytes. - */ -#ifndef UNQLITE_DEFAULT_SECTOR_SIZE -#define UNQLITE_DEFAULT_SECTOR_SIZE 512 -#endif -/* - * Each open database file is managed by a separate instance - * of the "Pager" structure. - */ -typedef struct Pager Pager; -/* - * Each database file to be accessed by the system is an instance - * of the following structure. - */ -struct unqlite_db -{ - Pager *pPager; /* Pager and Transaction manager */ - jx9 *pJx9; /* Jx9 Engine handle */ - unqlite_kv_cursor *pCursor; /* Database cursor for common usage */ -}; -/* - * Each database connection is an instance of the following structure. - */ -struct unqlite -{ - SyMemBackend sMem; /* Memory allocator subsystem */ - SyBlob sErr; /* Error log */ - unqlite_db sDB; /* Storage backend */ -#if defined(UNQLITE_ENABLE_THREADS) - const SyMutexMethods *pMethods; /* Mutex methods */ - SyMutex *pMutex; /* Per-handle mutex */ -#endif - unqlite_vm *pVms; /* List of active VM */ - sxi32 iVm; /* Total number of active VM */ - sxi32 iFlags; /* Control flags (See below) */ - unqlite *pNext,*pPrev; /* List of active DB handles */ - sxu32 nMagic; /* Sanity check against misuse */ -}; -#define UNQLITE_FL_DISABLE_AUTO_COMMIT 0x001 /* Disable auto-commit on close */ -/* - * VM control flags (Mostly related to collection handling). - */ -#define UNQLITE_VM_COLLECTION_CREATE 0x001 /* Create a new collection */ -#define UNQLITE_VM_COLLECTION_OVERWRITE 0x002 /* Overwrite old collection */ -#define UNQLITE_VM_AUTO_LOAD 0x004 /* Auto load a collection from the vfs */ -/* Forward declaration */ -typedef struct unqlite_col_record unqlite_col_record; -typedef struct unqlite_col unqlite_col; -/* - * Each an in-memory collection record is stored in an instance - * of the following structure. - */ -struct unqlite_col_record -{ - unqlite_col *pCol; /* Collecion this record belong */ - jx9_int64 nId; /* Unique record ID */ - jx9_value sValue; /* In-memory value of the record */ - unqlite_col_record *pNextCol,*pPrevCol; /* Collision chain */ - unqlite_col_record *pNext,*pPrev; /* Linked list of records */ -}; -/* - * Magic number to identify a valid collection on disk. - */ -#define UNQLITE_COLLECTION_MAGIC 0x611E /* sizeof(unsigned short) 2 bytes */ -/* - * A loaded collection is identified by an instance of the following structure. - */ -struct unqlite_col -{ - unqlite_vm *pVm; /* VM that own this instance */ - SyString sName; /* ID of the collection */ - sxu32 nHash; /* sName hash */ - jx9_value sSchema; /* Collection schema */ - sxu32 nSchemaOfft; /* Shema offset in sHeader */ - SyBlob sWorker; /* General purpose working buffer */ - SyBlob sHeader; /* Collection binary header */ - jx9_int64 nLastid; /* Last collection record ID */ - jx9_int64 nCurid; /* Current record ID */ - jx9_int64 nTotRec; /* Total number of records in the collection */ - int iFlags; /* Control flags (see below) */ - unqlite_col_record **apRecord; /* Hashtable of loaded records */ - unqlite_col_record *pList; /* Linked list of records */ - sxu32 nRec; /* Total number of records in apRecord[] */ - sxu32 nRecSize; /* apRecord[] size */ - Sytm sCreation; /* Colleation creation time */ - unqlite_kv_cursor *pCursor; /* Cursor pointing to the raw binary data */ - unqlite_col *pNext,*pPrev; /* Next and previous collection in the chain */ - unqlite_col *pNextCol,*pPrevCol; /* Collision chain */ -}; -/* - * Each unQLite Virtual Machine resulting from successful compilation of - * a Jx9 script is represented by an instance of the following structure. - */ -struct unqlite_vm -{ - unqlite *pDb; /* Database handle that own this instance */ - SyMemBackend sAlloc; /* Private memory allocator */ -#if defined(UNQLITE_ENABLE_THREADS) - SyMutex *pMutex; /* Recursive mutex associated with this VM. */ -#endif - unqlite_col **apCol; /* Table of loaded collections */ - unqlite_col *pCol; /* List of loaded collections */ - sxu32 iCol; /* Total number of loaded collections */ - sxu32 iColSize; /* apCol[] size */ - jx9_vm *pJx9Vm; /* Compiled Jx9 script*/ - unqlite_vm *pNext,*pPrev; /* Linked list of active unQLite VM */ - sxu32 nMagic; /* Magic number to avoid misuse */ -}; -/* - * Database signature to identify a valid database image. - */ -#define UNQLITE_DB_SIG "unqlite" -/* - * Database magic number (4 bytes). - */ -#define UNQLITE_DB_MAGIC 0xDB7C2712 -/* - * Maximum page size in bytes. - */ -#ifdef UNQLITE_MAX_PAGE_SIZE -# undef UNQLITE_MAX_PAGE_SIZE -#endif -#define UNQLITE_MAX_PAGE_SIZE 65536 /* 65K */ -/* - * Minimum page size in bytes. - */ -#ifdef UNQLITE_MIN_PAGE_SIZE -# undef UNQLITE_MIN_PAGE_SIZE -#endif -#define UNQLITE_MIN_PAGE_SIZE 512 -/* - * The default size of a database page. - */ -#ifndef UNQLITE_DEFAULT_PAGE_SIZE -# undef UNQLITE_DEFAULT_PAGE_SIZE -#endif -# define UNQLITE_DEFAULT_PAGE_SIZE 4096 /* 4K */ -/* Forward declaration */ -typedef struct Bitvec Bitvec; -/* Private library functions */ -/* api.c */ -UNQLITE_PRIVATE const SyMemBackend * unqliteExportMemBackend(void); -UNQLITE_PRIVATE int unqliteDataConsumer( - const void *pOut, /* Data to consume */ - unsigned int nLen, /* Data length */ - void *pUserData /* User private data */ - ); -UNQLITE_PRIVATE unqlite_kv_methods * unqliteFindKVStore( - const char *zName, /* Storage engine name [i.e. Hash, B+tree, LSM, etc.] */ - sxu32 nByte /* zName length */ - ); -UNQLITE_PRIVATE int unqliteGetPageSize(void); -UNQLITE_PRIVATE int unqliteGenError(unqlite *pDb,const char *zErr); -UNQLITE_PRIVATE int unqliteGenErrorFormat(unqlite *pDb,const char *zFmt,...); -UNQLITE_PRIVATE int unqliteGenOutofMem(unqlite *pDb); -/* unql_vm.c */ -UNQLITE_PRIVATE int unqliteCreateCollection(unqlite_vm *pVm,SyString *pName); -UNQLITE_PRIVATE jx9_int64 unqliteCollectionLastRecordId(unqlite_col *pCol); -UNQLITE_PRIVATE jx9_int64 unqliteCollectionCurrentRecordId(unqlite_col *pCol); -UNQLITE_PRIVATE int unqliteCollectionCacheRemoveRecord(unqlite_col *pCol,jx9_int64 nId); -UNQLITE_PRIVATE jx9_int64 unqliteCollectionTotalRecords(unqlite_col *pCol); -UNQLITE_PRIVATE void unqliteCollectionResetRecordCursor(unqlite_col *pCol); -UNQLITE_PRIVATE int unqliteCollectionFetchNextRecord(unqlite_col *pCol,jx9_value *pValue); -UNQLITE_PRIVATE int unqliteCollectionFetchRecordById(unqlite_col *pCol,jx9_int64 nId,jx9_value *pValue); -UNQLITE_PRIVATE unqlite_col * unqliteCollectionFetch(unqlite_vm *pVm,SyString *pCol,int iFlag); -UNQLITE_PRIVATE int unqliteCollectionSetSchema(unqlite_col *pCol,jx9_value *pValue); -UNQLITE_PRIVATE int unqliteCollectionPut(unqlite_col *pCol,jx9_value *pValue,int iFlag); -UNQLITE_PRIVATE int unqliteCollectionDropRecord(unqlite_col *pCol,jx9_int64 nId,int wr_header,int log_err); -UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol); -/* unql_jx9.c */ -UNQLITE_PRIVATE int unqliteRegisterJx9Functions(unqlite_vm *pVm); -/* fastjson.c */ -UNQLITE_PRIVATE sxi32 FastJsonEncode( - jx9_value *pValue, /* Value to encode */ - SyBlob *pOut, /* Store encoded value here */ - int iNest /* Nesting limit */ - ); -UNQLITE_PRIVATE sxi32 FastJsonDecode( - const void *pIn, /* Binary JSON */ - sxu32 nByte, /* Chunk delimiter */ - jx9_value *pOut, /* Decoded value */ - const unsigned char **pzPtr, - int iNest /* Nesting limit */ - ); -/* vfs.c [io_win.c, io_unix.c ] */ -UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void); -/* mem_kv.c */ -UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void); -/* lhash_kv.c */ -UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void); -/* os.c */ -UNQLITE_PRIVATE int unqliteOsRead(unqlite_file *id, void *pBuf, unqlite_int64 amt, unqlite_int64 offset); -UNQLITE_PRIVATE int unqliteOsWrite(unqlite_file *id, const void *pBuf, unqlite_int64 amt, unqlite_int64 offset); -UNQLITE_PRIVATE int unqliteOsTruncate(unqlite_file *id, unqlite_int64 size); -UNQLITE_PRIVATE int unqliteOsSync(unqlite_file *id, int flags); -UNQLITE_PRIVATE int unqliteOsFileSize(unqlite_file *id, unqlite_int64 *pSize); -UNQLITE_PRIVATE int unqliteOsLock(unqlite_file *id, int lockType); -UNQLITE_PRIVATE int unqliteOsUnlock(unqlite_file *id, int lockType); -UNQLITE_PRIVATE int unqliteOsCheckReservedLock(unqlite_file *id, int *pResOut); -UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id); -UNQLITE_PRIVATE int unqliteOsOpen( - unqlite_vfs *pVfs, - SyMemBackend *pAlloc, - const char *zPath, - unqlite_file **ppOut, - unsigned int flags -); -UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId); -UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int dirSync); -UNQLITE_PRIVATE int unqliteOsAccess(unqlite_vfs *pVfs,const char *zPath,int flags,int *pResOut); -/* bitmap.c */ -UNQLITE_PRIVATE Bitvec *unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize); -UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i); -UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i); -UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p); -/* pager.c */ -UNQLITE_PRIVATE int unqliteInitCursor(unqlite *pDb,unqlite_kv_cursor **ppOut); -UNQLITE_PRIVATE int unqliteReleaseCursor(unqlite *pDb,unqlite_kv_cursor *pCur); -UNQLITE_PRIVATE int unqlitePagerSetCachesize(Pager *pPager,int mxPage); -UNQLITE_PRIVATE int unqlitePagerClose(Pager *pPager); -UNQLITE_PRIVATE int unqlitePagerOpen( - unqlite_vfs *pVfs, /* The virtual file system to use */ - unqlite *pDb, /* Database handle */ - const char *zFilename, /* Name of the database file to open */ - unsigned int iFlags /* flags controlling this file */ - ); -UNQLITE_PRIVATE int unqlitePagerRegisterKvEngine(Pager *pPager,unqlite_kv_methods *pMethods); -UNQLITE_PRIVATE unqlite_kv_engine * unqlitePagerGetKvEngine(unqlite *pDb); -UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager); -UNQLITE_PRIVATE int unqlitePagerCommit(Pager *pPager); -UNQLITE_PRIVATE int unqlitePagerRollback(Pager *pPager,int bResetKvEngine); -UNQLITE_PRIVATE void unqlitePagerRandomString(Pager *pPager,char *zBuf,sxu32 nLen); -UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager); -#endif /* __UNQLITEINT_H__ */ -/* - * ---------------------------------------------------------- - * File: api.c - * MD5: 5d020de5ba84f99af867d524f4f99769 - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: api.c v2.0 FreeBSD 2012-11-08 23:07 stable $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif -/* This file implement the public interfaces presented to host-applications. - * Routines in other files are for internal use by UnQLite and should not be - * accessed by users of the library. - */ -#define UNQLITE_DB_MISUSE(DB) (DB == 0 || DB->nMagic != UNQLITE_DB_MAGIC) -#define UNQLITE_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE) -/* If another thread have released a working instance, the following macros - * evaluates to true. These macros are only used when the library - * is built with threading support enabled. - */ -#define UNQLITE_THRD_DB_RELEASE(DB) (DB->nMagic != UNQLITE_DB_MAGIC) -#define UNQLITE_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE) -/* IMPLEMENTATION: unqlite@embedded@symisc 118-09-4785 */ -/* - * All global variables are collected in the structure named "sUnqlMPGlobal". - * That way it is clear in the code when we are using static variable because - * its name start with sUnqlMPGlobal. - */ -static struct unqlGlobal_Data -{ - SyMemBackend sAllocator; /* Global low level memory allocator */ -#if defined(UNQLITE_ENABLE_THREADS) - const SyMutexMethods *pMutexMethods; /* Mutex methods */ - SyMutex *pMutex; /* Global mutex */ - sxu32 nThreadingLevel; /* Threading level: 0 == Single threaded/1 == Multi-Threaded - * The threading level can be set using the [unqlite_lib_config()] - * interface with a configuration verb set to - * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE or - * UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI - */ -#endif - SySet kv_storage; /* Installed KV storage engines */ - int iPageSize; /* Default Page size */ - unqlite_vfs *pVfs; /* Underlying virtual file system (Vfs) */ - sxi32 nDB; /* Total number of active DB handles */ - unqlite *pDB; /* List of active DB handles */ - sxu32 nMagic; /* Sanity check against library misuse */ -}sUnqlMPGlobal = { - {0, 0, 0, 0, 0, 0, 0, 0, {0}}, -#if defined(UNQLITE_ENABLE_THREADS) - 0, - 0, - 0, -#endif - {0, 0, 0, 0, 0, 0, 0 }, - UNQLITE_DEFAULT_PAGE_SIZE, - 0, - 0, - 0, - 0 -}; -#define UNQLITE_LIB_MAGIC 0xEA1495BA -#define UNQLITE_LIB_MISUSE (sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC) -/* - * Supported threading level. - * These options have meaning only when the library is compiled with multi-threading - * support. That is, the UNQLITE_ENABLE_THREADS compile time directive must be defined - * when UnQLite is built. - * UNQLITE_THREAD_LEVEL_SINGLE: - * In this mode, mutexing is disabled and the library can only be used by a single thread. - * UNQLITE_THREAD_LEVEL_MULTI - * In this mode, all mutexes including the recursive mutexes on [unqlite] objects - * are enabled so that the application is free to share the same database handle - * between different threads at the same time. - */ -#define UNQLITE_THREAD_LEVEL_SINGLE 1 -#define UNQLITE_THREAD_LEVEL_MULTI 2 -/* - * Find a Key Value storage engine from the set of installed engines. - * Return a pointer to the storage engine methods on success. NULL on failure. - */ -UNQLITE_PRIVATE unqlite_kv_methods * unqliteFindKVStore( - const char *zName, /* Storage engine name [i.e. Hash, B+tree, LSM, etc.] */ - sxu32 nByte /* zName length */ - ) -{ - unqlite_kv_methods **apStore,*pEntry; - sxu32 n,nMax; - /* Point to the set of installed engines */ - apStore = (unqlite_kv_methods **)SySetBasePtr(&sUnqlMPGlobal.kv_storage); - nMax = SySetUsed(&sUnqlMPGlobal.kv_storage); - for( n = 0 ; n < nMax; ++n ){ - pEntry = apStore[n]; - if( nByte == SyStrlen(pEntry->zName) && SyStrnicmp(pEntry->zName,zName,nByte) == 0 ){ - /* Storage engine found */ - return pEntry; - } - } - /* No such entry, return NULL */ - return 0; -} -/* - * Configure the UnQLite library. - * Return UNQLITE_OK on success. Any other return value indicates failure. - * Refer to [unqlite_lib_config()]. - */ -static sxi32 unqliteCoreConfigure(sxi32 nOp, va_list ap) -{ - int rc = UNQLITE_OK; - switch(nOp){ - case UNQLITE_LIB_CONFIG_PAGE_SIZE: { - /* Default page size: Must be a power of two */ - int iPage = va_arg(ap,int); - if( iPage >= UNQLITE_MIN_PAGE_SIZE && iPage <= UNQLITE_MAX_PAGE_SIZE ){ - if( !(iPage & (iPage - 1)) ){ - sUnqlMPGlobal.iPageSize = iPage; - }else{ - /* Invalid page size */ - rc = UNQLITE_INVALID; - } - }else{ - /* Invalid page size */ - rc = UNQLITE_INVALID; - } - break; - } - case UNQLITE_LIB_CONFIG_STORAGE_ENGINE: { - /* Install a key value storage engine */ - unqlite_kv_methods *pMethods = va_arg(ap,unqlite_kv_methods *); - /* Make sure we are delaing with a valid methods */ - if( pMethods == 0 || SX_EMPTY_STR(pMethods->zName) || pMethods->xSeek == 0 || pMethods->xData == 0 - || pMethods->xKey == 0 || pMethods->xDataLength == 0 || pMethods->xKeyLength == 0 - || pMethods->szKv < (int)sizeof(unqlite_kv_engine) ){ - rc = UNQLITE_INVALID; - break; - } - /* Install it */ - rc = SySetPut(&sUnqlMPGlobal.kv_storage,(const void *)&pMethods); - break; - } - case UNQLITE_LIB_CONFIG_VFS:{ - /* Install a virtual file system */ - unqlite_vfs *pVfs = va_arg(ap,unqlite_vfs *); - if( pVfs ){ - sUnqlMPGlobal.pVfs = pVfs; - } - break; - } - case UNQLITE_LIB_CONFIG_USER_MALLOC: { - /* Use an alternative low-level memory allocation routines */ - const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *); - /* Save the memory failure callback (if available) */ - ProcMemError xMemErr = sUnqlMPGlobal.sAllocator.xMemError; - void *pMemErr = sUnqlMPGlobal.sAllocator.pUserData; - if( pMethods == 0 ){ - /* Use the built-in memory allocation subsystem */ - rc = SyMemBackendInit(&sUnqlMPGlobal.sAllocator, xMemErr, pMemErr); - }else{ - rc = SyMemBackendInitFromOthers(&sUnqlMPGlobal.sAllocator, pMethods, xMemErr, pMemErr); - } - break; - } - case UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK: { - /* Memory failure callback */ - ProcMemError xMemErr = va_arg(ap, ProcMemError); - void *pUserData = va_arg(ap, void *); - sUnqlMPGlobal.sAllocator.xMemError = xMemErr; - sUnqlMPGlobal.sAllocator.pUserData = pUserData; - break; - } - case UNQLITE_LIB_CONFIG_USER_MUTEX: { -#if defined(UNQLITE_ENABLE_THREADS) - /* Use an alternative low-level mutex subsystem */ - const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *); -#if defined (UNTRUST) - if( pMethods == 0 ){ - rc = UNQLITE_CORRUPT; - } -#endif - /* Sanity check */ - if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){ - /* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */ - rc = UNQLITE_CORRUPT; - break; - } - if( sUnqlMPGlobal.pMutexMethods ){ - /* Overwrite the previous mutex subsystem */ - SyMutexRelease(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); - if( sUnqlMPGlobal.pMutexMethods->xGlobalRelease ){ - sUnqlMPGlobal.pMutexMethods->xGlobalRelease(); - } - sUnqlMPGlobal.pMutex = 0; - } - /* Initialize and install the new mutex subsystem */ - if( pMethods->xGlobalInit ){ - rc = pMethods->xGlobalInit(); - if ( rc != UNQLITE_OK ){ - break; - } - } - /* Create the global mutex */ - sUnqlMPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST); - if( sUnqlMPGlobal.pMutex == 0 ){ - /* - * If the supplied mutex subsystem is so sick that we are unable to - * create a single mutex, there is no much we can do here. - */ - if( pMethods->xGlobalRelease ){ - pMethods->xGlobalRelease(); - } - rc = UNQLITE_CORRUPT; - break; - } - sUnqlMPGlobal.pMutexMethods = pMethods; - if( sUnqlMPGlobal.nThreadingLevel == 0 ){ - /* Set a default threading level */ - sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI; - } -#endif - break; - } - case UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE: -#if defined(UNQLITE_ENABLE_THREADS) - /* Single thread mode (Only one thread is allowed to play with the library) */ - sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_SINGLE; - jx9_lib_config(JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE); -#endif - break; - case UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI: -#if defined(UNQLITE_ENABLE_THREADS) - /* Multi-threading mode (library is thread safe and database handles and virtual machines - * may be shared between multiple threads). - */ - sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI; - jx9_lib_config(JX9_LIB_CONFIG_THREAD_LEVEL_MULTI); -#endif - break; - default: - /* Unknown configuration option */ - rc = UNQLITE_CORRUPT; - break; - } - return rc; -} -/* - * [CAPIREF: unqlite_lib_config()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_lib_config(int nConfigOp,...) -{ - va_list ap; - int rc; - if( sUnqlMPGlobal.nMagic == UNQLITE_LIB_MAGIC ){ - /* Library is already initialized, this operation is forbidden */ - return UNQLITE_LOCKED; - } - va_start(ap,nConfigOp); - rc = unqliteCoreConfigure(nConfigOp,ap); - va_end(ap); - return rc; -} -/* - * Global library initialization - * Refer to [unqlite_lib_init()] - * This routine must be called to initialize the memory allocation subsystem, the mutex - * subsystem prior to doing any serious work with the library. The first thread to call - * this routine does the initialization process and set the magic number so no body later - * can re-initialize the library. If subsequent threads call this routine before the first - * thread have finished the initialization process, then the subsequent threads must block - * until the initialization process is done. - */ -static sxi32 unqliteCoreInitialize(void) -{ - const unqlite_kv_methods *pMethods; - const unqlite_vfs *pVfs; /* Built-in vfs */ -#if defined(UNQLITE_ENABLE_THREADS) - const SyMutexMethods *pMutexMethods = 0; - SyMutex *pMaster = 0; -#endif - int rc; - /* - * If the library is already initialized, then a call to this routine - * is a no-op. - */ - if( sUnqlMPGlobal.nMagic == UNQLITE_LIB_MAGIC ){ - return UNQLITE_OK; /* Already initialized */ - } - /* Point to the built-in vfs */ - pVfs = unqliteExportBuiltinVfs(); - /* Install it */ - unqlite_lib_config(UNQLITE_LIB_CONFIG_VFS, pVfs); -#if defined(UNQLITE_ENABLE_THREADS) - if( sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_SINGLE ){ - pMutexMethods = sUnqlMPGlobal.pMutexMethods; - if( pMutexMethods == 0 ){ - /* Use the built-in mutex subsystem */ - pMutexMethods = SyMutexExportMethods(); - if( pMutexMethods == 0 ){ - return UNQLITE_CORRUPT; /* Can't happen */ - } - /* Install the mutex subsystem */ - rc = unqlite_lib_config(UNQLITE_LIB_CONFIG_USER_MUTEX, pMutexMethods); - if( rc != UNQLITE_OK ){ - return rc; - } - } - /* Obtain a static mutex so we can initialize the library without calling malloc() */ - pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1); - if( pMaster == 0 ){ - return UNQLITE_CORRUPT; /* Can't happen */ - } - } - /* Lock the master mutex */ - rc = UNQLITE_OK; - SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */ - if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){ -#endif - if( sUnqlMPGlobal.sAllocator.pMethods == 0 ){ - /* Install a memory subsystem */ - rc = unqlite_lib_config(UNQLITE_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */ - if( rc != UNQLITE_OK ){ - /* If we are unable to initialize the memory backend, there is no much we can do here.*/ - goto End; - } - } -#if defined(UNQLITE_ENABLE_THREADS) - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){ - /* Protect the memory allocation subsystem */ - rc = SyMemBackendMakeThreadSafe(&sUnqlMPGlobal.sAllocator, sUnqlMPGlobal.pMutexMethods); - if( rc != UNQLITE_OK ){ - goto End; - } - } -#endif - SySetInit(&sUnqlMPGlobal.kv_storage,&sUnqlMPGlobal.sAllocator,sizeof(unqlite_kv_methods *)); - /* Install the built-in Key Value storage engines */ - pMethods = unqliteExportMemKvStorage(); /* In-memory storage */ - unqlite_lib_config(UNQLITE_LIB_CONFIG_STORAGE_ENGINE,pMethods); - /* Default disk key/value storage engine */ - pMethods = unqliteExportDiskKvStorage(); /* Disk storage */ - unqlite_lib_config(UNQLITE_LIB_CONFIG_STORAGE_ENGINE,pMethods); - /* Default page size */ - if( sUnqlMPGlobal.iPageSize < UNQLITE_MIN_PAGE_SIZE ){ - unqlite_lib_config(UNQLITE_LIB_CONFIG_PAGE_SIZE,UNQLITE_DEFAULT_PAGE_SIZE); - } - /* Our library is initialized, set the magic number */ - sUnqlMPGlobal.nMagic = UNQLITE_LIB_MAGIC; - rc = UNQLITE_OK; -#if defined(UNQLITE_ENABLE_THREADS) - } /* sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC */ -#endif -End: -#if defined(UNQLITE_ENABLE_THREADS) - /* Unlock the master mutex */ - SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */ -#endif - return rc; -} -/* Forward declaration */ -static int unqliteVmRelease(unqlite_vm *pVm); -/* - * Release a single instance of an unqlite database handle. - */ -static int unqliteDbRelease(unqlite *pDb) -{ - unqlite_db *pStore = &pDb->sDB; - unqlite_vm *pVm,*pNext; - int rc = UNQLITE_OK; - if( (pDb->iFlags & UNQLITE_FL_DISABLE_AUTO_COMMIT) == 0 ){ - /* Commit any outstanding transaction */ - rc = unqlitePagerCommit(pStore->pPager); - if( rc != UNQLITE_OK ){ - /* Rollback the transaction */ - rc = unqlitePagerRollback(pStore->pPager,FALSE); - } - }else{ - /* Rollback any outstanding transaction */ - rc = unqlitePagerRollback(pStore->pPager,FALSE); - } - /* Close the pager */ - unqlitePagerClose(pStore->pPager); - /* Release any active VM's */ - pVm = pDb->pVms; - for(;;){ - if( pDb->iVm < 1 ){ - break; - } - /* Point to the next entry */ - pNext = pVm->pNext; - unqliteVmRelease(pVm); - pVm = pNext; - pDb->iVm--; - } - /* Release the Jx9 handle */ - jx9_release(pStore->pJx9); - /* Set a dummy magic number */ - pDb->nMagic = 0x7250; - /* Release the whole memory subsystem */ - SyMemBackendRelease(&pDb->sMem); - /* Commit or rollback result */ - return rc; -} -/* - * Release all resources consumed by the library. - * Note: This call is not thread safe. Refer to [unqlite_lib_shutdown()]. - */ -static void unqliteCoreShutdown(void) -{ - unqlite *pDb, *pNext; - /* Release all active databases handles */ - pDb = sUnqlMPGlobal.pDB; - for(;;){ - if( sUnqlMPGlobal.nDB < 1 ){ - break; - } - pNext = pDb->pNext; - unqliteDbRelease(pDb); - pDb = pNext; - sUnqlMPGlobal.nDB--; - } - /* Release the storage methods container */ - SySetRelease(&sUnqlMPGlobal.kv_storage); -#if defined(UNQLITE_ENABLE_THREADS) - /* Release the mutex subsystem */ - if( sUnqlMPGlobal.pMutexMethods ){ - if( sUnqlMPGlobal.pMutex ){ - SyMutexRelease(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); - sUnqlMPGlobal.pMutex = 0; - } - if( sUnqlMPGlobal.pMutexMethods->xGlobalRelease ){ - sUnqlMPGlobal.pMutexMethods->xGlobalRelease(); - } - sUnqlMPGlobal.pMutexMethods = 0; - } - sUnqlMPGlobal.nThreadingLevel = 0; -#endif - if( sUnqlMPGlobal.sAllocator.pMethods ){ - /* Release the memory backend */ - SyMemBackendRelease(&sUnqlMPGlobal.sAllocator); - } - sUnqlMPGlobal.nMagic = 0x1764; - /* Finally, shutdown the Jx9 library */ - jx9_lib_shutdown(); -} -/* - * [CAPIREF: unqlite_lib_init()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_lib_init(void) -{ - int rc; - rc = unqliteCoreInitialize(); - return rc; -} -/* - * [CAPIREF: unqlite_lib_shutdown()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_lib_shutdown(void) -{ - if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){ - /* Already shut */ - return UNQLITE_OK; - } - unqliteCoreShutdown(); - return UNQLITE_OK; -} -/* - * [CAPIREF: unqlite_lib_is_threadsafe()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_lib_is_threadsafe(void) -{ - if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){ - return 0; - } -#if defined(UNQLITE_ENABLE_THREADS) - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){ - /* Muli-threading support is enabled */ - return 1; - }else{ - /* Single-threading */ - return 0; - } -#else - return 0; -#endif -} -/* - * - * [CAPIREF: unqlite_lib_version()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -const char * unqlite_lib_version(void) -{ - return UNQLITE_VERSION; -} -/* - * - * [CAPIREF: unqlite_lib_signature()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -const char * unqlite_lib_signature(void) -{ - return UNQLITE_SIG; -} -/* - * - * [CAPIREF: unqlite_lib_ident()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -const char * unqlite_lib_ident(void) -{ - return UNQLITE_IDENT; -} -/* - * - * [CAPIREF: unqlite_lib_copyright()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -const char * unqlite_lib_copyright(void) -{ - return UNQLITE_COPYRIGHT; -} -/* - * Remove harmfull and/or stale flags passed to the [unqlite_open()] interface. - */ -static unsigned int unqliteSanityzeFlag(unsigned int iFlags) -{ - iFlags &= ~UNQLITE_OPEN_EXCLUSIVE; /* Reserved flag */ - if( iFlags & UNQLITE_OPEN_TEMP_DB ){ - /* Omit journaling for temporary database */ - iFlags |= UNQLITE_OPEN_OMIT_JOURNALING|UNQLITE_OPEN_CREATE; - } - if( (iFlags & (UNQLITE_OPEN_READONLY|UNQLITE_OPEN_READWRITE)) == 0 ){ - /* Auto-append the R+W flag */ - iFlags |= UNQLITE_OPEN_READWRITE; - } - if( iFlags & UNQLITE_OPEN_CREATE ){ - iFlags &= ~(UNQLITE_OPEN_MMAP|UNQLITE_OPEN_READONLY); - /* Auto-append the R+W flag */ - iFlags |= UNQLITE_OPEN_READWRITE; - }else{ - if( iFlags & UNQLITE_OPEN_READONLY ){ - iFlags &= ~UNQLITE_OPEN_READWRITE; - }else if( iFlags & UNQLITE_OPEN_READWRITE ){ - iFlags &= ~UNQLITE_OPEN_MMAP; - } - } - return iFlags; -} -/* - * This routine does the work of initializing a database handle on behalf - * of [unqlite_open()]. - */ -static int unqliteInitDatabase( - unqlite *pDB, /* Database handle */ - SyMemBackend *pParent, /* Master memory backend */ - const char *zFilename, /* Target database */ - unsigned int iFlags /* Open flags */ - ) -{ - unqlite_db *pStorage = &pDB->sDB; - int rc; - /* Initialiaze the memory subsystem */ - SyMemBackendInitFromParent(&pDB->sMem,pParent); -#if defined(UNQLITE_ENABLE_THREADS) - /* No need for internal mutexes */ - SyMemBackendDisbaleMutexing(&pDB->sMem); -#endif - SyBlobInit(&pDB->sErr,&pDB->sMem); - /* Sanityze flags */ - iFlags = unqliteSanityzeFlag(iFlags); - /* Init the pager and the transaction manager */ - rc = unqlitePagerOpen(sUnqlMPGlobal.pVfs,pDB,zFilename,iFlags); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Allocate a new Jx9 engine handle */ - rc = jx9_init(&pStorage->pJx9); - if( rc != JX9_OK ){ - return rc; - } - return UNQLITE_OK; -} -/* - * Allocate and initialize a new UnQLite Virtual Mahcine and attach it - * to the compiled Jx9 script. - */ -static int unqliteInitVm(unqlite *pDb,jx9_vm *pJx9Vm,unqlite_vm **ppOut) -{ - unqlite_vm *pVm; - - *ppOut = 0; - /* Allocate a new VM instance */ - pVm = (unqlite_vm *)SyMemBackendPoolAlloc(&pDb->sMem,sizeof(unqlite_vm)); - if( pVm == 0 ){ - return UNQLITE_NOMEM; - } - /* Zero the structure */ - SyZero(pVm,sizeof(unqlite_vm)); - /* Initialize */ - SyMemBackendInitFromParent(&pVm->sAlloc,&pDb->sMem); - /* Allocate a new collection table */ - pVm->apCol = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc,32 * sizeof(unqlite_col *)); - if( pVm->apCol == 0 ){ - goto fail; - } - pVm->iColSize = 32; /* Must be a power of two */ - /* Zero the table */ - SyZero((void *)pVm->apCol,pVm->iColSize * sizeof(unqlite_col *)); -#if defined(UNQLITE_ENABLE_THREADS) - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){ - /* Associate a recursive mutex with this instance */ - pVm->pMutex = SyMutexNew(sUnqlMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE); - if( pVm->pMutex == 0 ){ - goto fail; - } - } -#endif - /* Link the VM to the list of active virtual machines */ - pVm->pJx9Vm = pJx9Vm; - pVm->pDb = pDb; - MACRO_LD_PUSH(pDb->pVms,pVm); - pDb->iVm++; - /* Register Jx9 functions */ - unqliteRegisterJx9Functions(pVm); - /* Set the magic number */ - pVm->nMagic = JX9_VM_INIT; /* Same magic number as Jx9 */ - /* All done */ - *ppOut = pVm; - return UNQLITE_OK; -fail: - SyMemBackendRelease(&pVm->sAlloc); - SyMemBackendPoolFree(&pDb->sMem,pVm); - return UNQLITE_NOMEM; -} -/* - * Release an active VM. - */ -static int unqliteVmRelease(unqlite_vm *pVm) -{ - /* Release the Jx9 VM */ - jx9_vm_release(pVm->pJx9Vm); - /* Release the private memory backend */ - SyMemBackendRelease(&pVm->sAlloc); - /* Upper layer will discard this VM from the list - * of active VM. - */ - return UNQLITE_OK; -} -/* - * Return the default page size. - */ -UNQLITE_PRIVATE int unqliteGetPageSize(void) -{ - int iSize = sUnqlMPGlobal.iPageSize; - if( iSize < UNQLITE_MIN_PAGE_SIZE || iSize > UNQLITE_MAX_PAGE_SIZE ){ - iSize = UNQLITE_DEFAULT_PAGE_SIZE; - } - return iSize; -} -/* - * Generate an error message. - */ -UNQLITE_PRIVATE int unqliteGenError(unqlite *pDb,const char *zErr) -{ - int rc; - /* Append the error message */ - rc = SyBlobAppend(&pDb->sErr,(const void *)zErr,SyStrlen(zErr)); - /* Append a new line */ - SyBlobAppend(&pDb->sErr,(const void *)"\n",sizeof(char)); - return rc; -} -/* - * Generate an error message (Printf like). - */ -UNQLITE_PRIVATE int unqliteGenErrorFormat(unqlite *pDb,const char *zFmt,...) -{ - va_list ap; - int rc; - va_start(ap,zFmt); - rc = SyBlobFormatAp(&pDb->sErr,zFmt,ap); - va_end(ap); - /* Append a new line */ - SyBlobAppend(&pDb->sErr,(const void *)"\n",sizeof(char)); - return rc; -} -/* - * Generate an error message (Out of memory). - */ -UNQLITE_PRIVATE int unqliteGenOutofMem(unqlite *pDb) -{ - int rc; - rc = unqliteGenError(pDb,"unQLite is running out of memory"); - return rc; -} -/* - * Configure a working UnQLite database handle. - */ -static int unqliteConfigure(unqlite *pDb,int nOp,va_list ap) -{ - int rc = UNQLITE_OK; - switch(nOp){ - case UNQLITE_CONFIG_JX9_ERR_LOG: - /* Jx9 compile-time error log */ - rc = jx9EngineConfig(pDb->sDB.pJx9,JX9_CONFIG_ERR_LOG,ap); - break; - case UNQLITE_CONFIG_MAX_PAGE_CACHE: { - int max_page = va_arg(ap,int); - /* Maximum number of page to cache (Simple hint). */ - rc = unqlitePagerSetCachesize(pDb->sDB.pPager,max_page); - break; - } - case UNQLITE_CONFIG_ERR_LOG: { - /* Database error log if any */ - const char **pzPtr = va_arg(ap, const char **); - int *pLen = va_arg(ap, int *); - if( pzPtr == 0 ){ - rc = JX9_CORRUPT; - break; - } - /* NULL terminate the error-log buffer */ - SyBlobNullAppend(&pDb->sErr); - /* Point to the error-log buffer */ - *pzPtr = (const char *)SyBlobData(&pDb->sErr); - if( pLen ){ - if( SyBlobLength(&pDb->sErr) > 1 /* NULL '\0' terminator */ ){ - *pLen = (int)SyBlobLength(&pDb->sErr); - }else{ - *pLen = 0; - } - } - break; - } - case UNQLITE_CONFIG_DISABLE_AUTO_COMMIT:{ - /* Disable auto-commit */ - pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT; - break; - } - case UNQLITE_CONFIG_GET_KV_NAME: { - /* Name of the underlying KV storage engine */ - const char **pzPtr = va_arg(ap,const char **); - if( pzPtr ){ - unqlite_kv_engine *pEngine; - pEngine = unqlitePagerGetKvEngine(pDb); - /* Point to the name */ - *pzPtr = pEngine->pIo->pMethods->zName; - } - } - default: - /* Unknown configuration option */ - rc = UNQLITE_UNKNOWN; - break; - } - return rc; -} -/* - * Export the global (master) memory allocator to submodules. - */ -UNQLITE_PRIVATE const SyMemBackend * unqliteExportMemBackend(void) -{ - return &sUnqlMPGlobal.sAllocator; -} -/* - * [CAPIREF: unqlite_open()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode) -{ - unqlite *pHandle; - int rc; -#if defined(UNTRUST) - if( ppDB == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - *ppDB = 0; - /* One-time automatic library initialization */ - rc = unqliteCoreInitialize(); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Allocate a new database handle */ - pHandle = (unqlite *)SyMemBackendPoolAlloc(&sUnqlMPGlobal.sAllocator, sizeof(unqlite)); - if( pHandle == 0 ){ - return UNQLITE_NOMEM; - } - /* Zero the structure */ - SyZero(pHandle,sizeof(unqlite)); - if( iMode < 1 ){ - /* Assume a read-only database */ - iMode = UNQLITE_OPEN_READONLY|UNQLITE_OPEN_MMAP; - } - /* Init the database */ - rc = unqliteInitDatabase(pHandle,&sUnqlMPGlobal.sAllocator,zFilename,iMode); - if( rc != UNQLITE_OK ){ - goto Release; - } -#if defined(UNQLITE_ENABLE_THREADS) - if( !(iMode & UNQLITE_OPEN_NOMUTEX) && (sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE) ){ - /* Associate a recursive mutex with this instance */ - pHandle->pMutex = SyMutexNew(sUnqlMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE); - if( pHandle->pMutex == 0 ){ - rc = UNQLITE_NOMEM; - goto Release; - } - } -#endif - /* Link to the list of active DB handles */ -#if defined(UNQLITE_ENABLE_THREADS) - /* Enter the global mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */ -#endif - MACRO_LD_PUSH(sUnqlMPGlobal.pDB,pHandle); - sUnqlMPGlobal.nDB++; -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave the global mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */ -#endif - /* Set the magic number to identify a valid DB handle */ - pHandle->nMagic = UNQLITE_DB_MAGIC; - /* Make the handle available to the caller */ - *ppDB = pHandle; - return UNQLITE_OK; -Release: - SyMemBackendRelease(&pHandle->sMem); - SyMemBackendPoolFree(&sUnqlMPGlobal.sAllocator,pHandle); - return rc; -} -/* - * [CAPIREF: unqlite_config()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_config(unqlite *pDb,int nConfigOp,...) -{ - va_list ap; - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - va_start(ap, nConfigOp); - rc = unqliteConfigure(&(*pDb),nConfigOp, ap); - va_end(ap); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_close()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_close(unqlite *pDb) -{ - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Release the database handle */ - rc = unqliteDbRelease(pDb); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - /* Release DB mutex */ - SyMutexRelease(sUnqlMPGlobal.pMutexMethods, pDb->pMutex) /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif -#if defined(UNQLITE_ENABLE_THREADS) - /* Enter the global mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */ -#endif - /* Unlink from the list of active database handles */ - MACRO_LD_REMOVE(sUnqlMPGlobal.pDB, pDb); - sUnqlMPGlobal.nDB--; -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave the global mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */ -#endif - /* Release the memory chunk allocated to this handle */ - SyMemBackendPoolFree(&sUnqlMPGlobal.sAllocator,pDb); - return rc; -} -/* - * [CAPIREF: unqlite_compile()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut) -{ - jx9_vm *pVm; - int rc; - if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; - } -#endif - /* Compile the Jx9 script first */ - rc = jx9_compile(pDb->sDB.pJx9,zJx9,nByte,&pVm); - if( rc == JX9_OK ){ - /* Allocate a new unqlite VM instance */ - rc = unqliteInitVm(pDb,pVm,ppOut); - if( rc != UNQLITE_OK ){ - /* Release the Jx9 VM */ - jx9_vm_release(pVm); - } - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_compile_file()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut) -{ - jx9_vm *pVm; - int rc; - if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; - } -#endif - /* Compile the Jx9 script first */ - rc = jx9_compile_file(pDb->sDB.pJx9,zPath,&pVm); - if( rc == JX9_OK ){ - /* Allocate a new unqlite VM instance */ - rc = unqliteInitVm(pDb,pVm,ppOut); - if( rc != UNQLITE_OK ){ - /* Release the Jx9 VM */ - jx9_vm_release(pVm); - } - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * Configure an unqlite virtual machine (Mostly Jx9 VM) instance. - */ -static int unqliteVmConfig(unqlite_vm *pVm,sxi32 iOp,va_list ap) -{ - int rc; - rc = jx9VmConfigure(pVm->pJx9Vm,iOp,ap); - return rc; -} -/* - * [CAPIREF: unqlite_vm_config()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_vm_config(unqlite_vm *pVm,int iOp,...) -{ - va_list ap; - int rc; - if( UNQLITE_VM_MISUSE(pVm) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - va_start(ap,iOp); - rc = unqliteVmConfig(pVm,iOp,ap); - va_end(ap); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_vm_exec()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_vm_exec(unqlite_vm *pVm) -{ - int rc; - if( UNQLITE_VM_MISUSE(pVm) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Execute the Jx9 bytecode program */ - rc = jx9VmByteCodeExec(pVm->pJx9Vm); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_vm_release()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_vm_release(unqlite_vm *pVm) -{ - int rc; - if( UNQLITE_VM_MISUSE(pVm) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Release the VM */ - rc = unqliteVmRelease(pVm); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave VM mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - /* Release VM mutex */ - SyMutexRelease(sUnqlMPGlobal.pMutexMethods,pVm->pMutex) /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - if( rc == UNQLITE_OK ){ - unqlite *pDb = pVm->pDb; - /* Unlink from the list of active VM's */ -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - MACRO_LD_REMOVE(pDb->pVms, pVm); - pDb->iVm--; - /* Release the memory chunk allocated to this instance */ - SyMemBackendPoolFree(&pDb->sMem,pVm); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - } - return rc; -} -/* - * [CAPIREF: unqlite_vm_reset()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_vm_reset(unqlite_vm *pVm) -{ - int rc; - if( UNQLITE_VM_MISUSE(pVm) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Reset the Jx9 VM */ - rc = jx9VmReset(pVm->pJx9Vm); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_vm_dump()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData) -{ - int rc; - if( UNQLITE_VM_MISUSE(pVm) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Dump the Jx9 VM */ - rc = jx9VmDump(pVm->pJx9Vm,xConsumer,pUserData); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_vm_extract_variable()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname) -{ - unqlite_value *pValue; - SyString sVariable; - if( UNQLITE_VM_MISUSE(pVm) ){ - return 0; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return 0; /* Another thread have released this instance */ - } -#endif - /* Extract the target variable */ - SyStringInitFromBuf(&sVariable,zVarname,SyStrlen(zVarname)); - pValue = jx9VmExtractVariable(pVm->pJx9Vm,&sVariable); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return pValue; -} -/* - * [CAPIREF: unqlite_create_function()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_create_function(unqlite_vm *pVm, const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData) -{ - SyString sName; - int rc; - if( UNQLITE_VM_MISUSE(pVm) ){ - return UNQLITE_CORRUPT; - } - SyStringInitFromBuf(&sName, zName, SyStrlen(zName)); - /* Remove leading and trailing white spaces */ - SyStringFullTrim(&sName); - /* Ticket 1433-003: NULL values are not allowed */ - if( sName.nByte < 1 || xFunc == 0 ){ - return UNQLITE_INVALID; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Install the foreign function */ - rc = jx9VmInstallForeignFunction(pVm->pJx9Vm,&sName,xFunc,pUserData); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_delete_function()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_delete_function(unqlite_vm *pVm, const char *zName) -{ - int rc; - if( UNQLITE_VM_MISUSE(pVm) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Unlink the foreign function */ - rc = jx9DeleteFunction(pVm->pJx9Vm,zName); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_create_constant()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData) -{ - SyString sName; - int rc; - if( UNQLITE_VM_MISUSE(pVm) ){ - return UNQLITE_CORRUPT; - } - SyStringInitFromBuf(&sName, zName, SyStrlen(zName)); - /* Remove leading and trailing white spaces */ - SyStringFullTrim(&sName); - if( sName.nByte < 1 ){ - /* Empty constant name */ - return UNQLITE_INVALID; - } - /* TICKET 1433-003: NULL pointer is harmless operation */ - if( xExpand == 0 ){ - return UNQLITE_INVALID; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Install the foreign constant */ - rc = jx9VmRegisterConstant(pVm->pJx9Vm,&sName,xExpand,pUserData); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_delete_constant()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_delete_constant(unqlite_vm *pVm, const char *zName) -{ - int rc; - if( UNQLITE_VM_MISUSE(pVm) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Unlink the foreign constant */ - rc = Jx9DeleteConstant(pVm->pJx9Vm,zName); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_value_int()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_int(unqlite_value *pVal, int iValue) -{ - return jx9_value_int(pVal,iValue); -} -/* - * [CAPIREF: unqlite_value_int64()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_int64(unqlite_value *pVal,unqlite_int64 iValue) -{ - return jx9_value_int64(pVal,iValue); -} -/* - * [CAPIREF: unqlite_value_bool()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_bool(unqlite_value *pVal, int iBool) -{ - return jx9_value_bool(pVal,iBool); -} -/* - * [CAPIREF: unqlite_value_null()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_null(unqlite_value *pVal) -{ - return jx9_value_null(pVal); -} -/* - * [CAPIREF: unqlite_value_double()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_double(unqlite_value *pVal, double Value) -{ - return jx9_value_double(pVal,Value); -} -/* - * [CAPIREF: unqlite_value_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen) -{ - return jx9_value_string(pVal,zString,nLen); -} -/* - * [CAPIREF: unqlite_value_string_format()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...) -{ - va_list ap; - int rc; - if((pVal->iFlags & MEMOBJ_STRING) == 0 ){ - /* Invalidate any prior representation */ - jx9MemObjRelease(pVal); - MemObjSetType(pVal, MEMOBJ_STRING); - } - va_start(ap, zFormat); - rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap); - va_end(ap); - return UNQLITE_OK; -} -/* - * [CAPIREF: unqlite_value_reset_string_cursor()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_reset_string_cursor(unqlite_value *pVal) -{ - return jx9_value_reset_string_cursor(pVal); -} -/* - * [CAPIREF: unqlite_value_resource()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_resource(unqlite_value *pVal,void *pUserData) -{ - return jx9_value_resource(pVal,pUserData); -} -/* - * [CAPIREF: unqlite_value_release()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_release(unqlite_value *pVal) -{ - return jx9_value_release(pVal); -} -/* - * [CAPIREF: unqlite_value_to_int()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_to_int(unqlite_value *pValue) -{ - return jx9_value_to_int(pValue); -} -/* - * [CAPIREF: unqlite_value_to_bool()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_to_bool(unqlite_value *pValue) -{ - return jx9_value_to_bool(pValue); -} -/* - * [CAPIREF: unqlite_value_to_int64()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue) -{ - return jx9_value_to_int64(pValue); -} -/* - * [CAPIREF: unqlite_value_to_double()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -double unqlite_value_to_double(unqlite_value *pValue) -{ - return jx9_value_to_double(pValue); -} -/* - * [CAPIREF: unqlite_value_to_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen) -{ - return jx9_value_to_string(pValue,pLen); -} -/* - * [CAPIREF: unqlite_value_to_resource()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -void * unqlite_value_to_resource(unqlite_value *pValue) -{ - return jx9_value_to_resource(pValue); -} -/* - * [CAPIREF: unqlite_value_compare()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict) -{ - return jx9_value_compare(pLeft,pRight,bStrict); -} -/* - * [CAPIREF: unqlite_result_int()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_result_int(unqlite_context *pCtx, int iValue) -{ - return jx9_result_int(pCtx,iValue); -} -/* - * [CAPIREF: unqlite_result_int64()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue) -{ - return jx9_result_int64(pCtx,iValue); -} -/* - * [CAPIREF: unqlite_result_bool()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_result_bool(unqlite_context *pCtx, int iBool) -{ - return jx9_result_bool(pCtx,iBool); -} -/* - * [CAPIREF: unqlite_result_double()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_result_double(unqlite_context *pCtx, double Value) -{ - return jx9_result_double(pCtx,Value); -} -/* - * [CAPIREF: unqlite_result_null()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_result_null(unqlite_context *pCtx) -{ - return jx9_result_null(pCtx); -} -/* - * [CAPIREF: unqlite_result_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen) -{ - return jx9_result_string(pCtx,zString,nLen); -} -/* - * [CAPIREF: unqlite_result_string_format()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...) -{ - jx9_value *p; - va_list ap; - int rc; - p = pCtx->pRet; - if( (p->iFlags & MEMOBJ_STRING) == 0 ){ - /* Invalidate any prior representation */ - jx9MemObjRelease(p); - MemObjSetType(p, MEMOBJ_STRING); - } - /* Format the given string */ - va_start(ap, zFormat); - rc = SyBlobFormatAp(&p->sBlob, zFormat, ap); - va_end(ap); - return rc; -} -/* - * [CAPIREF: unqlite_result_value()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue) -{ - return jx9_result_value(pCtx,pValue); -} -/* - * [CAPIREF: unqlite_result_resource()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_result_resource(unqlite_context *pCtx, void *pUserData) -{ - return jx9_result_resource(pCtx,pUserData); -} -/* - * [CAPIREF: unqlite_value_is_int()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_int(unqlite_value *pVal) -{ - return jx9_value_is_int(pVal); -} -/* - * [CAPIREF: unqlite_value_is_float()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_float(unqlite_value *pVal) -{ - return jx9_value_is_float(pVal); -} -/* - * [CAPIREF: unqlite_value_is_bool()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_bool(unqlite_value *pVal) -{ - return jx9_value_is_bool(pVal); -} -/* - * [CAPIREF: unqlite_value_is_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_string(unqlite_value *pVal) -{ - return jx9_value_is_string(pVal); -} -/* - * [CAPIREF: unqlite_value_is_null()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_null(unqlite_value *pVal) -{ - return jx9_value_is_null(pVal); -} -/* - * [CAPIREF: unqlite_value_is_numeric()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_numeric(unqlite_value *pVal) -{ - return jx9_value_is_numeric(pVal); -} -/* - * [CAPIREF: unqlite_value_is_callable()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_callable(unqlite_value *pVal) -{ - return jx9_value_is_callable(pVal); -} -/* - * [CAPIREF: unqlite_value_is_scalar()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_scalar(unqlite_value *pVal) -{ - return jx9_value_is_scalar(pVal); -} -/* - * [CAPIREF: unqlite_value_is_json_array()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_json_array(unqlite_value *pVal) -{ - return jx9_value_is_json_array(pVal); -} -/* - * [CAPIREF: unqlite_value_is_json_object()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_json_object(unqlite_value *pVal) -{ - return jx9_value_is_json_object(pVal); -} -/* - * [CAPIREF: unqlite_value_is_resource()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_resource(unqlite_value *pVal) -{ - return jx9_value_is_resource(pVal); -} -/* - * [CAPIREF: unqlite_value_is_empty()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_value_is_empty(unqlite_value *pVal) -{ - return jx9_value_is_empty(pVal); -} -/* - * [CAPIREF: unqlite_array_fetch()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte) -{ - return jx9_array_fetch(pArray,zKey,nByte); -} -/* - * [CAPIREF: unqlite_array_walk()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData) -{ - return jx9_array_walk(pArray,xWalk,pUserData); -} -/* - * [CAPIREF: unqlite_array_add_elem()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue) -{ - return jx9_array_add_elem(pArray,pKey,pValue); -} -/* - * [CAPIREF: unqlite_array_add_strkey_elem()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue) -{ - return jx9_array_add_strkey_elem(pArray,zKey,pValue); -} -/* - * [CAPIREF: unqlite_array_count()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_array_count(unqlite_value *pArray) -{ - return (int)jx9_array_count(pArray); -} -/* - * [CAPIREF: unqlite_vm_new_scalar()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm) -{ - unqlite_value *pValue; - if( UNQLITE_VM_MISUSE(pVm) ){ - return 0; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return 0; /* Another thread have released this instance */ - } -#endif - pValue = jx9_new_scalar(pVm->pJx9Vm); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return pValue; -} -/* - * [CAPIREF: unqlite_vm_new_array()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm) -{ - unqlite_value *pValue; - if( UNQLITE_VM_MISUSE(pVm) ){ - return 0; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return 0; /* Another thread have released this instance */ - } -#endif - pValue = jx9_new_array(pVm->pJx9Vm); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return pValue; -} -/* - * [CAPIREF: unqlite_vm_release_value()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue) -{ - int rc; - if( UNQLITE_VM_MISUSE(pVm) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_VM_RELEASE(pVm) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - rc = jx9_release_value(pVm->pJx9Vm,pValue); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_context_output()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen) -{ - return jx9_context_output(pCtx,zString,nLen); -} -/* - * [CAPIREF: unqlite_context_output_format()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...) -{ - va_list ap; - int rc; - va_start(ap, zFormat); - rc = jx9VmOutputConsumeAp(pCtx->pVm,zFormat, ap); - va_end(ap); - return rc; -} -/* - * [CAPIREF: unqlite_context_throw_error()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr) -{ - return jx9_context_throw_error(pCtx,iErr,zErr); -} -/* - * [CAPIREF: unqlite_context_throw_error_format()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...) -{ - va_list ap; - int rc; - if( zFormat == 0){ - return JX9_OK; - } - va_start(ap, zFormat); - rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap); - va_end(ap); - return rc; -} -/* - * [CAPIREF: unqlite_context_random_num()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -unsigned int unqlite_context_random_num(unqlite_context *pCtx) -{ - return jx9_context_random_num(pCtx); -} -/* - * [CAPIREF: unqlite_context_random_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen) -{ - return jx9_context_random_string(pCtx,zBuf,nBuflen); -} -/* - * [CAPIREF: unqlite_context_user_data()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -void * unqlite_context_user_data(unqlite_context *pCtx) -{ - return jx9_context_user_data(pCtx); -} -/* - * [CAPIREF: unqlite_context_push_aux_data()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData) -{ - return jx9_context_push_aux_data(pCtx,pUserData); -} -/* - * [CAPIREF: unqlite_context_peek_aux_data()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -void * unqlite_context_peek_aux_data(unqlite_context *pCtx) -{ - return jx9_context_peek_aux_data(pCtx); -} -/* - * [CAPIREF: unqlite_context_pop_aux_data()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -void * unqlite_context_pop_aux_data(unqlite_context *pCtx) -{ - return jx9_context_pop_aux_data(pCtx); -} -/* - * [CAPIREF: unqlite_context_result_buf_length()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx) -{ - return jx9_context_result_buf_length(pCtx); -} -/* - * [CAPIREF: unqlite_function_name()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -const char * unqlite_function_name(unqlite_context *pCtx) -{ - return jx9_function_name(pCtx); -} -/* - * [CAPIREF: unqlite_context_new_scalar()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx) -{ - return jx9_context_new_scalar(pCtx); -} -/* - * [CAPIREF: unqlite_context_new_array()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -unqlite_value * unqlite_context_new_array(unqlite_context *pCtx) -{ - return jx9_context_new_array(pCtx); -} -/* - * [CAPIREF: unqlite_context_release_value()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue) -{ - jx9_context_release_value(pCtx,pValue); -} -/* - * [CAPIREF: unqlite_context_alloc_chunk()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease) -{ - return jx9_context_alloc_chunk(pCtx,nByte,ZeroChunk,AutoRelease); -} -/* - * [CAPIREF: unqlite_context_realloc_chunk()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte) -{ - return jx9_context_realloc_chunk(pCtx,pChunk,nByte); -} -/* - * [CAPIREF: unqlite_context_free_chunk()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk) -{ - jx9_context_free_chunk(pCtx,pChunk); -} -/* - * [CAPIREF: unqlite_kv_store()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen) -{ - unqlite_kv_engine *pEngine; - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Point to the underlying storage engine */ - pEngine = unqlitePagerGetKvEngine(pDb); - if( pEngine->pIo->pMethods->xReplace == 0 ){ - /* Storage engine does not implement such method */ - unqliteGenError(pDb,"xReplace() method not implemented in the underlying storage engine"); - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - if( nKeyLen < 0 ){ - /* Assume a null terminated string and compute it's length */ - nKeyLen = SyStrlen((const char *)pKey); - } - if( !nKeyLen ){ - unqliteGenError(pDb,"Empty key"); - rc = UNQLITE_EMPTY; - }else{ - /* Perform the requested operation */ - rc = pEngine->pIo->pMethods->xReplace(pEngine,pKey,nKeyLen,pData,nDataLen); - } - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_kv_store_fmt()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...) -{ - unqlite_kv_engine *pEngine; - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Point to the underlying storage engine */ - pEngine = unqlitePagerGetKvEngine(pDb); - if( pEngine->pIo->pMethods->xReplace == 0 ){ - /* Storage engine does not implement such method */ - unqliteGenError(pDb,"xReplace() method not implemented in the underlying storage engine"); - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - if( nKeyLen < 0 ){ - /* Assume a null terminated string and compute it's length */ - nKeyLen = SyStrlen((const char *)pKey); - } - if( !nKeyLen ){ - unqliteGenError(pDb,"Empty key"); - rc = UNQLITE_EMPTY; - }else{ - SyBlob sWorker; /* Working buffer */ - va_list ap; - SyBlobInit(&sWorker,&pDb->sMem); - /* Format the data */ - va_start(ap,zFormat); - SyBlobFormatAp(&sWorker,zFormat,ap); - va_end(ap); - /* Perform the requested operation */ - rc = pEngine->pIo->pMethods->xReplace(pEngine,pKey,nKeyLen,SyBlobData(&sWorker),SyBlobLength(&sWorker)); - /* Clean up */ - SyBlobRelease(&sWorker); - } - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_kv_append()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen) -{ - unqlite_kv_engine *pEngine; - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Point to the underlying storage engine */ - pEngine = unqlitePagerGetKvEngine(pDb); - if( pEngine->pIo->pMethods->xAppend == 0 ){ - /* Storage engine does not implement such method */ - unqliteGenError(pDb,"xAppend() method not implemented in the underlying storage engine"); - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - if( nKeyLen < 0 ){ - /* Assume a null terminated string and compute it's length */ - nKeyLen = SyStrlen((const char *)pKey); - } - if( !nKeyLen ){ - unqliteGenError(pDb,"Empty key"); - rc = UNQLITE_EMPTY; - }else{ - /* Perform the requested operation */ - rc = pEngine->pIo->pMethods->xAppend(pEngine,pKey,nKeyLen,pData,nDataLen); - } - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_kv_append_fmt()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...) -{ - unqlite_kv_engine *pEngine; - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Point to the underlying storage engine */ - pEngine = unqlitePagerGetKvEngine(pDb); - if( pEngine->pIo->pMethods->xAppend == 0 ){ - /* Storage engine does not implement such method */ - unqliteGenError(pDb,"xAppend() method not implemented in the underlying storage engine"); - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - if( nKeyLen < 0 ){ - /* Assume a null terminated string and compute it's length */ - nKeyLen = SyStrlen((const char *)pKey); - } - if( !nKeyLen ){ - unqliteGenError(pDb,"Empty key"); - rc = UNQLITE_EMPTY; - }else{ - SyBlob sWorker; /* Working buffer */ - va_list ap; - SyBlobInit(&sWorker,&pDb->sMem); - /* Format the data */ - va_start(ap,zFormat); - SyBlobFormatAp(&sWorker,zFormat,ap); - va_end(ap); - /* Perform the requested operation */ - rc = pEngine->pIo->pMethods->xAppend(pEngine,pKey,nKeyLen,SyBlobData(&sWorker),SyBlobLength(&sWorker)); - /* Clean up */ - SyBlobRelease(&sWorker); - } - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_kv_fetch()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 *pBufLen) -{ - unqlite_kv_methods *pMethods; - unqlite_kv_engine *pEngine; - unqlite_kv_cursor *pCur; - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Point to the underlying storage engine */ - pEngine = unqlitePagerGetKvEngine(pDb); - pMethods = pEngine->pIo->pMethods; - pCur = pDb->sDB.pCursor; - if( nKeyLen < 0 ){ - /* Assume a null terminated string and compute it's length */ - nKeyLen = SyStrlen((const char *)pKey); - } - if( !nKeyLen ){ - unqliteGenError(pDb,"Empty key"); - rc = UNQLITE_EMPTY; - }else{ - /* Seek to the record position */ - rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT); - } - if( rc == UNQLITE_OK ){ - if( pBuf == 0 ){ - /* Data length only */ - rc = pMethods->xDataLength(pCur,pBufLen); - }else{ - SyBlob sBlob; - /* Initialize the data consumer */ - SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)*pBufLen); - /* Consume the data */ - rc = pMethods->xData(pCur,unqliteDataConsumer,&sBlob); - /* Data length */ - *pBufLen = (unqlite_int64)SyBlobLength(&sBlob); - /* Cleanup */ - SyBlobRelease(&sBlob); - } - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_kv_fetch_callback()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData) -{ - unqlite_kv_methods *pMethods; - unqlite_kv_engine *pEngine; - unqlite_kv_cursor *pCur; - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Point to the underlying storage engine */ - pEngine = unqlitePagerGetKvEngine(pDb); - pMethods = pEngine->pIo->pMethods; - pCur = pDb->sDB.pCursor; - if( nKeyLen < 0 ){ - /* Assume a null terminated string and compute it's length */ - nKeyLen = SyStrlen((const char *)pKey); - } - if( !nKeyLen ){ - unqliteGenError(pDb,"Empty key"); - rc = UNQLITE_EMPTY; - }else{ - /* Seek to the record position */ - rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT); - } - if( rc == UNQLITE_OK && xConsumer ){ - /* Consume the data directly */ - rc = pMethods->xData(pCur,xConsumer,pUserData); - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_kv_delete()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen) -{ - unqlite_kv_methods *pMethods; - unqlite_kv_engine *pEngine; - unqlite_kv_cursor *pCur; - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Point to the underlying storage engine */ - pEngine = unqlitePagerGetKvEngine(pDb); - pMethods = pEngine->pIo->pMethods; - pCur = pDb->sDB.pCursor; - if( pMethods->xDelete == 0 ){ - /* Storage engine does not implement such method */ - unqliteGenError(pDb,"xDelete() method not implemented in the underlying storage engine"); - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - if( nKeyLen < 0 ){ - /* Assume a null terminated string and compute it's length */ - nKeyLen = SyStrlen((const char *)pKey); - } - if( !nKeyLen ){ - unqliteGenError(pDb,"Empty key"); - rc = UNQLITE_EMPTY; - }else{ - /* Seek to the record position */ - rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT); - } - if( rc == UNQLITE_OK ){ - /* Exact match found, delete the entry */ - rc = pMethods->xDelete(pCur); - } - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_kv_config()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_config(unqlite *pDb,int iOp,...) -{ - unqlite_kv_engine *pEngine; - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Point to the underlying storage engine */ - pEngine = unqlitePagerGetKvEngine(pDb); - if( pEngine->pIo->pMethods->xConfig == 0 ){ - /* Storage engine does not implements such method */ - unqliteGenError(pDb,"xConfig() method not implemented in the underlying storage engine"); - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - va_list ap; - /* Configure the storage engine */ - va_start(ap,iOp); - rc = pEngine->pIo->pMethods->xConfig(pEngine,iOp,ap); - va_end(ap); - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_init()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut) -{ - int rc; - if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0 /* Noop */){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Allocate a new cursor */ - rc = unqliteInitCursor(pDb,ppOut); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_release()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur) -{ - int rc; - if( UNQLITE_DB_MISUSE(pDb) || pCur == 0 /* Noop */){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Release the cursor */ - rc = unqliteReleaseCursor(pDb,pCur); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_first_entry()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor) -{ - int rc; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - /* Check if the requested method is implemented by the underlying storage engine */ - if( pCursor->pStore->pIo->pMethods->xFirst == 0 ){ - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - /* Seek to the first entry */ - rc = pCursor->pStore->pIo->pMethods->xFirst(pCursor); - } - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_last_entry()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor) -{ - int rc; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - /* Check if the requested method is implemented by the underlying storage engine */ - if( pCursor->pStore->pIo->pMethods->xLast == 0 ){ - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - /* Seek to the last entry */ - rc = pCursor->pStore->pIo->pMethods->xLast(pCursor); - } - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_valid_entry()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor) -{ - int rc; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - /* Check if the requested method is implemented by the underlying storage engine */ - if( pCursor->pStore->pIo->pMethods->xValid == 0 ){ - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - rc = pCursor->pStore->pIo->pMethods->xValid(pCursor); - } - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_next_entry()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor) -{ - int rc; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - /* Check if the requested method is implemented by the underlying storage engine */ - if( pCursor->pStore->pIo->pMethods->xNext == 0 ){ - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - /* Seek to the next entry */ - rc = pCursor->pStore->pIo->pMethods->xNext(pCursor); - } - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_prev_entry()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor) -{ - int rc; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - /* Check if the requested method is implemented by the underlying storage engine */ - if( pCursor->pStore->pIo->pMethods->xPrev == 0 ){ - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - /* Seek to the previous entry */ - rc = pCursor->pStore->pIo->pMethods->xPrev(pCursor); - } - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_delete_entry()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor) -{ - int rc; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - /* Check if the requested method is implemented by the underlying storage engine */ - if( pCursor->pStore->pIo->pMethods->xDelete == 0 ){ - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - /* Delete the entry */ - rc = pCursor->pStore->pIo->pMethods->xDelete(pCursor); - } - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_reset()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor) -{ - int rc = UNQLITE_OK; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - /* Check if the requested method is implemented by the underlying storage engine */ - if( pCursor->pStore->pIo->pMethods->xReset == 0 ){ - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - /* Reset */ - pCursor->pStore->pIo->pMethods->xReset(pCursor); - } - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_seek()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos) -{ - int rc = UNQLITE_OK; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - if( nKeyLen < 0 ){ - /* Assume a null terminated string and compute it's length */ - nKeyLen = SyStrlen((const char *)pKey); - } - if( !nKeyLen ){ - rc = UNQLITE_EMPTY; - }else{ - /* Seek to the desired location */ - rc = pCursor->pStore->pIo->pMethods->xSeek(pCursor,pKey,nKeyLen,iPos); - } - return rc; -} -/* - * Default data consumer callback. That is, all retrieved is redirected to this - * routine which store the output in an internal blob. - */ -UNQLITE_PRIVATE int unqliteDataConsumer( - const void *pOut, /* Data to consume */ - unsigned int nLen, /* Data length */ - void *pUserData /* User private data */ - ) -{ - sxi32 rc; - /* Store the output in an internal BLOB */ - rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen); - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_data_callback()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData) -{ - int rc; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - /* Consume the key directly */ - rc = pCursor->pStore->pIo->pMethods->xKey(pCursor,xConsumer,pUserData); - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_key()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte) -{ - int rc; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - if( pBuf == 0 ){ - /* Key length only */ - rc = pCursor->pStore->pIo->pMethods->xKeyLength(pCursor,pnByte); - }else{ - SyBlob sBlob; - if( (*pnByte) < 0 ){ - return UNQLITE_CORRUPT; - } - /* Initialize the data consumer */ - SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)(*pnByte)); - /* Consume the key */ - rc = pCursor->pStore->pIo->pMethods->xKey(pCursor,unqliteDataConsumer,&sBlob); - /* Key length */ - *pnByte = SyBlobLength(&sBlob); - /* Cleanup */ - SyBlobRelease(&sBlob); - } - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_data_callback()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData) -{ - int rc; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - /* Consume the data directly */ - rc = pCursor->pStore->pIo->pMethods->xData(pCursor,xConsumer,pUserData); - return rc; -} -/* - * [CAPIREF: unqlite_kv_cursor_data()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnByte) -{ - int rc; -#ifdef UNTRUST - if( pCursor == 0 ){ - return UNQLITE_CORRUPT; - } -#endif - if( pBuf == 0 ){ - /* Data length only */ - rc = pCursor->pStore->pIo->pMethods->xDataLength(pCursor,pnByte); - }else{ - SyBlob sBlob; - if( (*pnByte) < 0 ){ - return UNQLITE_CORRUPT; - } - /* Initialize the data consumer */ - SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)(*pnByte)); - /* Consume the data */ - rc = pCursor->pStore->pIo->pMethods->xData(pCursor,unqliteDataConsumer,&sBlob); - /* Data length */ - *pnByte = SyBlobLength(&sBlob); - /* Cleanup */ - SyBlobRelease(&sBlob); - } - return rc; -} -/* - * [CAPIREF: unqlite_begin()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_begin(unqlite *pDb) -{ - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Begin the write transaction */ - rc = unqlitePagerBegin(pDb->sDB.pPager); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_commit()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_commit(unqlite *pDb) -{ - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Commit the transaction */ - rc = unqlitePagerCommit(pDb->sDB.pPager); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_rollback()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int unqlite_rollback(unqlite *pDb) -{ - int rc; - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Rollback the transaction */ - rc = unqlitePagerRollback(pDb->sDB.pPager,TRUE); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: unqlite_util_load_mmaped_file()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize) -{ - const jx9_vfs *pVfs; - int rc; - if( SX_EMPTY_STR(zFile) || ppMap == 0 || pFileSize == 0){ - /* Sanity check */ - return UNQLITE_CORRUPT; - } - *ppMap = 0; - /* Extract the Jx9 Vfs */ - pVfs = jx9ExportBuiltinVfs(); - /* - * Check if the underlying vfs implement the memory map routines - * [i.e: mmap() under UNIX/MapViewOfFile() under windows]. - */ - if( pVfs == 0 || pVfs->xMmap == 0 ){ - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - /* Try to get a read-only memory view of the whole file */ - rc = pVfs->xMmap(zFile,ppMap,pFileSize); - } - return rc; -} -/* - * [CAPIREF: unqlite_util_release_mmaped_file()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize) -{ - const jx9_vfs *pVfs; - int rc = UNQLITE_OK; - if( pMap == 0 ){ - return UNQLITE_OK; - } - /* Extract the Jx9 Vfs */ - pVfs = jx9ExportBuiltinVfs(); - if( pVfs == 0 || pVfs->xUnmap == 0 ){ - rc = UNQLITE_NOTIMPLEMENTED; - }else{ - pVfs->xUnmap(pMap,iFileSize); - } - return rc; -} -/* - * [CAPIREF: unqlite_util_random_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size) -{ - if( UNQLITE_DB_MISUSE(pDb) ){ - return UNQLITE_CORRUPT; - } - if( zBuf == 0 || buf_size < 3 ){ - /* Buffer must be long enough to hold three bytes */ - return UNQLITE_INVALID; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return UNQLITE_ABORT; /* Another thread have released this instance */ - } -#endif - /* Generate the random string */ - unqlitePagerRandomString(pDb->sDB.pPager,zBuf,buf_size); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return UNQLITE_OK; -} -/* - * [CAPIREF: unqlite_util_random_num()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb) -{ - sxu32 iNum; - if( UNQLITE_DB_MISUSE(pDb) ){ - return 0; - } -#if defined(UNQLITE_ENABLE_THREADS) - /* Acquire DB mutex */ - SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && - UNQLITE_THRD_DB_RELEASE(pDb) ){ - return 0; /* Another thread have released this instance */ - } -#endif - /* Generate the random number */ - iNum = unqlitePagerRandomNum(pDb->sDB.pPager); -#if defined(UNQLITE_ENABLE_THREADS) - /* Leave DB mutex */ - SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ -#endif - return iNum; -} -/* - * ---------------------------------------------------------- - * File: bitvec.c - * MD5: 7e3376710d8454ebcf8c77baacca880f - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: bitvec.c v1.0 Win7 2013-02-27 15:16 stable $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif - -/** This file implements an object that represents a dynmaic -** bitmap. -** -** A bitmap is used to record which pages of a database file have been -** journalled during a transaction, or which pages have the "dont-write" -** property. Usually only a few pages are meet either condition. -** So the bitmap is usually sparse and has low cardinality. -*/ -/* - * Actually, this is not a bitmap but a simple hashtable where page - * number (64-bit unsigned integers) are used as the lookup keys. - */ -typedef struct bitvec_rec bitvec_rec; -struct bitvec_rec -{ - pgno iPage; /* Page number */ - bitvec_rec *pNext,*pNextCol; /* Collison link */ -}; -struct Bitvec -{ - SyMemBackend *pAlloc; /* Memory allocator */ - sxu32 nRec; /* Total number of records */ - sxu32 nSize; /* Table size */ - bitvec_rec **apRec; /* Record table */ - bitvec_rec *pList; /* List of records */ -}; -/* - * Allocate a new bitvec instance. -*/ -UNQLITE_PRIVATE Bitvec * unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize) -{ - bitvec_rec **apNew; - Bitvec *p; - - p = (Bitvec *)SyMemBackendAlloc(pAlloc,sizeof(*p) ); - if( p == 0 ){ - SXUNUSED(iSize); /* cc warning */ - return 0; - } - /* Zero the structure */ - SyZero(p,sizeof(Bitvec)); - /* Allocate a new table */ - p->nSize = 64; /* Must be a power of two */ - apNew = (bitvec_rec **)SyMemBackendAlloc(pAlloc,p->nSize * sizeof(bitvec_rec *)); - if( apNew == 0 ){ - SyMemBackendFree(pAlloc,p); - return 0; - } - /* Zero the new table */ - SyZero((void *)apNew,p->nSize * sizeof(bitvec_rec *)); - /* Fill-in */ - p->apRec = apNew; - p->pAlloc = pAlloc; - return p; -} -/* - * Check if the given page number is already installed in the table. - * Return true if installed. False otherwise. - */ -UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i) -{ - bitvec_rec *pRec; - /* Point to the desired bucket */ - pRec = p->apRec[i & (p->nSize - 1)]; - for(;;){ - if( pRec == 0 ){ break; } - if( pRec->iPage == i ){ - /* Page found */ - return 1; - } - /* Point to the next entry */ - pRec = pRec->pNextCol; - - if( pRec == 0 ){ break; } - if( pRec->iPage == i ){ - /* Page found */ - return 1; - } - /* Point to the next entry */ - pRec = pRec->pNextCol; - - - if( pRec == 0 ){ break; } - if( pRec->iPage == i ){ - /* Page found */ - return 1; - } - /* Point to the next entry */ - pRec = pRec->pNextCol; - - - if( pRec == 0 ){ break; } - if( pRec->iPage == i ){ - /* Page found */ - return 1; - } - /* Point to the next entry */ - pRec = pRec->pNextCol; - } - /* No such entry */ - return 0; -} -/* - * Install a given page number in our bitmap (Actually, our hashtable). - */ -UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i) -{ - bitvec_rec *pRec; - sxi32 iBuck; - /* Allocate a new instance */ - pRec = (bitvec_rec *)SyMemBackendPoolAlloc(p->pAlloc,sizeof(bitvec_rec)); - if( pRec == 0 ){ - return UNQLITE_NOMEM; - } - /* Zero the structure */ - SyZero(pRec,sizeof(bitvec_rec)); - /* Fill-in */ - pRec->iPage = i; - iBuck = i & (p->nSize - 1); - pRec->pNextCol = p->apRec[iBuck]; - p->apRec[iBuck] = pRec; - pRec->pNext = p->pList; - p->pList = pRec; - p->nRec++; - if( p->nRec >= (p->nSize * 3) && p->nRec < 100000 ){ - /* Grow the hashtable */ - sxu32 nNewSize = p->nSize << 1; - bitvec_rec *pEntry,**apNew; - sxu32 n; - apNew = (bitvec_rec **)SyMemBackendAlloc(p->pAlloc, nNewSize * sizeof(bitvec_rec *)); - if( apNew ){ - sxu32 iBucket; - /* Zero the new table */ - SyZero((void *)apNew, nNewSize * sizeof(bitvec_rec *)); - /* Rehash all entries */ - n = 0; - pEntry = p->pList; - for(;;){ - /* Loop one */ - if( n >= p->nRec ){ - break; - } - pEntry->pNextCol = 0; - /* Install in the new bucket */ - iBucket = pEntry->iPage & (nNewSize - 1); - pEntry->pNextCol = apNew[iBucket]; - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - n++; - } - /* Release the old table and reflect the change */ - SyMemBackendFree(p->pAlloc,(void *)p->apRec); - p->apRec = apNew; - p->nSize = nNewSize; - } - } - return UNQLITE_OK; -} -/* - * Destroy a bitvec instance. Reclaim all memory used. - */ -UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p) -{ - bitvec_rec *pNext,*pRec = p->pList; - SyMemBackend *pAlloc = p->pAlloc; - - for(;;){ - if( p->nRec < 1 ){ - break; - } - pNext = pRec->pNext; - SyMemBackendPoolFree(pAlloc,(void *)pRec); - pRec = pNext; - p->nRec--; - - if( p->nRec < 1 ){ - break; - } - pNext = pRec->pNext; - SyMemBackendPoolFree(pAlloc,(void *)pRec); - pRec = pNext; - p->nRec--; - - - if( p->nRec < 1 ){ - break; - } - pNext = pRec->pNext; - SyMemBackendPoolFree(pAlloc,(void *)pRec); - pRec = pNext; - p->nRec--; - - - if( p->nRec < 1 ){ - break; - } - pNext = pRec->pNext; - SyMemBackendPoolFree(pAlloc,(void *)pRec); - pRec = pNext; - p->nRec--; - } - SyMemBackendFree(pAlloc,(void *)p->apRec); - SyMemBackendFree(pAlloc,p); -} -/* - * ---------------------------------------------------------- - * File: fastjson.c - * MD5: 3693c0022edc7d37b65124d7aef68397 - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: fastjson.c v1.1 FreeBSD 2012-12-05 22:52 stable $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif -/* JSON binary encoding, decoding and stuff like that */ -#ifndef UNQLITE_FAST_JSON_NEST_LIMIT -#if defined(__WINNT__) || defined(__UNIXES__) -#define UNQLITE_FAST_JSON_NEST_LIMIT 64 /* Nesting limit */ -#else -#define UNQLITE_FAST_JSON_NEST_LIMIT 32 /* Nesting limit */ -#endif -#endif /* UNQLITE_FAST_JSON_NEST_LIMIT */ -/* - * JSON to Binary using the FastJSON implementation (BigEndian). - */ -/* - * FastJSON implemented binary token. - */ -#define FJSON_DOC_START 1 /* { */ -#define FJSON_DOC_END 2 /* } */ -#define FJSON_ARRAY_START 3 /* [ */ -#define FJSON_ARRAY_END 4 /* ] */ -#define FJSON_COLON 5 /* : */ -#define FJSON_COMMA 6 /* , */ -#define FJSON_ID 7 /* ID + 4 Bytes length */ -#define FJSON_STRING 8 /* String + 4 bytes length */ -#define FJSON_BYTE 9 /* Byte */ -#define FJSON_INT64 10 /* Integer 64 + 8 bytes */ -#define FJSON_REAL 18 /* Floating point value + 2 bytes */ -#define FJSON_NULL 23 /* NULL */ -#define FJSON_TRUE 24 /* TRUE */ -#define FJSON_FALSE 25 /* FALSE */ -/* - * Encode a Jx9 value to binary JSON. - */ -UNQLITE_PRIVATE sxi32 FastJsonEncode( - jx9_value *pValue, /* Value to encode */ - SyBlob *pOut, /* Store encoded value here */ - int iNest /* Nesting limit */ - ) -{ - sxi32 iType = pValue ? pValue->iFlags : MEMOBJ_NULL; - sxi32 rc = SXRET_OK; - int c; - if( iNest >= UNQLITE_FAST_JSON_NEST_LIMIT ){ - /* Nesting limit reached */ - return SXERR_LIMIT; - } - if( iType & (MEMOBJ_NULL|MEMOBJ_RES) ){ - /* - * Resources are encoded as null also. - */ - c = FJSON_NULL; - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - }else if( iType & MEMOBJ_BOOL ){ - c = pValue->x.iVal ? FJSON_TRUE : FJSON_FALSE; - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - }else if( iType & MEMOBJ_STRING ){ - unsigned char zBuf[sizeof(sxu32)]; /* String length */ - c = FJSON_STRING; - SyBigEndianPack32(zBuf,SyBlobLength(&pValue->sBlob)); - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - if( rc == SXRET_OK ){ - rc = SyBlobAppend(pOut,(const void *)zBuf,sizeof(zBuf)); - if( rc == SXRET_OK ){ - rc = SyBlobAppend(pOut,SyBlobData(&pValue->sBlob),SyBlobLength(&pValue->sBlob)); - } - } - }else if( iType & MEMOBJ_INT ){ - unsigned char zBuf[8]; - /* 64bit big endian integer */ - c = FJSON_INT64; - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - if( rc == SXRET_OK ){ - SyBigEndianPack64(zBuf,(sxu64)pValue->x.iVal); - rc = SyBlobAppend(pOut,(const void *)zBuf,sizeof(zBuf)); - } - }else if( iType & MEMOBJ_REAL ){ - /* Real number */ - c = FJSON_REAL; - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - if( rc == SXRET_OK ){ - sxu32 iOfft = SyBlobLength(pOut); - rc = SyBlobAppendBig16(pOut,0); - if( rc == SXRET_OK ){ - unsigned char *zBlob; - SyBlobFormat(pOut,"%.15g",pValue->x.rVal); - zBlob = (unsigned char *)SyBlobDataAt(pOut,iOfft); - SyBigEndianPack16(zBlob,(sxu16)(SyBlobLength(pOut) - ( 2 + iOfft))); - } - } - }else if( iType & MEMOBJ_HASHMAP ){ - /* A JSON object or array */ - jx9_hashmap *pMap = (jx9_hashmap *)pValue->x.pOther; - jx9_hashmap_node *pNode; - jx9_value *pEntry; - /* Reset the hashmap loop cursor */ - jx9HashmapResetLoopCursor(pMap); - if( pMap->iFlags & HASHMAP_JSON_OBJECT ){ - jx9_value sKey; - /* A JSON object */ - c = FJSON_DOC_START; /* { */ - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - if( rc == SXRET_OK ){ - jx9MemObjInit(pMap->pVm,&sKey); - /* Encode object entries */ - while((pNode = jx9HashmapGetNextEntry(pMap)) != 0 ){ - /* Extract the key */ - jx9HashmapExtractNodeKey(pNode,&sKey); - /* Encode it */ - rc = FastJsonEncode(&sKey,pOut,iNest+1); - if( rc != SXRET_OK ){ - break; - } - c = FJSON_COLON; - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - if( rc != SXRET_OK ){ - break; - } - /* Extract the value */ - pEntry = jx9HashmapGetNodeValue(pNode); - /* Encode it */ - rc = FastJsonEncode(pEntry,pOut,iNest+1); - if( rc != SXRET_OK ){ - break; - } - /* Delimit the entry */ - c = FJSON_COMMA; - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - if( rc != SXRET_OK ){ - break; - } - } - jx9MemObjRelease(&sKey); - if( rc == SXRET_OK ){ - c = FJSON_DOC_END; /* } */ - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - } - } - }else{ - /* A JSON array */ - c = FJSON_ARRAY_START; /* [ */ - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - if( rc == SXRET_OK ){ - /* Encode array entries */ - while( (pNode = jx9HashmapGetNextEntry(pMap)) != 0 ){ - /* Extract the value */ - pEntry = jx9HashmapGetNodeValue(pNode); - /* Encode it */ - rc = FastJsonEncode(pEntry,pOut,iNest+1); - if( rc != SXRET_OK ){ - break; - } - /* Delimit the entry */ - c = FJSON_COMMA; - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - if( rc != SXRET_OK ){ - break; - } - } - if( rc == SXRET_OK ){ - c = FJSON_ARRAY_END; /* ] */ - rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char)); - } - } - } - } - return rc; -} -/* - * Decode a FastJSON binary blob. - */ -UNQLITE_PRIVATE sxi32 FastJsonDecode( - const void *pIn, /* Binary JSON */ - sxu32 nByte, /* Chunk delimiter */ - jx9_value *pOut, /* Decoded value */ - const unsigned char **pzPtr, - int iNest /* Nesting limit */ - ) -{ - const unsigned char *zIn = (const unsigned char *)pIn; - const unsigned char *zEnd = &zIn[nByte]; - sxi32 rc = SXRET_OK; - int c; - if( iNest >= UNQLITE_FAST_JSON_NEST_LIMIT ){ - /* Nesting limit reached */ - return SXERR_LIMIT; - } - c = zIn[0]; - /* Advance the stream cursor */ - zIn++; - /* Process the binary token */ - switch(c){ - case FJSON_NULL: - /* null */ - jx9_value_null(pOut); - break; - case FJSON_FALSE: - /* Boolean FALSE */ - jx9_value_bool(pOut,0); - break; - case FJSON_TRUE: - /* Boolean TRUE */ - jx9_value_bool(pOut,1); - break; - case FJSON_INT64: { - /* 64Bit integer */ - sxu64 iVal; - /* Sanity check */ - if( &zIn[8] >= zEnd ){ - /* Corrupt chunk */ - rc = SXERR_CORRUPT; - break; - } - SyBigEndianUnpack64(zIn,&iVal); - /* Advance the pointer */ - zIn += 8; - jx9_value_int64(pOut,(jx9_int64)iVal); - break; - } - case FJSON_REAL: { - /* Real number */ - double iVal = 0; /* cc warning */ - sxu16 iLen; - /* Sanity check */ - if( &zIn[2] >= zEnd ){ - /* Corrupt chunk */ - rc = SXERR_CORRUPT; - break; - } - SyBigEndianUnpack16(zIn,&iLen); - if( &zIn[iLen] >= zEnd ){ - /* Corrupt chunk */ - rc = SXERR_CORRUPT; - break; - } - zIn += 2; - SyStrToReal((const char *)zIn,(sxu32)iLen,&iVal,0); - /* Advance the pointer */ - zIn += iLen; - jx9_value_double(pOut,iVal); - break; - } - case FJSON_STRING: { - /* UTF-8/Binary chunk */ - sxu32 iLength; - /* Sanity check */ - if( &zIn[4] >= zEnd ){ - /* Corrupt chunk */ - rc = SXERR_CORRUPT; - break; - } - SyBigEndianUnpack32(zIn,&iLength); - if( &zIn[iLength] >= zEnd ){ - /* Corrupt chunk */ - rc = SXERR_CORRUPT; - break; - } - zIn += 4; - /* Invalidate any prior representation */ - if( pOut->iFlags & MEMOBJ_STRING ){ - /* Reset the string cursor */ - SyBlobReset(&pOut->sBlob); - } - rc = jx9MemObjStringAppend(pOut,(const char *)zIn,iLength); - /* Update pointer */ - zIn += iLength; - break; - } - case FJSON_ARRAY_START: { - /* Binary JSON array */ - jx9_hashmap *pMap; - jx9_value sVal; - /* Allocate a new hashmap */ - pMap = (jx9_hashmap *)jx9NewHashmap(pOut->pVm,0,0); - if( pMap == 0 ){ - rc = SXERR_MEM; - break; - } - jx9MemObjInit(pOut->pVm,&sVal); - jx9MemObjRelease(pOut); - MemObjSetType(pOut,MEMOBJ_HASHMAP); - pOut->x.pOther = pMap; - rc = SXRET_OK; - for(;;){ - /* Jump leading binary commas */ - while (zIn < zEnd && zIn[0] == FJSON_COMMA ){ - zIn++; - } - if( zIn >= zEnd || zIn[0] == FJSON_ARRAY_END ){ - if( zIn < zEnd ){ - zIn++; /* Jump the trailing binary ] */ - } - break; - } - /* Decode the value */ - rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sVal,&zIn,iNest+1); - if( rc != SXRET_OK ){ - break; - } - /* Insert the decoded value */ - rc = jx9HashmapInsert(pMap,0,&sVal); - if( rc != UNQLITE_OK ){ - break; - } - } - if( rc != SXRET_OK ){ - jx9MemObjRelease(pOut); - } - jx9MemObjRelease(&sVal); - break; - } - case FJSON_DOC_START: { - /* Binary JSON object */ - jx9_value sVal,sKey; - jx9_hashmap *pMap; - /* Allocate a new hashmap */ - pMap = (jx9_hashmap *)jx9NewHashmap(pOut->pVm,0,0); - if( pMap == 0 ){ - rc = SXERR_MEM; - break; - } - jx9MemObjInit(pOut->pVm,&sVal); - jx9MemObjInit(pOut->pVm,&sKey); - jx9MemObjRelease(pOut); - MemObjSetType(pOut,MEMOBJ_HASHMAP); - pOut->x.pOther = pMap; - rc = SXRET_OK; - for(;;){ - /* Jump leading binary commas */ - while (zIn < zEnd && zIn[0] == FJSON_COMMA ){ - zIn++; - } - if( zIn >= zEnd || zIn[0] == FJSON_DOC_END ){ - if( zIn < zEnd ){ - zIn++; /* Jump the trailing binary } */ - } - break; - } - /* Extract the key */ - rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sKey,&zIn,iNest+1); - if( rc != UNQLITE_OK ){ - break; - } - if( zIn >= zEnd || zIn[0] != FJSON_COLON ){ - rc = UNQLITE_CORRUPT; - break; - } - zIn++; /* Jump the binary colon ':' */ - if( zIn >= zEnd ){ - rc = UNQLITE_CORRUPT; - break; - } - /* Decode the value */ - rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sVal,&zIn,iNest+1); - if( rc != SXRET_OK ){ - break; - } - /* Insert the key and its associated value */ - rc = jx9HashmapInsert(pMap,&sKey,&sVal); - if( rc != UNQLITE_OK ){ - break; - } - } - if( rc != SXRET_OK ){ - jx9MemObjRelease(pOut); - } - jx9MemObjRelease(&sVal); - jx9MemObjRelease(&sKey); - break; - } - default: - /* Corrupt data */ - rc = SXERR_CORRUPT; - break; - } - if( pzPtr ){ - *pzPtr = zIn; - } - return rc; -} -/* - * ---------------------------------------------------------- - * File: jx9_api.c - * MD5: 73cba599c009cee0ff878666d0543438 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: api.c v1.7 FreeBSD 2012-12-18 06:54 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* This file implement the public interfaces presented to host-applications. - * Routines in other files are for internal use by JX9 and should not be - * accessed by users of the library. - */ -#define JX9_ENGINE_MAGIC 0xF874BCD7 -#define JX9_ENGINE_MISUSE(ENGINE) (ENGINE == 0 || ENGINE->nMagic != JX9_ENGINE_MAGIC) -#define JX9_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE) -/* If another thread have released a working instance, the following macros - * evaluates to true. These macros are only used when the library - * is built with threading support enabled which is not the case in - * the default built. - */ -#define JX9_THRD_ENGINE_RELEASE(ENGINE) (ENGINE->nMagic != JX9_ENGINE_MAGIC) -#define JX9_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE) -/* IMPLEMENTATION: jx9@embedded@symisc 311-12-32 */ -/* - * All global variables are collected in the structure named "sJx9MPGlobal". - * That way it is clear in the code when we are using static variable because - * its name start with sJx9MPGlobal. - */ -static struct Jx9Global_Data -{ - SyMemBackend sAllocator; /* Global low level memory allocator */ -#if defined(JX9_ENABLE_THREADS) - const SyMutexMethods *pMutexMethods; /* Mutex methods */ - SyMutex *pMutex; /* Global mutex */ - sxu32 nThreadingLevel; /* Threading level: 0 == Single threaded/1 == Multi-Threaded - * The threading level can be set using the [jx9_lib_config()] - * interface with a configuration verb set to - * JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE or - * JX9_LIB_CONFIG_THREAD_LEVEL_MULTI - */ -#endif - const jx9_vfs *pVfs; /* Underlying virtual file system */ - sxi32 nEngine; /* Total number of active engines */ - jx9 *pEngines; /* List of active engine */ - sxu32 nMagic; /* Sanity check against library misuse */ -}sJx9MPGlobal = { - {0, 0, 0, 0, 0, 0, 0, 0, {0}}, -#if defined(JX9_ENABLE_THREADS) - 0, - 0, - 0, -#endif - 0, - 0, - 0, - 0 -}; -#define JX9_LIB_MAGIC 0xEA1495BA -#define JX9_LIB_MISUSE (sJx9MPGlobal.nMagic != JX9_LIB_MAGIC) -/* - * Supported threading level. - * These options have meaning only when the library is compiled with multi-threading - * support.That is, the JX9_ENABLE_THREADS compile time directive must be defined - * when JX9 is built. - * JX9_THREAD_LEVEL_SINGLE: - * In this mode, mutexing is disabled and the library can only be used by a single thread. - * JX9_THREAD_LEVEL_MULTI - * In this mode, all mutexes including the recursive mutexes on [jx9] objects - * are enabled so that the application is free to share the same engine - * between different threads at the same time. - */ -#define JX9_THREAD_LEVEL_SINGLE 1 -#define JX9_THREAD_LEVEL_MULTI 2 -/* - * Configure a running JX9 engine instance. - * return JX9_OK on success.Any other return - * value indicates failure. - * Refer to [jx9_config()]. - */ -JX9_PRIVATE sxi32 jx9EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap) -{ - jx9_conf *pConf = &pEngine->xConf; - int rc = JX9_OK; - /* Perform the requested operation */ - switch(nOp){ - case JX9_CONFIG_ERR_LOG:{ - /* Extract compile-time error log if any */ - const char **pzPtr = va_arg(ap, const char **); - int *pLen = va_arg(ap, int *); - if( pzPtr == 0 ){ - rc = JX9_CORRUPT; - break; - } - /* NULL terminate the error-log buffer */ - SyBlobNullAppend(&pConf->sErrConsumer); - /* Point to the error-log buffer */ - *pzPtr = (const char *)SyBlobData(&pConf->sErrConsumer); - if( pLen ){ - if( SyBlobLength(&pConf->sErrConsumer) > 1 /* NULL '\0' terminator */ ){ - *pLen = (int)SyBlobLength(&pConf->sErrConsumer); - }else{ - *pLen = 0; - } - } - break; - } - case JX9_CONFIG_ERR_ABORT: - /* Reserved for future use */ - break; - default: - /* Unknown configuration verb */ - rc = JX9_CORRUPT; - break; - } /* Switch() */ - return rc; -} -/* - * Configure the JX9 library. - * Return JX9_OK on success. Any other return value indicates failure. - * Refer to [jx9_lib_config()]. - */ -static sxi32 Jx9CoreConfigure(sxi32 nOp, va_list ap) -{ - int rc = JX9_OK; - switch(nOp){ - case JX9_LIB_CONFIG_VFS:{ - /* Install a virtual file system */ - const jx9_vfs *pVfs = va_arg(ap, const jx9_vfs *); - sJx9MPGlobal.pVfs = pVfs; - break; - } - case JX9_LIB_CONFIG_USER_MALLOC: { - /* Use an alternative low-level memory allocation routines */ - const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *); - /* Save the memory failure callback (if available) */ - ProcMemError xMemErr = sJx9MPGlobal.sAllocator.xMemError; - void *pMemErr = sJx9MPGlobal.sAllocator.pUserData; - if( pMethods == 0 ){ - /* Use the built-in memory allocation subsystem */ - rc = SyMemBackendInit(&sJx9MPGlobal.sAllocator, xMemErr, pMemErr); - }else{ - rc = SyMemBackendInitFromOthers(&sJx9MPGlobal.sAllocator, pMethods, xMemErr, pMemErr); - } - break; - } - case JX9_LIB_CONFIG_MEM_ERR_CALLBACK: { - /* Memory failure callback */ - ProcMemError xMemErr = va_arg(ap, ProcMemError); - void *pUserData = va_arg(ap, void *); - sJx9MPGlobal.sAllocator.xMemError = xMemErr; - sJx9MPGlobal.sAllocator.pUserData = pUserData; - break; - } - case JX9_LIB_CONFIG_USER_MUTEX: { -#if defined(JX9_ENABLE_THREADS) - /* Use an alternative low-level mutex subsystem */ - const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *); -#if defined (UNTRUST) - if( pMethods == 0 ){ - rc = JX9_CORRUPT; - } -#endif - /* Sanity check */ - if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){ - /* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */ - rc = JX9_CORRUPT; - break; - } - if( sJx9MPGlobal.pMutexMethods ){ - /* Overwrite the previous mutex subsystem */ - SyMutexRelease(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); - if( sJx9MPGlobal.pMutexMethods->xGlobalRelease ){ - sJx9MPGlobal.pMutexMethods->xGlobalRelease(); - } - sJx9MPGlobal.pMutex = 0; - } - /* Initialize and install the new mutex subsystem */ - if( pMethods->xGlobalInit ){ - rc = pMethods->xGlobalInit(); - if ( rc != JX9_OK ){ - break; - } - } - /* Create the global mutex */ - sJx9MPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST); - if( sJx9MPGlobal.pMutex == 0 ){ - /* - * If the supplied mutex subsystem is so sick that we are unable to - * create a single mutex, there is no much we can do here. - */ - if( pMethods->xGlobalRelease ){ - pMethods->xGlobalRelease(); - } - rc = JX9_CORRUPT; - break; - } - sJx9MPGlobal.pMutexMethods = pMethods; - if( sJx9MPGlobal.nThreadingLevel == 0 ){ - /* Set a default threading level */ - sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI; - } -#endif - break; - } - case JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE: -#if defined(JX9_ENABLE_THREADS) - /* Single thread mode(Only one thread is allowed to play with the library) */ - sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_SINGLE; -#endif - break; - case JX9_LIB_CONFIG_THREAD_LEVEL_MULTI: -#if defined(JX9_ENABLE_THREADS) - /* Multi-threading mode (library is thread safe and JX9 engines and virtual machines - * may be shared between multiple threads). - */ - sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI; -#endif - break; - default: - /* Unknown configuration option */ - rc = JX9_CORRUPT; - break; - } - return rc; -} -/* - * [CAPIREF: jx9_lib_config()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_lib_config(int nConfigOp, ...) -{ - va_list ap; - int rc; - if( sJx9MPGlobal.nMagic == JX9_LIB_MAGIC ){ - /* Library is already initialized, this operation is forbidden */ - return JX9_LOOKED; - } - va_start(ap, nConfigOp); - rc = Jx9CoreConfigure(nConfigOp, ap); - va_end(ap); - return rc; -} -/* - * Global library initialization - * Refer to [jx9_lib_init()] - * This routine must be called to initialize the memory allocation subsystem, the mutex - * subsystem prior to doing any serious work with the library.The first thread to call - * this routine does the initialization process and set the magic number so no body later - * can re-initialize the library.If subsequent threads call this routine before the first - * thread have finished the initialization process, then the subsequent threads must block - * until the initialization process is done. - */ -static sxi32 Jx9CoreInitialize(void) -{ - const jx9_vfs *pVfs; /* Built-in vfs */ -#if defined(JX9_ENABLE_THREADS) - const SyMutexMethods *pMutexMethods = 0; - SyMutex *pMaster = 0; -#endif - int rc; - /* - * If the library is already initialized, then a call to this routine - * is a no-op. - */ - if( sJx9MPGlobal.nMagic == JX9_LIB_MAGIC ){ - return JX9_OK; /* Already initialized */ - } - /* Point to the built-in vfs */ - pVfs = jx9ExportBuiltinVfs(); - /* Install it */ - jx9_lib_config(JX9_LIB_CONFIG_VFS, pVfs); -#if defined(JX9_ENABLE_THREADS) - if( sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_SINGLE ){ - pMutexMethods = sJx9MPGlobal.pMutexMethods; - if( pMutexMethods == 0 ){ - /* Use the built-in mutex subsystem */ - pMutexMethods = SyMutexExportMethods(); - if( pMutexMethods == 0 ){ - return JX9_CORRUPT; /* Can't happen */ - } - /* Install the mutex subsystem */ - rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MUTEX, pMutexMethods); - if( rc != JX9_OK ){ - return rc; - } - } - /* Obtain a static mutex so we can initialize the library without calling malloc() */ - pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1); - if( pMaster == 0 ){ - return JX9_CORRUPT; /* Can't happen */ - } - } - /* Lock the master mutex */ - rc = JX9_OK; - SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */ - if( sJx9MPGlobal.nMagic != JX9_LIB_MAGIC ){ -#endif - if( sJx9MPGlobal.sAllocator.pMethods == 0 ){ - /* Install a memory subsystem */ - rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */ - if( rc != JX9_OK ){ - /* If we are unable to initialize the memory backend, there is no much we can do here.*/ - goto End; - } - } -#if defined(JX9_ENABLE_THREADS) - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){ - /* Protect the memory allocation subsystem */ - rc = SyMemBackendMakeThreadSafe(&sJx9MPGlobal.sAllocator, sJx9MPGlobal.pMutexMethods); - if( rc != JX9_OK ){ - goto End; - } - } -#endif - /* Our library is initialized, set the magic number */ - sJx9MPGlobal.nMagic = JX9_LIB_MAGIC; - rc = JX9_OK; -#if defined(JX9_ENABLE_THREADS) - } /* sJx9MPGlobal.nMagic != JX9_LIB_MAGIC */ -#endif -End: -#if defined(JX9_ENABLE_THREADS) - /* Unlock the master mutex */ - SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */ -#endif - return rc; -} -/* - * Release an active JX9 engine and it's associated active virtual machines. - */ -static sxi32 EngineRelease(jx9 *pEngine) -{ - jx9_vm *pVm, *pNext; - /* Release all active VM */ - pVm = pEngine->pVms; - for(;;){ - if( pEngine->iVm < 1 ){ - break; - } - pNext = pVm->pNext; - jx9VmRelease(pVm); - pVm = pNext; - pEngine->iVm--; - } - /* Set a dummy magic number */ - pEngine->nMagic = 0x7635; - /* Release the private memory subsystem */ - SyMemBackendRelease(&pEngine->sAllocator); - return JX9_OK; -} -/* - * Release all resources consumed by the library. - * If JX9 is already shut when this routine is invoked then this - * routine is a harmless no-op. - * Note: This call is not thread safe. Refer to [jx9_lib_shutdown()]. - */ -static void JX9CoreShutdown(void) -{ - jx9 *pEngine, *pNext; - /* Release all active engines first */ - pEngine = sJx9MPGlobal.pEngines; - for(;;){ - if( sJx9MPGlobal.nEngine < 1 ){ - break; - } - pNext = pEngine->pNext; - EngineRelease(pEngine); - pEngine = pNext; - sJx9MPGlobal.nEngine--; - } -#if defined(JX9_ENABLE_THREADS) - /* Release the mutex subsystem */ - if( sJx9MPGlobal.pMutexMethods ){ - if( sJx9MPGlobal.pMutex ){ - SyMutexRelease(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); - sJx9MPGlobal.pMutex = 0; - } - if( sJx9MPGlobal.pMutexMethods->xGlobalRelease ){ - sJx9MPGlobal.pMutexMethods->xGlobalRelease(); - } - sJx9MPGlobal.pMutexMethods = 0; - } - sJx9MPGlobal.nThreadingLevel = 0; -#endif - if( sJx9MPGlobal.sAllocator.pMethods ){ - /* Release the memory backend */ - SyMemBackendRelease(&sJx9MPGlobal.sAllocator); - } - sJx9MPGlobal.nMagic = 0x1928; -} -/* - * [CAPIREF: jx9_lib_shutdown()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_lib_shutdown(void) -{ - if( sJx9MPGlobal.nMagic != JX9_LIB_MAGIC ){ - /* Already shut */ - return JX9_OK; - } - JX9CoreShutdown(); - return JX9_OK; -} -/* - * [CAPIREF: jx9_lib_signature()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE const char * jx9_lib_signature(void) -{ - return JX9_SIG; -} -/* - * [CAPIREF: jx9_init()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_init(jx9 **ppEngine) -{ - jx9 *pEngine; - int rc; -#if defined(UNTRUST) - if( ppEngine == 0 ){ - return JX9_CORRUPT; - } -#endif - *ppEngine = 0; - /* One-time automatic library initialization */ - rc = Jx9CoreInitialize(); - if( rc != JX9_OK ){ - return rc; - } - /* Allocate a new engine */ - pEngine = (jx9 *)SyMemBackendPoolAlloc(&sJx9MPGlobal.sAllocator, sizeof(jx9)); - if( pEngine == 0 ){ - return JX9_NOMEM; - } - /* Zero the structure */ - SyZero(pEngine, sizeof(jx9)); - /* Initialize engine fields */ - pEngine->nMagic = JX9_ENGINE_MAGIC; - rc = SyMemBackendInitFromParent(&pEngine->sAllocator, &sJx9MPGlobal.sAllocator); - if( rc != JX9_OK ){ - goto Release; - } -#if defined(JX9_ENABLE_THREADS) - SyMemBackendDisbaleMutexing(&pEngine->sAllocator); -#endif - /* Default configuration */ - SyBlobInit(&pEngine->xConf.sErrConsumer, &pEngine->sAllocator); - /* Install a default compile-time error consumer routine */ - pEngine->xConf.xErr = jx9VmBlobConsumer; - pEngine->xConf.pErrData = &pEngine->xConf.sErrConsumer; - /* Built-in vfs */ - pEngine->pVfs = sJx9MPGlobal.pVfs; -#if defined(JX9_ENABLE_THREADS) - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){ - /* Associate a recursive mutex with this instance */ - pEngine->pMutex = SyMutexNew(sJx9MPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE); - if( pEngine->pMutex == 0 ){ - rc = JX9_NOMEM; - goto Release; - } - } -#endif - /* Link to the list of active engines */ -#if defined(JX9_ENABLE_THREADS) - /* Enter the global mutex */ - SyMutexEnter(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */ -#endif - MACRO_LD_PUSH(sJx9MPGlobal.pEngines, pEngine); - sJx9MPGlobal.nEngine++; -#if defined(JX9_ENABLE_THREADS) - /* Leave the global mutex */ - SyMutexLeave(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */ -#endif - /* Write a pointer to the new instance */ - *ppEngine = pEngine; - return JX9_OK; -Release: - SyMemBackendRelease(&pEngine->sAllocator); - SyMemBackendPoolFree(&sJx9MPGlobal.sAllocator,pEngine); - return rc; -} -/* - * [CAPIREF: jx9_release()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_release(jx9 *pEngine) -{ - int rc; - if( JX9_ENGINE_MISUSE(pEngine) ){ - return JX9_CORRUPT; - } -#if defined(JX9_ENABLE_THREADS) - /* Acquire engine mutex */ - SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && - JX9_THRD_ENGINE_RELEASE(pEngine) ){ - return JX9_ABORT; /* Another thread have released this instance */ - } -#endif - /* Release the engine */ - rc = EngineRelease(&(*pEngine)); -#if defined(JX9_ENABLE_THREADS) - /* Leave engine mutex */ - SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ - /* Release engine mutex */ - SyMutexRelease(sJx9MPGlobal.pMutexMethods, pEngine->pMutex) /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ -#endif -#if defined(JX9_ENABLE_THREADS) - /* Enter the global mutex */ - SyMutexEnter(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */ -#endif - /* Unlink from the list of active engines */ - MACRO_LD_REMOVE(sJx9MPGlobal.pEngines, pEngine); - sJx9MPGlobal.nEngine--; -#if defined(JX9_ENABLE_THREADS) - /* Leave the global mutex */ - SyMutexLeave(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */ -#endif - /* Release the memory chunk allocated to this engine */ - SyMemBackendPoolFree(&sJx9MPGlobal.sAllocator, pEngine); - return rc; -} -/* - * Compile a raw JX9 script. - * To execute a JX9 code, it must first be compiled into a bytecode program using this routine. - * If something goes wrong [i.e: compile-time error], your error log [i.e: error consumer callback] - * should display the appropriate error message and this function set ppVm to null and return - * an error code that is different from JX9_OK. Otherwise when the script is successfully compiled - * ppVm should hold the JX9 bytecode and it's safe to call [jx9_vm_exec(), jx9_vm_reset(), etc.]. - * This API does not actually evaluate the JX9 code. It merely compile and prepares the JX9 script - * for evaluation. - */ -static sxi32 ProcessScript( - jx9 *pEngine, /* Running JX9 engine */ - jx9_vm **ppVm, /* OUT: A pointer to the virtual machine */ - SyString *pScript, /* Raw JX9 script to compile */ - sxi32 iFlags, /* Compile-time flags */ - const char *zFilePath /* File path if script come from a file. NULL otherwise */ - ) -{ - jx9_vm *pVm; - int rc; - /* Allocate a new virtual machine */ - pVm = (jx9_vm *)SyMemBackendPoolAlloc(&pEngine->sAllocator, sizeof(jx9_vm)); - if( pVm == 0 ){ - /* If the supplied memory subsystem is so sick that we are unable to allocate - * a tiny chunk of memory, there is no much we can do here. */ - if( ppVm ){ - *ppVm = 0; - } - return JX9_NOMEM; - } - if( iFlags < 0 ){ - /* Default compile-time flags */ - iFlags = 0; - } - /* Initialize the Virtual Machine */ - rc = jx9VmInit(pVm, &(*pEngine)); - if( rc != JX9_OK ){ - SyMemBackendPoolFree(&pEngine->sAllocator, pVm); - if( ppVm ){ - *ppVm = 0; - } - return JX9_VM_ERR; - } - if( zFilePath ){ - /* Push processed file path */ - jx9VmPushFilePath(pVm, zFilePath, -1, TRUE, 0); - } - /* Reset the error message consumer */ - SyBlobReset(&pEngine->xConf.sErrConsumer); - /* Compile the script */ - jx9CompileScript(pVm, &(*pScript), iFlags); - if( pVm->sCodeGen.nErr > 0 || pVm == 0){ - sxu32 nErr = pVm->sCodeGen.nErr; - /* Compilation error or null ppVm pointer, release this VM */ - SyMemBackendRelease(&pVm->sAllocator); - SyMemBackendPoolFree(&pEngine->sAllocator, pVm); - if( ppVm ){ - *ppVm = 0; - } - return nErr > 0 ? JX9_COMPILE_ERR : JX9_OK; - } - /* Prepare the virtual machine for bytecode execution */ - rc = jx9VmMakeReady(pVm); - if( rc != JX9_OK ){ - goto Release; - } - /* Install local import path which is the current directory */ - jx9_vm_config(pVm, JX9_VM_CONFIG_IMPORT_PATH, "./"); -#if defined(JX9_ENABLE_THREADS) - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){ - /* Associate a recursive mutex with this instance */ - pVm->pMutex = SyMutexNew(sJx9MPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE); - if( pVm->pMutex == 0 ){ - goto Release; - } - } -#endif - /* Script successfully compiled, link to the list of active virtual machines */ - MACRO_LD_PUSH(pEngine->pVms, pVm); - pEngine->iVm++; - /* Point to the freshly created VM */ - *ppVm = pVm; - /* Ready to execute JX9 bytecode */ - return JX9_OK; -Release: - SyMemBackendRelease(&pVm->sAllocator); - SyMemBackendPoolFree(&pEngine->sAllocator, pVm); - *ppVm = 0; - return JX9_VM_ERR; -} -/* - * [CAPIREF: jx9_compile()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm) -{ - SyString sScript; - int rc; - if( JX9_ENGINE_MISUSE(pEngine) ){ - return JX9_CORRUPT; - } - if( zSource == 0 ){ - /* Empty Jx9 statement ';' */ - zSource = ";"; - nLen = (int)sizeof(char); - } - if( nLen < 0 ){ - /* Compute input length automatically */ - nLen = (int)SyStrlen(zSource); - } - SyStringInitFromBuf(&sScript, zSource, nLen); -#if defined(JX9_ENABLE_THREADS) - /* Acquire engine mutex */ - SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && - JX9_THRD_ENGINE_RELEASE(pEngine) ){ - return JX9_ABORT; /* Another thread have released this instance */ - } -#endif - /* Compile the script */ - rc = ProcessScript(&(*pEngine),ppOutVm,&sScript,0,0); -#if defined(JX9_ENABLE_THREADS) - /* Leave engine mutex */ - SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ -#endif - /* Compilation result */ - return rc; -} -/* - * [CAPIREF: jx9_compile_file()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm) -{ - const jx9_vfs *pVfs; - int rc; - if( ppOutVm ){ - *ppOutVm = 0; - } - rc = JX9_OK; /* cc warning */ - if( JX9_ENGINE_MISUSE(pEngine) || SX_EMPTY_STR(zFilePath) ){ - return JX9_CORRUPT; - } -#if defined(JX9_ENABLE_THREADS) - /* Acquire engine mutex */ - SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && - JX9_THRD_ENGINE_RELEASE(pEngine) ){ - return JX9_ABORT; /* Another thread have released this instance */ - } -#endif - /* - * Check if the underlying vfs implement the memory map - * [i.e: mmap() under UNIX/MapViewOfFile() under windows] function. - */ - pVfs = pEngine->pVfs; - if( pVfs == 0 || pVfs->xMmap == 0 ){ - /* Memory map routine not implemented */ - rc = JX9_IO_ERR; - }else{ - void *pMapView = 0; /* cc warning */ - jx9_int64 nSize = 0; /* cc warning */ - SyString sScript; - /* Try to get a memory view of the whole file */ - rc = pVfs->xMmap(zFilePath, &pMapView, &nSize); - if( rc != JX9_OK ){ - /* Assume an IO error */ - rc = JX9_IO_ERR; - }else{ - /* Compile the file */ - SyStringInitFromBuf(&sScript, pMapView, nSize); - rc = ProcessScript(&(*pEngine), ppOutVm, &sScript,0,zFilePath); - /* Release the memory view of the whole file */ - if( pVfs->xUnmap ){ - pVfs->xUnmap(pMapView, nSize); - } - } - } -#if defined(JX9_ENABLE_THREADS) - /* Leave engine mutex */ - SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ -#endif - /* Compilation result */ - return rc; -} -/* - * [CAPIREF: jx9_vm_config()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...) -{ - va_list ap; - int rc; - /* Ticket 1433-002: NULL VM is harmless operation */ - if ( JX9_VM_MISUSE(pVm) ){ - return JX9_CORRUPT; - } -#if defined(JX9_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && - JX9_THRD_VM_RELEASE(pVm) ){ - return JX9_ABORT; /* Another thread have released this instance */ - } -#endif - /* Confiugure the virtual machine */ - va_start(ap, iConfigOp); - rc = jx9VmConfigure(&(*pVm), iConfigOp, ap); - va_end(ap); -#if defined(JX9_ENABLE_THREADS) - /* Leave VM mutex */ - SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -/* - * [CAPIREF: jx9_vm_release()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_vm_release(jx9_vm *pVm) -{ - jx9 *pEngine; - int rc; - /* Ticket 1433-002: NULL VM is harmless operation */ - if ( JX9_VM_MISUSE(pVm) ){ - return JX9_CORRUPT; - } -#if defined(JX9_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && - JX9_THRD_VM_RELEASE(pVm) ){ - return JX9_ABORT; /* Another thread have released this instance */ - } -#endif - pEngine = pVm->pEngine; - rc = jx9VmRelease(&(*pVm)); -#if defined(JX9_ENABLE_THREADS) - /* Leave VM mutex */ - SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ - /* Release VM mutex */ - SyMutexRelease(sJx9MPGlobal.pMutexMethods, pVm->pMutex) /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ -#endif - if( rc == JX9_OK ){ - /* Unlink from the list of active VM */ -#if defined(JX9_ENABLE_THREADS) - /* Acquire engine mutex */ - SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && - JX9_THRD_ENGINE_RELEASE(pEngine) ){ - return JX9_ABORT; /* Another thread have released this instance */ - } -#endif - MACRO_LD_REMOVE(pEngine->pVms, pVm); - pEngine->iVm--; - /* Release the memory chunk allocated to this VM */ - SyMemBackendPoolFree(&pEngine->sAllocator, pVm); -#if defined(JX9_ENABLE_THREADS) - /* Leave engine mutex */ - SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ -#endif - } - return rc; -} -/* - * [CAPIREF: jx9_create_function()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData) -{ - SyString sName; - int rc; - /* Ticket 1433-002: NULL VM is harmless operation */ - if ( JX9_VM_MISUSE(pVm) ){ - return JX9_CORRUPT; - } - SyStringInitFromBuf(&sName, zName, SyStrlen(zName)); - /* Remove leading and trailing white spaces */ - SyStringFullTrim(&sName); - /* Ticket 1433-003: NULL values are not allowed */ - if( sName.nByte < 1 || xFunc == 0 ){ - return JX9_CORRUPT; - } -#if defined(JX9_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && - JX9_THRD_VM_RELEASE(pVm) ){ - return JX9_ABORT; /* Another thread have released this instance */ - } -#endif - /* Install the foreign function */ - rc = jx9VmInstallForeignFunction(&(*pVm), &sName, xFunc, pUserData); -#if defined(JX9_ENABLE_THREADS) - /* Leave VM mutex */ - SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -JX9_PRIVATE int jx9DeleteFunction(jx9_vm *pVm,const char *zName) -{ - jx9_user_func *pFunc = 0; /* cc warning */ - int rc; - /* Perform the deletion */ - rc = SyHashDeleteEntry(&pVm->hHostFunction, (const void *)zName, SyStrlen(zName), (void **)&pFunc); - if( rc == JX9_OK ){ - /* Release internal fields */ - SySetRelease(&pFunc->aAux); - SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName)); - SyMemBackendPoolFree(&pVm->sAllocator, pFunc); - } - return rc; -} -/* - * [CAPIREF: jx9_create_constant()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData) -{ - SyString sName; - int rc; - /* Ticket 1433-002: NULL VM is harmless operation */ - if ( JX9_VM_MISUSE(pVm) ){ - return JX9_CORRUPT; - } - SyStringInitFromBuf(&sName, zName, SyStrlen(zName)); - /* Remove leading and trailing white spaces */ - SyStringFullTrim(&sName); - if( sName.nByte < 1 ){ - /* Empty constant name */ - return JX9_CORRUPT; - } - /* TICKET 1433-003: NULL pointer is harmless operation */ - if( xExpand == 0 ){ - return JX9_CORRUPT; - } -#if defined(JX9_ENABLE_THREADS) - /* Acquire VM mutex */ - SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ - if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && - JX9_THRD_VM_RELEASE(pVm) ){ - return JX9_ABORT; /* Another thread have released this instance */ - } -#endif - /* Perform the registration */ - rc = jx9VmRegisterConstant(&(*pVm), &sName, xExpand, pUserData); -#if defined(JX9_ENABLE_THREADS) - /* Leave VM mutex */ - SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */ -#endif - return rc; -} -JX9_PRIVATE int Jx9DeleteConstant(jx9_vm *pVm,const char *zName) -{ - jx9_constant *pCons; - int rc; - /* Query the constant hashtable */ - rc = SyHashDeleteEntry(&pVm->hConstant, (const void *)zName, SyStrlen(zName), (void **)&pCons); - if( rc == JX9_OK ){ - /* Perform the deletion */ - SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pCons->sName)); - SyMemBackendPoolFree(&pVm->sAllocator, pCons); - } - return rc; -} -/* - * [CAPIREF: jx9_new_scalar()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE jx9_value * jx9_new_scalar(jx9_vm *pVm) -{ - jx9_value *pObj; - /* Ticket 1433-002: NULL VM is harmless operation */ - if ( JX9_VM_MISUSE(pVm) ){ - return 0; - } - /* Allocate a new scalar variable */ - pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value)); - if( pObj == 0 ){ - return 0; - } - /* Nullify the new scalar */ - jx9MemObjInit(pVm, pObj); - return pObj; -} -/* - * [CAPIREF: jx9_new_array()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE jx9_value * jx9_new_array(jx9_vm *pVm) -{ - jx9_hashmap *pMap; - jx9_value *pObj; - /* Ticket 1433-002: NULL VM is harmless operation */ - if ( JX9_VM_MISUSE(pVm) ){ - return 0; - } - /* Create a new hashmap first */ - pMap = jx9NewHashmap(&(*pVm), 0, 0); - if( pMap == 0 ){ - return 0; - } - /* Associate a new jx9_value with this hashmap */ - pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value)); - if( pObj == 0 ){ - jx9HashmapRelease(pMap, TRUE); - return 0; - } - jx9MemObjInitFromArray(pVm, pObj, pMap); - return pObj; -} -/* - * [CAPIREF: jx9_release_value()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_release_value(jx9_vm *pVm, jx9_value *pValue) -{ - /* Ticket 1433-002: NULL VM is a harmless operation */ - if ( JX9_VM_MISUSE(pVm) ){ - return JX9_CORRUPT; - } - if( pValue ){ - /* Release the value */ - jx9MemObjRelease(pValue); - SyMemBackendPoolFree(&pVm->sAllocator, pValue); - } - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_to_int()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_to_int(jx9_value *pValue) -{ - int rc; - rc = jx9MemObjToInteger(pValue); - if( rc != JX9_OK ){ - return 0; - } - return (int)pValue->x.iVal; -} -/* - * [CAPIREF: jx9_value_to_bool()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_to_bool(jx9_value *pValue) -{ - int rc; - rc = jx9MemObjToBool(pValue); - if( rc != JX9_OK ){ - return 0; - } - return (int)pValue->x.iVal; -} -/* - * [CAPIREF: jx9_value_to_int64()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE jx9_int64 jx9_value_to_int64(jx9_value *pValue) -{ - int rc; - rc = jx9MemObjToInteger(pValue); - if( rc != JX9_OK ){ - return 0; - } - return pValue->x.iVal; -} -/* - * [CAPIREF: jx9_value_to_double()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE double jx9_value_to_double(jx9_value *pValue) -{ - int rc; - rc = jx9MemObjToReal(pValue); - if( rc != JX9_OK ){ - return (double)0; - } - return (double)pValue->x.rVal; -} -/* - * [CAPIREF: jx9_value_to_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE const char * jx9_value_to_string(jx9_value *pValue, int *pLen) -{ - jx9MemObjToString(pValue); - if( SyBlobLength(&pValue->sBlob) > 0 ){ - SyBlobNullAppend(&pValue->sBlob); - if( pLen ){ - *pLen = (int)SyBlobLength(&pValue->sBlob); - } - return (const char *)SyBlobData(&pValue->sBlob); - }else{ - /* Return the empty string */ - if( pLen ){ - *pLen = 0; - } - return ""; - } -} -/* - * [CAPIREF: jx9_value_to_resource()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE void * jx9_value_to_resource(jx9_value *pValue) -{ - if( (pValue->iFlags & MEMOBJ_RES) == 0 ){ - /* Not a resource, return NULL */ - return 0; - } - return pValue->x.pOther; -} -/* - * [CAPIREF: jx9_value_compare()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict) -{ - int rc; - if( pLeft == 0 || pRight == 0 ){ - /* TICKET 1433-24: NULL values is harmless operation */ - return 1; - } - /* Perform the comparison */ - rc = jx9MemObjCmp(&(*pLeft), &(*pRight), bStrict, 0); - /* Comparison result */ - return rc; -} -/* - * [CAPIREF: jx9_result_int()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_result_int(jx9_context *pCtx, int iValue) -{ - return jx9_value_int(pCtx->pRet, iValue); -} -/* - * [CAPIREF: jx9_result_int64()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue) -{ - return jx9_value_int64(pCtx->pRet, iValue); -} -/* - * [CAPIREF: jx9_result_bool()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_result_bool(jx9_context *pCtx, int iBool) -{ - return jx9_value_bool(pCtx->pRet, iBool); -} -/* - * [CAPIREF: jx9_result_double()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_result_double(jx9_context *pCtx, double Value) -{ - return jx9_value_double(pCtx->pRet, Value); -} -/* - * [CAPIREF: jx9_result_null()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_result_null(jx9_context *pCtx) -{ - /* Invalidate any prior representation and set the NULL flag */ - jx9MemObjRelease(pCtx->pRet); - return JX9_OK; -} -/* - * [CAPIREF: jx9_result_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen) -{ - return jx9_value_string(pCtx->pRet, zString, nLen); -} -/* - * [CAPIREF: jx9_result_string_format()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...) -{ - jx9_value *p; - va_list ap; - int rc; - p = pCtx->pRet; - if( (p->iFlags & MEMOBJ_STRING) == 0 ){ - /* Invalidate any prior representation */ - jx9MemObjRelease(p); - MemObjSetType(p, MEMOBJ_STRING); - } - /* Format the given string */ - va_start(ap, zFormat); - rc = SyBlobFormatAp(&p->sBlob, zFormat, ap); - va_end(ap); - return rc; -} -/* - * [CAPIREF: jx9_result_value()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_result_value(jx9_context *pCtx, jx9_value *pValue) -{ - int rc = JX9_OK; - if( pValue == 0 ){ - jx9MemObjRelease(pCtx->pRet); - }else{ - rc = jx9MemObjStore(pValue, pCtx->pRet); - } - return rc; -} -/* - * [CAPIREF: jx9_result_resource()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_result_resource(jx9_context *pCtx, void *pUserData) -{ - return jx9_value_resource(pCtx->pRet, pUserData); -} -/* - * [CAPIREF: jx9_context_new_scalar()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE jx9_value * jx9_context_new_scalar(jx9_context *pCtx) -{ - jx9_value *pVal; - pVal = jx9_new_scalar(pCtx->pVm); - if( pVal ){ - /* Record value address so it can be freed automatically - * when the calling function returns. - */ - SySetPut(&pCtx->sVar, (const void *)&pVal); - } - return pVal; -} -/* - * [CAPIREF: jx9_context_new_array()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE jx9_value * jx9_context_new_array(jx9_context *pCtx) -{ - jx9_value *pVal; - pVal = jx9_new_array(pCtx->pVm); - if( pVal ){ - /* Record value address so it can be freed automatically - * when the calling function returns. - */ - SySetPut(&pCtx->sVar, (const void *)&pVal); - } - return pVal; -} -/* - * [CAPIREF: jx9_context_release_value()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue) -{ - jx9VmReleaseContextValue(&(*pCtx), pValue); -} -/* - * [CAPIREF: jx9_context_alloc_chunk()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease) -{ - void *pChunk; - pChunk = SyMemBackendAlloc(&pCtx->pVm->sAllocator, nByte); - if( pChunk ){ - if( ZeroChunk ){ - /* Zero the memory chunk */ - SyZero(pChunk, nByte); - } - if( AutoRelease ){ - jx9_aux_data sAux; - /* Track the chunk so that it can be released automatically - * upon this context is destroyed. - */ - sAux.pAuxData = pChunk; - SySetPut(&pCtx->sChunk, (const void *)&sAux); - } - } - return pChunk; -} -/* - * Check if the given chunk address is registered in the call context - * chunk container. - * Return TRUE if registered.FALSE otherwise. - * Refer to [jx9_context_realloc_chunk(), jx9_context_free_chunk()]. - */ -static jx9_aux_data * ContextFindChunk(jx9_context *pCtx, void *pChunk) -{ - jx9_aux_data *aAux, *pAux; - sxu32 n; - if( SySetUsed(&pCtx->sChunk) < 1 ){ - /* Don't bother processing, the container is empty */ - return 0; - } - /* Perform the lookup */ - aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk); - for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){ - pAux = &aAux[n]; - if( pAux->pAuxData == pChunk ){ - /* Chunk found */ - return pAux; - } - } - /* No such allocated chunk */ - return 0; -} -/* - * [CAPIREF: jx9_context_realloc_chunk()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte) -{ - jx9_aux_data *pAux; - void *pNew; - pNew = SyMemBackendRealloc(&pCtx->pVm->sAllocator, pChunk, nByte); - if( pNew ){ - pAux = ContextFindChunk(pCtx, pChunk); - if( pAux ){ - pAux->pAuxData = pNew; - } - } - return pNew; -} -/* - * [CAPIREF: jx9_context_free_chunk()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk) -{ - jx9_aux_data *pAux; - if( pChunk == 0 ){ - /* TICKET-1433-93: NULL chunk is a harmless operation */ - return; - } - pAux = ContextFindChunk(pCtx, pChunk); - if( pAux ){ - /* Mark as destroyed */ - pAux->pAuxData = 0; - } - SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk); -} -/* - * [CAPIREF: jx9_array_fetch()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte) -{ - jx9_hashmap_node *pNode; - jx9_value *pValue; - jx9_value skey; - int rc; - /* Make sure we are dealing with a valid hashmap */ - if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ - return 0; - } - if( nByte < 0 ){ - nByte = (int)SyStrlen(zKey); - } - /* Convert the key to a jx9_value */ - jx9MemObjInit(pArray->pVm, &skey); - jx9MemObjStringAppend(&skey, zKey, (sxu32)nByte); - /* Perform the lookup */ - rc = jx9HashmapLookup((jx9_hashmap *)pArray->x.pOther, &skey, &pNode); - jx9MemObjRelease(&skey); - if( rc != JX9_OK ){ - /* No such entry */ - return 0; - } - /* Extract the target value */ - pValue = (jx9_value *)SySetAt(&pArray->pVm->aMemObj, pNode->nValIdx); - return pValue; -} -/* - * [CAPIREF: jx9_array_walk()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *pValue, jx9_value *, void *), void *pUserData) -{ - int rc; - if( xWalk == 0 ){ - return JX9_CORRUPT; - } - /* Make sure we are dealing with a valid hashmap */ - if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ - return JX9_CORRUPT; - } - /* Start the walk process */ - rc = jx9HashmapWalk((jx9_hashmap *)pArray->x.pOther, xWalk, pUserData); - return rc != JX9_OK ? JX9_ABORT /* User callback request an operation abort*/ : JX9_OK; -} -/* - * [CAPIREF: jx9_array_add_elem()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue) -{ - int rc; - /* Make sure we are dealing with a valid hashmap */ - if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ - return JX9_CORRUPT; - } - /* Perform the insertion */ - rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &(*pKey), &(*pValue)); - return rc; -} -/* - * [CAPIREF: jx9_array_add_strkey_elem()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue) -{ - int rc; - /* Make sure we are dealing with a valid hashmap */ - if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ - return JX9_CORRUPT; - } - /* Perform the insertion */ - if( SX_EMPTY_STR(zKey) ){ - /* Empty key, assign an automatic index */ - rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, 0, &(*pValue)); - }else{ - jx9_value sKey; - jx9MemObjInitFromString(pArray->pVm, &sKey, 0); - jx9MemObjStringAppend(&sKey, zKey, (sxu32)SyStrlen(zKey)); - rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &sKey, &(*pValue)); - jx9MemObjRelease(&sKey); - } - return rc; -} -/* - * [CAPIREF: jx9_array_count()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE unsigned int jx9_array_count(jx9_value *pArray) -{ - jx9_hashmap *pMap; - /* Make sure we are dealing with a valid hashmap */ - if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ - return 0; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_hashmap *)pArray->x.pOther; - return pMap->nEntry; -} -/* - * [CAPIREF: jx9_context_output()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen) -{ - SyString sData; - int rc; - if( nLen < 0 ){ - nLen = (int)SyStrlen(zString); - } - SyStringInitFromBuf(&sData, zString, nLen); - rc = jx9VmOutputConsume(pCtx->pVm, &sData); - return rc; -} -/* - * [CAPIREF: jx9_context_throw_error()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr) -{ - int rc = JX9_OK; - if( zErr ){ - rc = jx9VmThrowError(pCtx->pVm, &pCtx->pFunc->sName, iErr, zErr); - } - return rc; -} -/* - * [CAPIREF: jx9_context_throw_error_format()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...) -{ - va_list ap; - int rc; - if( zFormat == 0){ - return JX9_OK; - } - va_start(ap, zFormat); - rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap); - va_end(ap); - return rc; -} -/* - * [CAPIREF: jx9_context_random_num()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE unsigned int jx9_context_random_num(jx9_context *pCtx) -{ - sxu32 n; - n = jx9VmRandomNum(pCtx->pVm); - return n; -} -/* - * [CAPIREF: jx9_context_random_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen) -{ - if( nBuflen < 3 ){ - return JX9_CORRUPT; - } - jx9VmRandomString(pCtx->pVm, zBuf, nBuflen); - return JX9_OK; -} -/* - * [CAPIREF: jx9_context_user_data()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE void * jx9_context_user_data(jx9_context *pCtx) -{ - return pCtx->pFunc->pUserData; -} -/* - * [CAPIREF: jx9_context_push_aux_data()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData) -{ - jx9_aux_data sAux; - int rc; - sAux.pAuxData = pUserData; - rc = SySetPut(&pCtx->pFunc->aAux, (const void *)&sAux); - return rc; -} -/* - * [CAPIREF: jx9_context_peek_aux_data()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE void * jx9_context_peek_aux_data(jx9_context *pCtx) -{ - jx9_aux_data *pAux; - pAux = (jx9_aux_data *)SySetPeek(&pCtx->pFunc->aAux); - return pAux ? pAux->pAuxData : 0; -} -/* - * [CAPIREF: jx9_context_pop_aux_data()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE void * jx9_context_pop_aux_data(jx9_context *pCtx) -{ - jx9_aux_data *pAux; - pAux = (jx9_aux_data *)SySetPop(&pCtx->pFunc->aAux); - return pAux ? pAux->pAuxData : 0; -} -/* - * [CAPIREF: jx9_context_result_buf_length()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE unsigned int jx9_context_result_buf_length(jx9_context *pCtx) -{ - return SyBlobLength(&pCtx->pRet->sBlob); -} -/* - * [CAPIREF: jx9_function_name()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE const char * jx9_function_name(jx9_context *pCtx) -{ - SyString *pName; - pName = &pCtx->pFunc->sName; - return pName->zString; -} -/* - * [CAPIREF: jx9_value_int()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_int(jx9_value *pVal, int iValue) -{ - /* Invalidate any prior representation */ - jx9MemObjRelease(pVal); - pVal->x.iVal = (jx9_int64)iValue; - MemObjSetType(pVal, MEMOBJ_INT); - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_int64()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue) -{ - /* Invalidate any prior representation */ - jx9MemObjRelease(pVal); - pVal->x.iVal = iValue; - MemObjSetType(pVal, MEMOBJ_INT); - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_bool()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_bool(jx9_value *pVal, int iBool) -{ - /* Invalidate any prior representation */ - jx9MemObjRelease(pVal); - pVal->x.iVal = iBool ? 1 : 0; - MemObjSetType(pVal, MEMOBJ_BOOL); - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_null()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_null(jx9_value *pVal) -{ - /* Invalidate any prior representation and set the NULL flag */ - jx9MemObjRelease(pVal); - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_double()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_double(jx9_value *pVal, double Value) -{ - /* Invalidate any prior representation */ - jx9MemObjRelease(pVal); - pVal->x.rVal = (jx9_real)Value; - MemObjSetType(pVal, MEMOBJ_REAL); - /* Try to get an integer representation also */ - jx9MemObjTryInteger(pVal); - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_string(jx9_value *pVal, const char *zString, int nLen) -{ - if((pVal->iFlags & MEMOBJ_STRING) == 0 ){ - /* Invalidate any prior representation */ - jx9MemObjRelease(pVal); - MemObjSetType(pVal, MEMOBJ_STRING); - } - if( zString ){ - if( nLen < 0 ){ - /* Compute length automatically */ - nLen = (int)SyStrlen(zString); - } - SyBlobAppend(&pVal->sBlob, (const void *)zString, (sxu32)nLen); - } - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_string_format()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...) -{ - va_list ap; - int rc; - if((pVal->iFlags & MEMOBJ_STRING) == 0 ){ - /* Invalidate any prior representation */ - jx9MemObjRelease(pVal); - MemObjSetType(pVal, MEMOBJ_STRING); - } - va_start(ap, zFormat); - rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap); - va_end(ap); - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_reset_string_cursor()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_reset_string_cursor(jx9_value *pVal) -{ - /* Reset the string cursor */ - SyBlobReset(&pVal->sBlob); - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_resource()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_resource(jx9_value *pVal, void *pUserData) -{ - /* Invalidate any prior representation */ - jx9MemObjRelease(pVal); - /* Reflect the new type */ - pVal->x.pOther = pUserData; - MemObjSetType(pVal, MEMOBJ_RES); - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_release()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_release(jx9_value *pVal) -{ - jx9MemObjRelease(pVal); - return JX9_OK; -} -/* - * [CAPIREF: jx9_value_is_int()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_int(jx9_value *pVal) -{ - return (pVal->iFlags & MEMOBJ_INT) ? TRUE : FALSE; -} -/* - * [CAPIREF: jx9_value_is_float()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_float(jx9_value *pVal) -{ - return (pVal->iFlags & MEMOBJ_REAL) ? TRUE : FALSE; -} -/* - * [CAPIREF: jx9_value_is_bool()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_bool(jx9_value *pVal) -{ - return (pVal->iFlags & MEMOBJ_BOOL) ? TRUE : FALSE; -} -/* - * [CAPIREF: jx9_value_is_string()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_string(jx9_value *pVal) -{ - return (pVal->iFlags & MEMOBJ_STRING) ? TRUE : FALSE; -} -/* - * [CAPIREF: jx9_value_is_null()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_null(jx9_value *pVal) -{ - return (pVal->iFlags & MEMOBJ_NULL) ? TRUE : FALSE; -} -/* - * [CAPIREF: jx9_value_is_numeric()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_numeric(jx9_value *pVal) -{ - int rc; - rc = jx9MemObjIsNumeric(pVal); - return rc; -} -/* - * [CAPIREF: jx9_value_is_callable()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_callable(jx9_value *pVal) -{ - int rc; - rc = jx9VmIsCallable(pVal->pVm, pVal); - return rc; -} -/* - * [CAPIREF: jx9_value_is_scalar()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_scalar(jx9_value *pVal) -{ - return (pVal->iFlags & MEMOBJ_SCALAR) ? TRUE : FALSE; -} -/* - * [CAPIREF: jx9_value_is_json_array()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_json_array(jx9_value *pVal) -{ - return (pVal->iFlags & MEMOBJ_HASHMAP) ? TRUE : FALSE; -} -/* - * [CAPIREF: jx9_value_is_json_object()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_json_object(jx9_value *pVal) -{ - jx9_hashmap *pMap; - if( (pVal->iFlags & MEMOBJ_HASHMAP) == 0 ){ - return FALSE; - } - pMap = (jx9_hashmap *)pVal->x.pOther; - if( (pMap->iFlags & HASHMAP_JSON_OBJECT) == 0 ){ - return FALSE; - } - return TRUE; -} -/* - * [CAPIREF: jx9_value_is_resource()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_resource(jx9_value *pVal) -{ - return (pVal->iFlags & MEMOBJ_RES) ? TRUE : FALSE; -} -/* - * [CAPIREF: jx9_value_is_empty()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -JX9_PRIVATE int jx9_value_is_empty(jx9_value *pVal) -{ - int rc; - rc = jx9MemObjIsEmpty(pVal); - return rc; -} -/* - * ---------------------------------------------------------- - * File: jx9_builtin.c - * MD5: 97ae6ddf8ded9fe14634060675e12f80 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: builtin.c v1.7 Win7 2012-12-13 00:01 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* This file implement built-in 'foreign' functions for the JX9 engine */ -/* - * Section: - * Variable handling Functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * bool is_bool($var) - * Finds out whether a variable is a boolean. - * Parameters - * $var: The variable being evaluated. - * Return - * TRUE if var is a boolean. False otherwise. - */ -static int jx9Builtin_is_bool(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 0; /* Assume false by default */ - if( nArg > 0 ){ - res = jx9_value_is_bool(apArg[0]); - } - /* Query result */ - jx9_result_bool(pCtx, res); - return JX9_OK; -} -/* - * bool is_float($var) - * bool is_real($var) - * bool is_double($var) - * Finds out whether a variable is a float. - * Parameters - * $var: The variable being evaluated. - * Return - * TRUE if var is a float. False otherwise. - */ -static int jx9Builtin_is_float(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 0; /* Assume false by default */ - if( nArg > 0 ){ - res = jx9_value_is_float(apArg[0]); - } - /* Query result */ - jx9_result_bool(pCtx, res); - return JX9_OK; -} -/* - * bool is_int($var) - * bool is_integer($var) - * bool is_long($var) - * Finds out whether a variable is an integer. - * Parameters - * $var: The variable being evaluated. - * Return - * TRUE if var is an integer. False otherwise. - */ -static int jx9Builtin_is_int(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 0; /* Assume false by default */ - if( nArg > 0 ){ - res = jx9_value_is_int(apArg[0]); - } - /* Query result */ - jx9_result_bool(pCtx, res); - return JX9_OK; -} -/* - * bool is_string($var) - * Finds out whether a variable is a string. - * Parameters - * $var: The variable being evaluated. - * Return - * TRUE if var is string. False otherwise. - */ -static int jx9Builtin_is_string(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 0; /* Assume false by default */ - if( nArg > 0 ){ - res = jx9_value_is_string(apArg[0]); - } - /* Query result */ - jx9_result_bool(pCtx, res); - return JX9_OK; -} -/* - * bool is_null($var) - * Finds out whether a variable is NULL. - * Parameters - * $var: The variable being evaluated. - * Return - * TRUE if var is NULL. False otherwise. - */ -static int jx9Builtin_is_null(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 0; /* Assume false by default */ - if( nArg > 0 ){ - res = jx9_value_is_null(apArg[0]); - } - /* Query result */ - jx9_result_bool(pCtx, res); - return JX9_OK; -} -/* - * bool is_numeric($var) - * Find out whether a variable is NULL. - * Parameters - * $var: The variable being evaluated. - * Return - * True if var is numeric. False otherwise. - */ -static int jx9Builtin_is_numeric(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 0; /* Assume false by default */ - if( nArg > 0 ){ - res = jx9_value_is_numeric(apArg[0]); - } - /* Query result */ - jx9_result_bool(pCtx, res); - return JX9_OK; -} -/* - * bool is_scalar($var) - * Find out whether a variable is a scalar. - * Parameters - * $var: The variable being evaluated. - * Return - * True if var is scalar. False otherwise. - */ -static int jx9Builtin_is_scalar(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 0; /* Assume false by default */ - if( nArg > 0 ){ - res = jx9_value_is_scalar(apArg[0]); - } - /* Query result */ - jx9_result_bool(pCtx, res); - return JX9_OK; -} -/* - * bool is_array($var) - * Find out whether a variable is an array. - * Parameters - * $var: The variable being evaluated. - * Return - * True if var is an array. False otherwise. - */ -static int jx9Builtin_is_array(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 0; /* Assume false by default */ - if( nArg > 0 ){ - res = jx9_value_is_json_array(apArg[0]); - } - /* Query result */ - jx9_result_bool(pCtx, res); - return JX9_OK; -} -/* - * bool is_object($var) - * Find out whether a variable is an object. - * Parameters - * $var: The variable being evaluated. - * Return - * True if var is an object. False otherwise. - */ -static int jx9Builtin_is_object(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 0; /* Assume false by default */ - if( nArg > 0 ){ - res = jx9_value_is_json_object(apArg[0]); - } - /* Query result */ - jx9_result_bool(pCtx, res); - return JX9_OK; -} -/* - * bool is_resource($var) - * Find out whether a variable is a resource. - * Parameters - * $var: The variable being evaluated. - * Return - * True if a resource. False otherwise. - */ -static int jx9Builtin_is_resource(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 0; /* Assume false by default */ - if( nArg > 0 ){ - res = jx9_value_is_resource(apArg[0]); - } - jx9_result_bool(pCtx, res); - return JX9_OK; -} -/* - * float floatval($var) - * Get float value of a variable. - * Parameter - * $var: The variable being processed. - * Return - * the float value of a variable. - */ -static int jx9Builtin_floatval(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 ){ - /* return 0.0 */ - jx9_result_double(pCtx, 0); - }else{ - double dval; - /* Perform the cast */ - dval = jx9_value_to_double(apArg[0]); - jx9_result_double(pCtx, dval); - } - return JX9_OK; -} -/* - * int intval($var) - * Get integer value of a variable. - * Parameter - * $var: The variable being processed. - * Return - * the int value of a variable. - */ -static int jx9Builtin_intval(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 ){ - /* return 0 */ - jx9_result_int(pCtx, 0); - }else{ - sxi64 iVal; - /* Perform the cast */ - iVal = jx9_value_to_int64(apArg[0]); - jx9_result_int64(pCtx, iVal); - } - return JX9_OK; -} -/* - * string strval($var) - * Get the string representation of a variable. - * Parameter - * $var: The variable being processed. - * Return - * the string value of a variable. - */ -static int jx9Builtin_strval(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 ){ - /* return NULL */ - jx9_result_null(pCtx); - }else{ - const char *zVal; - int iLen = 0; /* cc -O6 warning */ - /* Perform the cast */ - zVal = jx9_value_to_string(apArg[0], &iLen); - jx9_result_string(pCtx, zVal, iLen); - } - return JX9_OK; -} -/* - * bool empty($var) - * Determine whether a variable is empty. - * Parameters - * $var: The variable being checked. - * Return - * 0 if var has a non-empty and non-zero value.1 otherwise. - */ -static int jx9Builtin_empty(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int res = 1; /* Assume empty by default */ - if( nArg > 0 ){ - res = jx9_value_is_empty(apArg[0]); - } - jx9_result_bool(pCtx, res); - return JX9_OK; - -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -#ifdef JX9_ENABLE_MATH_FUNC -/* - * Section: - * Math Functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -#include /* abs */ -#include -/* - * float sqrt(float $arg ) - * Square root of the given number. - * Parameter - * The number to process. - * Return - * The square root of arg or the special value Nan of failure. - */ -static int jx9Builtin_sqrt(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = sqrt(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float exp(float $arg ) - * Calculates the exponent of e. - * Parameter - * The number to process. - * Return - * 'e' raised to the power of arg. - */ -static int jx9Builtin_exp(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = exp(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float floor(float $arg ) - * Round fractions down. - * Parameter - * The number to process. - * Return - * Returns the next lowest integer value by rounding down value if necessary. - */ -static int jx9Builtin_floor(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = floor(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float cos(float $arg ) - * Cosine. - * Parameter - * The number to process. - * Return - * The cosine of arg. - */ -static int jx9Builtin_cos(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = cos(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float acos(float $arg ) - * Arc cosine. - * Parameter - * The number to process. - * Return - * The arc cosine of arg. - */ -static int jx9Builtin_acos(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = acos(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float cosh(float $arg ) - * Hyperbolic cosine. - * Parameter - * The number to process. - * Return - * The hyperbolic cosine of arg. - */ -static int jx9Builtin_cosh(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = cosh(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float sin(float $arg ) - * Sine. - * Parameter - * The number to process. - * Return - * The sine of arg. - */ -static int jx9Builtin_sin(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = sin(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float asin(float $arg ) - * Arc sine. - * Parameter - * The number to process. - * Return - * The arc sine of arg. - */ -static int jx9Builtin_asin(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = asin(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float sinh(float $arg ) - * Hyperbolic sine. - * Parameter - * The number to process. - * Return - * The hyperbolic sine of arg. - */ -static int jx9Builtin_sinh(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = sinh(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float ceil(float $arg ) - * Round fractions up. - * Parameter - * The number to process. - * Return - * The next highest integer value by rounding up value if necessary. - */ -static int jx9Builtin_ceil(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = ceil(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float tan(float $arg ) - * Tangent. - * Parameter - * The number to process. - * Return - * The tangent of arg. - */ -static int jx9Builtin_tan(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = tan(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float atan(float $arg ) - * Arc tangent. - * Parameter - * The number to process. - * Return - * The arc tangent of arg. - */ -static int jx9Builtin_atan(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = atan(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float tanh(float $arg ) - * Hyperbolic tangent. - * Parameter - * The number to process. - * Return - * The Hyperbolic tangent of arg. - */ -static int jx9Builtin_tanh(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = tanh(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float atan2(float $y, float $x) - * Arc tangent of two variable. - * Parameter - * $y = Dividend parameter. - * $x = Divisor parameter. - * Return - * The arc tangent of y/x in radian. - */ -static int jx9Builtin_atan2(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x, y; - if( nArg < 2 ){ - /* Missing arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - y = jx9_value_to_double(apArg[0]); - x = jx9_value_to_double(apArg[1]); - /* Perform the requested operation */ - r = atan2(y, x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float/int64 abs(float/int64 $arg ) - * Absolute value. - * Parameter - * The number to process. - * Return - * The absolute value of number. - */ -static int jx9Builtin_abs(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int is_float; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - is_float = jx9_value_is_float(apArg[0]); - if( is_float ){ - double r, x; - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = fabs(x); - jx9_result_double(pCtx, r); - }else{ - int r, x; - x = jx9_value_to_int(apArg[0]); - /* Perform the requested operation */ - r = abs(x); - jx9_result_int(pCtx, r); - } - return JX9_OK; -} -/* - * float log(float $arg, [int/float $base]) - * Natural logarithm. - * Parameter - * $arg: The number to process. - * $base: The optional logarithmic base to use. (only base-10 is supported) - * Return - * The logarithm of arg to base, if given, or the natural logarithm. - * Note: - * only Natural log and base-10 log are supported. - */ -static int jx9Builtin_log(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - if( nArg == 2 && jx9_value_is_numeric(apArg[1]) && jx9_value_to_int(apArg[1]) == 10 ){ - /* Base-10 log */ - r = log10(x); - }else{ - r = log(x); - } - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float log10(float $arg ) - * Base-10 logarithm. - * Parameter - * The number to process. - * Return - * The Base-10 logarithm of the given number. - */ -static int jx9Builtin_log10(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - /* Perform the requested operation */ - r = log10(x); - /* store the result back */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * number pow(number $base, number $exp) - * Exponential expression. - * Parameter - * base - * The base to use. - * exp - * The exponent. - * Return - * base raised to the power of exp. - * If the result can be represented as integer it will be returned - * as type integer, else it will be returned as type float. - */ -static int jx9Builtin_pow(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double r, x, y; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - x = jx9_value_to_double(apArg[0]); - y = jx9_value_to_double(apArg[1]); - /* Perform the requested operation */ - r = pow(x, y); - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float pi(void) - * Returns an approximation of pi. - * Note - * you can use the M_PI constant which yields identical results to pi(). - * Return - * The value of pi as float. - */ -static int jx9Builtin_pi(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - jx9_result_double(pCtx, JX9_PI); - return JX9_OK; -} -/* - * float fmod(float $x, float $y) - * Returns the floating point remainder (modulo) of the division of the arguments. - * Parameters - * $x - * The dividend - * $y - * The divisor - * Return - * The floating point remainder of x/y. - */ -static int jx9Builtin_fmod(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double x, y, r; - if( nArg < 2 ){ - /* Missing arguments */ - jx9_result_double(pCtx, 0); - return JX9_OK; - } - /* Extract given arguments */ - x = jx9_value_to_double(apArg[0]); - y = jx9_value_to_double(apArg[1]); - /* Perform the requested operation */ - r = fmod(x, y); - /* Processing result */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -/* - * float hypot(float $x, float $y) - * Calculate the length of the hypotenuse of a right-angle triangle . - * Parameters - * $x - * Length of first side - * $y - * Length of first side - * Return - * Calculated length of the hypotenuse. - */ -static int jx9Builtin_hypot(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - double x, y, r; - if( nArg < 2 ){ - /* Missing arguments */ - jx9_result_double(pCtx, 0); - return JX9_OK; - } - /* Extract given arguments */ - x = jx9_value_to_double(apArg[0]); - y = jx9_value_to_double(apArg[1]); - /* Perform the requested operation */ - r = hypot(x, y); - /* Processing result */ - jx9_result_double(pCtx, r); - return JX9_OK; -} -#endif /* JX9_ENABLE_MATH_FUNC */ -/* - * float round ( float $val [, int $precision = 0 [, int $mode = JX9_ROUND_HALF_UP ]] ) - * Exponential expression. - * Parameter - * $val - * The value to round. - * $precision - * The optional number of decimal digits to round to. - * $mode - * One of JX9_ROUND_HALF_UP, JX9_ROUND_HALF_DOWN, JX9_ROUND_HALF_EVEN, or JX9_ROUND_HALF_ODD. - * (not supported). - * Return - * The rounded value. - */ -static int jx9Builtin_round(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int n = 0; - double r; - if( nArg < 1 ){ - /* Missing argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Extract the precision if available */ - if( nArg > 1 ){ - n = jx9_value_to_int(apArg[1]); - if( n>30 ){ - n = 30; - } - if( n<0 ){ - n = 0; - } - } - r = jx9_value_to_double(apArg[0]); - /* If Y==0 and X will fit in a 64-bit int, - * handle the rounding directly.Otherwise - * use our own cutsom printf [i.e:SyBufferFormat()]. - */ - if( n==0 && r>=0 && r= 0xc0 ){ - /* UTF-8 stream */ - zString++; - while( zString < zEnd && (((unsigned char)zString[0] & 0xc0) == 0x80) ){ - zString++; - } - }else{ - if( SyisHex(zString[0]) ){ - break; - } - /* Ignore */ - zString++; - } - } - if( zString < zEnd ){ - /* Cast */ - SyHexStrToInt64(zString, (sxu32)(zEnd-zString), (void *)&iVal, 0); - } - }else{ - /* Extract as a 64-bit integer */ - iVal = jx9_value_to_int64(apArg[0]); - } - /* Return the number */ - jx9_result_int64(pCtx, iVal); - return JX9_OK; -} -/* - * int64 bindec(string $bin_string) - * Binary to decimal. - * Parameters - * $bin_string - * The binary string to convert - * Return - * Returns the decimal equivalent of the binary number represented by the binary_string argument. - */ -static int jx9Builtin_bindec(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString; - jx9_int64 iVal; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return -1 */ - jx9_result_int(pCtx, -1); - return JX9_OK; - } - iVal = 0; - if( jx9_value_is_string(apArg[0]) ){ - /* Extract the given string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nLen > 0 ){ - /* Perform a binary cast */ - SyBinaryStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0); - } - }else{ - /* Extract as a 64-bit integer */ - iVal = jx9_value_to_int64(apArg[0]); - } - /* Return the number */ - jx9_result_int64(pCtx, iVal); - return JX9_OK; -} -/* - * int64 octdec(string $oct_string) - * Octal to decimal. - * Parameters - * $oct_string - * The octal string to convert - * Return - * Returns the decimal equivalent of the octal number represented by the octal_string argument. - */ -static int jx9Builtin_octdec(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString; - jx9_int64 iVal; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return -1 */ - jx9_result_int(pCtx, -1); - return JX9_OK; - } - iVal = 0; - if( jx9_value_is_string(apArg[0]) ){ - /* Extract the given string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nLen > 0 ){ - /* Perform the cast */ - SyOctalStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0); - } - }else{ - /* Extract as a 64-bit integer */ - iVal = jx9_value_to_int64(apArg[0]); - } - /* Return the number */ - jx9_result_int64(pCtx, iVal); - return JX9_OK; -} -/* - * string base_convert(string $number, int $frombase, int $tobase) - * Convert a number between arbitrary bases. - * Parameters - * $number - * The number to convert - * $frombase - * The base number is in - * $tobase - * The base to convert number to - * Return - * Number converted to base tobase - */ -static int jx9Builtin_base_convert(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int nLen, iFbase, iTobase; - const char *zNum; - jx9_int64 iNum; - if( nArg < 3 ){ - /* Return the empty string*/ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Base numbers */ - iFbase = jx9_value_to_int(apArg[1]); - iTobase = jx9_value_to_int(apArg[2]); - if( jx9_value_is_string(apArg[0]) ){ - /* Extract the target number */ - zNum = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Return the empty string*/ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Base conversion */ - switch(iFbase){ - case 16: - /* Hex */ - SyHexStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0); - break; - case 8: - /* Octal */ - SyOctalStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0); - break; - case 2: - /* Binary */ - SyBinaryStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0); - break; - default: - /* Decimal */ - SyStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0); - break; - } - }else{ - iNum = jx9_value_to_int64(apArg[0]); - } - switch(iTobase){ - case 16: - /* Hex */ - jx9_result_string_format(pCtx, "%qx", iNum); /* Quad hex */ - break; - case 8: - /* Octal */ - jx9_result_string_format(pCtx, "%qo", iNum); /* Quad octal */ - break; - case 2: - /* Binary */ - jx9_result_string_format(pCtx, "%qB", iNum); /* Quad binary */ - break; - default: - /* Decimal */ - jx9_result_string_format(pCtx, "%qd", iNum); /* Quad decimal */ - break; - } - return JX9_OK; -} -/* - * Section: - * String handling Functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * string substr(string $string, int $start[, int $length ]) - * Return part of a string. - * Parameters - * $string - * The input string. Must be one character or longer. - * $start - * If start is non-negative, the returned string will start at the start'th position - * in string, counting from zero. For instance, in the string 'abcdef', the character - * at position 0 is 'a', the character at position 2 is 'c', and so forth. - * If start is negative, the returned string will start at the start'th character - * from the end of string. - * If string is less than or equal to start characters long, FALSE will be returned. - * $length - * If length is given and is positive, the string returned will contain at most length - * characters beginning from start (depending on the length of string). - * If length is given and is negative, then that many characters will be omitted from - * the end of string (after the start position has been calculated when a start is negative). - * If start denotes the position of this truncation or beyond, false will be returned. - * If length is given and is 0, FALSE or NULL an empty string will be returned. - * If length is omitted, the substring starting from start until the end of the string - * will be returned. - * Return - * Returns the extracted part of string, or FALSE on failure or an empty string. - */ -static int jx9Builtin_substr(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zSource, *zOfft; - int nOfft, nLen, nSrcLen; - if( nArg < 2 ){ - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zSource = jx9_value_to_string(apArg[0], &nSrcLen); - if( nSrcLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - nLen = nSrcLen; /* cc warning */ - /* Extract the offset */ - nOfft = jx9_value_to_int(apArg[1]); - if( nOfft < 0 ){ - zOfft = &zSource[nSrcLen+nOfft]; - if( zOfft < zSource ){ - /* Invalid offset */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - nLen = (int)(&zSource[nSrcLen]-zOfft); - nOfft = (int)(zOfft-zSource); - }else if( nOfft >= nSrcLen ){ - /* Invalid offset */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - }else{ - zOfft = &zSource[nOfft]; - nLen = nSrcLen - nOfft; - } - if( nArg > 2 ){ - /* Extract the length */ - nLen = jx9_value_to_int(apArg[2]); - if( nLen == 0 ){ - /* Invalid length, return an empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - }else if( nLen < 0 ){ - nLen = nSrcLen + nLen - nOfft; - if( nLen < 1 ){ - /* Invalid length */ - nLen = nSrcLen - nOfft; - } - } - if( nLen + nOfft > nSrcLen ){ - /* Invalid length */ - nLen = nSrcLen - nOfft; - } - } - /* Return the substring */ - jx9_result_string(pCtx, zOfft, nLen); - return JX9_OK; -} -/* - * int substr_compare(string $main_str, string $str , int $offset[, int $length[, bool $case_insensitivity = false ]]) - * Binary safe comparison of two strings from an offset, up to length characters. - * Parameters - * $main_str - * The main string being compared. - * $str - * The secondary string being compared. - * $offset - * The start position for the comparison. If negative, it starts counting from - * the end of the string. - * $length - * The length of the comparison. The default value is the largest of the length - * of the str compared to the length of main_str less the offset. - * $case_insensitivity - * If case_insensitivity is TRUE, comparison is case insensitive. - * Return - * Returns < 0 if main_str from position offset is less than str, > 0 if it is greater than - * str, and 0 if they are equal. If offset is equal to or greater than the length of main_str - * or length is set and is less than 1, substr_compare() prints a warning and returns FALSE. - */ -static int jx9Builtin_substr_compare(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zSource, *zOfft, *zSub; - int nOfft, nLen, nSrcLen, nSublen; - int iCase = 0; - int rc; - if( nArg < 3 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zSource = jx9_value_to_string(apArg[0], &nSrcLen); - if( nSrcLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - nLen = nSrcLen; /* cc warning */ - /* Extract the substring */ - zSub = jx9_value_to_string(apArg[1], &nSublen); - if( nSublen < 1 || nSublen > nSrcLen){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the offset */ - nOfft = jx9_value_to_int(apArg[2]); - if( nOfft < 0 ){ - zOfft = &zSource[nSrcLen+nOfft]; - if( zOfft < zSource ){ - /* Invalid offset */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - nLen = (int)(&zSource[nSrcLen]-zOfft); - nOfft = (int)(zOfft-zSource); - }else if( nOfft >= nSrcLen ){ - /* Invalid offset */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - }else{ - zOfft = &zSource[nOfft]; - nLen = nSrcLen - nOfft; - } - if( nArg > 3 ){ - /* Extract the length */ - nLen = jx9_value_to_int(apArg[3]); - if( nLen < 1 ){ - /* Invalid length */ - jx9_result_int(pCtx, 1); - return JX9_OK; - }else if( nLen + nOfft > nSrcLen ){ - /* Invalid length */ - nLen = nSrcLen - nOfft; - } - if( nArg > 4 ){ - /* Case-sensitive or not */ - iCase = jx9_value_to_bool(apArg[4]); - } - } - /* Perform the comparison */ - if( iCase ){ - rc = SyStrnicmp(zOfft, zSub, (sxu32)nLen); - }else{ - rc = SyStrncmp(zOfft, zSub, (sxu32)nLen); - } - /* Comparison result */ - jx9_result_int(pCtx, rc); - return JX9_OK; -} -/* - * int substr_count(string $haystack, string $needle[, int $offset = 0 [, int $length ]]) - * Count the number of substring occurrences. - * Parameters - * $haystack - * The string to search in - * $needle - * The substring to search for - * $offset - * The offset where to start counting - * $length (NOT USED) - * The maximum length after the specified offset to search for the substring. - * It outputs a warning if the offset plus the length is greater than the haystack length. - * Return - * Toral number of substring occurrences. - */ -static int jx9Builtin_substr_count(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zText, *zPattern, *zEnd; - int nTextlen, nPatlen; - int iCount = 0; - sxu32 nOfft; - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Point to the haystack */ - zText = jx9_value_to_string(apArg[0], &nTextlen); - /* Point to the neddle */ - zPattern = jx9_value_to_string(apArg[1], &nPatlen); - if( nTextlen < 1 || nPatlen < 1 || nPatlen > nTextlen ){ - /* NOOP, return zero */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - if( nArg > 2 ){ - int nOfft; - /* Extract the offset */ - nOfft = jx9_value_to_int(apArg[2]); - if( nOfft < 0 || nOfft > nTextlen ){ - /* Invalid offset, return zero */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Point to the desired offset */ - zText = &zText[nOfft]; - /* Adjust length */ - nTextlen -= nOfft; - } - /* Point to the end of the string */ - zEnd = &zText[nTextlen]; - if( nArg > 3 ){ - int nLen; - /* Extract the length */ - nLen = jx9_value_to_int(apArg[3]); - if( nLen < 0 || nLen > nTextlen ){ - /* Invalid length, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Adjust pointer */ - nTextlen = nLen; - zEnd = &zText[nTextlen]; - } - /* Perform the search */ - for(;;){ - rc = SyBlobSearch((const void *)zText, (sxu32)(zEnd-zText), (const void *)zPattern, nPatlen, &nOfft); - if( rc != SXRET_OK ){ - /* Pattern not found, break immediately */ - break; - } - /* Increment counter and update the offset */ - iCount++; - zText += nOfft + nPatlen; - if( zText >= zEnd ){ - break; - } - } - /* Pattern count */ - jx9_result_int(pCtx, iCount); - return JX9_OK; -} -/* - * string chunk_split(string $body[, int $chunklen = 76 [, string $end = "\r\n" ]]) - * Split a string into smaller chunks. - * Parameters - * $body - * The string to be chunked. - * $chunklen - * The chunk length. - * $end - * The line ending sequence. - * Return - * The chunked string or NULL on failure. - */ -static int jx9Builtin_chunk_split(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn, *zEnd, *zSep = "\r\n"; - int nSepLen, nChunkLen, nLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Nothing to split, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* initialize/Extract arguments */ - nSepLen = (int)sizeof("\r\n") - 1; - nChunkLen = 76; - zIn = jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nArg > 1 ){ - /* Chunk length */ - nChunkLen = jx9_value_to_int(apArg[1]); - if( nChunkLen < 1 ){ - /* Switch back to the default length */ - nChunkLen = 76; - } - if( nArg > 2 ){ - /* Separator */ - zSep = jx9_value_to_string(apArg[2], &nSepLen); - if( nSepLen < 1 ){ - /* Switch back to the default separator */ - zSep = "\r\n"; - nSepLen = (int)sizeof("\r\n") - 1; - } - } - } - /* Perform the requested operation */ - if( nChunkLen > nLen ){ - /* Nothing to split, return the string and the separator */ - jx9_result_string_format(pCtx, "%.*s%.*s", nLen, zIn, nSepLen, zSep); - return JX9_OK; - } - while( zIn < zEnd ){ - if( nChunkLen > (int)(zEnd-zIn) ){ - nChunkLen = (int)(zEnd - zIn); - } - /* Append the chunk and the separator */ - jx9_result_string_format(pCtx, "%.*s%.*s", nChunkLen, zIn, nSepLen, zSep); - /* Point beyond the chunk */ - zIn += nChunkLen; - } - return JX9_OK; -} -/* - * string htmlspecialchars(string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $charset]]) - * HTML escaping of special characters. - * The translations performed are: - * '&' (ampersand) ==> '&' - * '"' (double quote) ==> '"' when ENT_NOQUOTES is not set. - * "'" (single quote) ==> ''' only when ENT_QUOTES is set. - * '<' (less than) ==> '<' - * '>' (greater than) ==> '>' - * Parameters - * $string - * The string being converted. - * $flags - * A bitmask of one or more of the following flags, which specify how to handle quotes. - * The default is ENT_COMPAT | ENT_HTML401. - * ENT_COMPAT Will convert double-quotes and leave single-quotes alone. - * ENT_QUOTES Will convert both double and single quotes. - * ENT_NOQUOTES Will leave both double and single quotes unconverted. - * ENT_IGNORE Silently discard invalid code unit sequences instead of returning an empty string. - * $charset - * Defines character set used in conversion. The default character set is ISO-8859-1. (Not used) - * Return - * The escaped string or NULL on failure. - */ -static int jx9Builtin_htmlspecialchars(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zCur, *zIn, *zEnd; - int iFlags = 0x01|0x40; /* ENT_COMPAT | ENT_HTML401 */ - int nLen, c; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - /* Extract the flags if available */ - if( nArg > 1 ){ - iFlags = jx9_value_to_int(apArg[1]); - if( iFlags < 0 ){ - iFlags = 0x01|0x40; - } - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - break; - } - zCur = zIn; - while( zIn < zEnd && zIn[0] != '&' && zIn[0] != '\'' && zIn[0] != '"' && zIn[0] != '<' && zIn[0] != '>' ){ - zIn++; - } - if( zCur < zIn ){ - /* Append the raw string verbatim */ - jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); - } - if( zIn >= zEnd ){ - break; - } - c = zIn[0]; - if( c == '&' ){ - /* Expand '&' */ - jx9_result_string(pCtx, "&", (int)sizeof("&")-1); - }else if( c == '<' ){ - /* Expand '<' */ - jx9_result_string(pCtx, "<", (int)sizeof("<")-1); - }else if( c == '>' ){ - /* Expand '>' */ - jx9_result_string(pCtx, ">", (int)sizeof(">")-1); - }else if( c == '\'' ){ - if( iFlags & 0x02 /*ENT_QUOTES*/ ){ - /* Expand ''' */ - jx9_result_string(pCtx, "'", (int)sizeof("'")-1); - }else{ - /* Leave the single quote untouched */ - jx9_result_string(pCtx, "'", (int)sizeof(char)); - } - }else if( c == '"' ){ - if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){ - /* Expand '"' */ - jx9_result_string(pCtx, """, (int)sizeof(""")-1); - }else{ - /* Leave the double quote untouched */ - jx9_result_string(pCtx, "\"", (int)sizeof(char)); - } - } - /* Ignore the unsafe HTML character */ - zIn++; - } - return JX9_OK; -} -/* - * string htmlspecialchars_decode(string $string[, int $quote_style = ENT_COMPAT ]) - * Unescape HTML entities. - * Parameters - * $string - * The string to decode - * $quote_style - * The quote style. One of the following constants: - * ENT_COMPAT Will convert double-quotes and leave single-quotes alone (default) - * ENT_QUOTES Will convert both double and single quotes - * ENT_NOQUOTES Will leave both double and single quotes unconverted - * Return - * The unescaped string or NULL on failure. - */ -static int jx9Builtin_htmlspecialchars_decode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zCur, *zIn, *zEnd; - int iFlags = 0x01; /* ENT_COMPAT */ - int nLen, nJump; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - /* Extract the flags if available */ - if( nArg > 1 ){ - iFlags = jx9_value_to_int(apArg[1]); - if( iFlags < 0 ){ - iFlags = 0x01; - } - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - break; - } - zCur = zIn; - while( zIn < zEnd && zIn[0] != '&' ){ - zIn++; - } - if( zCur < zIn ){ - /* Append the raw string verbatim */ - jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); - } - nLen = (int)(zEnd-zIn); - nJump = (int)sizeof(char); - if( nLen >= (int)sizeof("&")-1 && SyStrnicmp(zIn, "&", sizeof("&")-1) == 0 ){ - /* & ==> '&' */ - jx9_result_string(pCtx, "&", (int)sizeof(char)); - nJump = (int)sizeof("&")-1; - }else if( nLen >= (int)sizeof("<")-1 && SyStrnicmp(zIn, "<", sizeof("<")-1) == 0 ){ - /* < ==> < */ - jx9_result_string(pCtx, "<", (int)sizeof(char)); - nJump = (int)sizeof("<")-1; - }else if( nLen >= (int)sizeof(">")-1 && SyStrnicmp(zIn, ">", sizeof(">")-1) == 0 ){ - /* > ==> '>' */ - jx9_result_string(pCtx, ">", (int)sizeof(char)); - nJump = (int)sizeof(">")-1; - }else if( nLen >= (int)sizeof(""")-1 && SyStrnicmp(zIn, """, sizeof(""")-1) == 0 ){ - /* " ==> '"' */ - if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){ - jx9_result_string(pCtx, "\"", (int)sizeof(char)); - }else{ - /* Leave untouched */ - jx9_result_string(pCtx, """, (int)sizeof(""")-1); - } - nJump = (int)sizeof(""")-1; - }else if( nLen >= (int)sizeof("'")-1 && SyStrnicmp(zIn, "'", sizeof("'")-1) == 0 ){ - /* ' ==> ''' */ - if( iFlags & 0x02 /*ENT_QUOTES*/ ){ - /* Expand ''' */ - jx9_result_string(pCtx, "'", (int)sizeof(char)); - }else{ - /* Leave untouched */ - jx9_result_string(pCtx, "'", (int)sizeof("'")-1); - } - nJump = (int)sizeof("'")-1; - }else if( nLen >= (int)sizeof(char) ){ - /* expand '&' */ - jx9_result_string(pCtx, "&", (int)sizeof(char)); - }else{ - /* No more input to process */ - break; - } - zIn += nJump; - } - return JX9_OK; -} -/* HTML encoding/Decoding table - * Source: Symisc RunTime API.[chm@symisc.net] - */ -static const char *azHtmlEscape[] = { - "<", "<", ">", ">", "&", "&", """, "\"", "'", "'", - "!", "!", "$", "$", "#", "#", "%", "%", "(", "(", - ")", ")", "{", "{", "}", "}", "=", "=", "+", "+", - "?", "?", "[", "[", "]", "]", "@", "@", ",", "," - }; -/* - * array get_html_translation_table(void) - * Returns the translation table used by htmlspecialchars() and htmlentities(). - * Parameters - * None - * Return - * The translation table as an array or NULL on failure. - */ -static int jx9Builtin_get_html_translation_table(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pArray, *pValue; - sxu32 n; - /* Element value */ - pValue = jx9_context_new_scalar(pCtx); - if( pValue == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Make the table */ - for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){ - /* Prepare the value */ - jx9_value_string(pValue, azHtmlEscape[n], -1 /* Compute length automatically */); - /* Insert the value */ - jx9_array_add_strkey_elem(pArray, azHtmlEscape[n+1], pValue); - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - } - /* - * Return the array. - * Don't worry about freeing memory, everything will be automatically - * released upon we return from this function. - */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * string htmlentities( string $string [, int $flags = ENT_COMPAT | ENT_HTML401]); - * Convert all applicable characters to HTML entities - * Parameters - * $string - * The input string. - * $flags - * A bitmask of one or more of the flags (see block-comment on jx9Builtin_htmlspecialchars()) - * Return - * The encoded string. - */ -static int jx9Builtin_htmlentities(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int iFlags = 0x01; /* ENT_COMPAT */ - const char *zIn, *zEnd; - int nLen, c; - sxu32 n; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - /* Extract the flags if available */ - if( nArg > 1 ){ - iFlags = jx9_value_to_int(apArg[1]); - if( iFlags < 0 ){ - iFlags = 0x01; - } - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* No more input to process */ - break; - } - c = zIn[0]; - /* Perform a linear lookup on the decoding table */ - for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){ - if( azHtmlEscape[n+1][0] == c ){ - /* Got one */ - break; - } - } - if( n < SX_ARRAYSIZE(azHtmlEscape) ){ - /* Output the safe sequence [i.e: '<' ==> '<"] */ - if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){ - /* Expand the double quote verbatim */ - jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - }else if(c == '\'' && ((iFlags & 0x02 /*ENT_QUOTES*/) == 0 || (iFlags & 0x04) /*ENT_NOQUOTES*/) ){ - /* expand single quote verbatim */ - jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - }else{ - jx9_result_string(pCtx, azHtmlEscape[n], -1/*Compute length automatically */); - } - }else{ - /* Output character verbatim */ - jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - } - zIn++; - } - return JX9_OK; -} -/* - * string html_entity_decode(string $string [, int $quote_style = ENT_COMPAT [, string $charset = 'UTF-8' ]]) - * Perform the reverse operation of html_entity_decode(). - * Parameters - * $string - * The input string. - * $flags - * A bitmask of one or more of the flags (see comment on jx9Builtin_htmlspecialchars()) - * Return - * The decoded string. - */ -static int jx9Builtin_html_entity_decode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zCur, *zIn, *zEnd; - int iFlags = 0x01; /* ENT_COMPAT */ - int nLen; - sxu32 n; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - /* Extract the flags if available */ - if( nArg > 1 ){ - iFlags = jx9_value_to_int(apArg[1]); - if( iFlags < 0 ){ - iFlags = 0x01; - } - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* No more input to process */ - break; - } - zCur = zIn; - while( zIn < zEnd && zIn[0] != '&' ){ - zIn++; - } - if( zCur < zIn ){ - /* Append raw string verbatim */ - jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); - } - if( zIn >= zEnd ){ - break; - } - nLen = (int)(zEnd-zIn); - /* Find an encoded sequence */ - for(n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){ - int iLen = (int)SyStrlen(azHtmlEscape[n]); - if( nLen >= iLen && SyStrnicmp(zIn, azHtmlEscape[n], (sxu32)iLen) == 0 ){ - /* Got one */ - zIn += iLen; - break; - } - } - if( n < SX_ARRAYSIZE(azHtmlEscape) ){ - int c = azHtmlEscape[n+1][0]; - /* Output the decoded character */ - if( c == '\'' && ((iFlags & 0x02) == 0 /*ENT_QUOTES*/|| (iFlags & 0x04) /*ENT_NOQUOTES*/) ){ - /* Do not process single quotes */ - jx9_result_string(pCtx, azHtmlEscape[n], -1); - }else if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){ - /* Do not process double quotes */ - jx9_result_string(pCtx, azHtmlEscape[n], -1); - }else{ - jx9_result_string(pCtx, azHtmlEscape[n+1], -1); /* Compute length automatically */ - } - }else{ - /* Append '&' */ - jx9_result_string(pCtx, "&", (int)sizeof(char)); - zIn++; - } - } - return JX9_OK; -} -/* - * int strlen($string) - * return the length of the given string. - * Parameter - * string: The string being measured for length. - * Return - * length of the given string. - */ -static int jx9Builtin_strlen(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int iLen = 0; - if( nArg > 0 ){ - jx9_value_to_string(apArg[0], &iLen); - } - /* String length */ - jx9_result_int(pCtx, iLen); - return JX9_OK; -} -/* - * int strcmp(string $str1, string $str2) - * Perform a binary safe string comparison. - * Parameter - * str1: The first string - * str2: The second string - * Return - * Returns < 0 if str1 is less than str2; > 0 if str1 is greater - * than str2, and 0 if they are equal. - */ -static int jx9Builtin_strcmp(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *z1, *z2; - int n1, n2; - int res; - if( nArg < 2 ){ - res = nArg == 0 ? 0 : 1; - jx9_result_int(pCtx, res); - return JX9_OK; - } - /* Perform the comparison */ - z1 = jx9_value_to_string(apArg[0], &n1); - z2 = jx9_value_to_string(apArg[1], &n2); - res = SyStrncmp(z1, z2, (sxu32)(SXMAX(n1, n2))); - /* Comparison result */ - jx9_result_int(pCtx, res); - return JX9_OK; -} -/* - * int strncmp(string $str1, string $str2, int n) - * Perform a binary safe string comparison of the first n characters. - * Parameter - * str1: The first string - * str2: The second string - * Return - * Returns < 0 if str1 is less than str2; > 0 if str1 is greater - * than str2, and 0 if they are equal. - */ -static int jx9Builtin_strncmp(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *z1, *z2; - int res; - int n; - if( nArg < 3 ){ - /* Perform a standard comparison */ - return jx9Builtin_strcmp(pCtx, nArg, apArg); - } - /* Desired comparison length */ - n = jx9_value_to_int(apArg[2]); - if( n < 0 ){ - /* Invalid length */ - jx9_result_int(pCtx, -1); - return JX9_OK; - } - /* Perform the comparison */ - z1 = jx9_value_to_string(apArg[0], 0); - z2 = jx9_value_to_string(apArg[1], 0); - res = SyStrncmp(z1, z2, (sxu32)n); - /* Comparison result */ - jx9_result_int(pCtx, res); - return JX9_OK; -} -/* - * int strcasecmp(string $str1, string $str2, int n) - * Perform a binary safe case-insensitive string comparison. - * Parameter - * str1: The first string - * str2: The second string - * Return - * Returns < 0 if str1 is less than str2; > 0 if str1 is greater - * than str2, and 0 if they are equal. - */ -static int jx9Builtin_strcasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *z1, *z2; - int n1, n2; - int res; - if( nArg < 2 ){ - res = nArg == 0 ? 0 : 1; - jx9_result_int(pCtx, res); - return JX9_OK; - } - /* Perform the comparison */ - z1 = jx9_value_to_string(apArg[0], &n1); - z2 = jx9_value_to_string(apArg[1], &n2); - res = SyStrnicmp(z1, z2, (sxu32)(SXMAX(n1, n2))); - /* Comparison result */ - jx9_result_int(pCtx, res); - return JX9_OK; -} -/* - * int strncasecmp(string $str1, string $str2, int n) - * Perform a binary safe case-insensitive string comparison of the first n characters. - * Parameter - * $str1: The first string - * $str2: The second string - * $len: The length of strings to be used in the comparison. - * Return - * Returns < 0 if str1 is less than str2; > 0 if str1 is greater - * than str2, and 0 if they are equal. - */ -static int jx9Builtin_strncasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *z1, *z2; - int res; - int n; - if( nArg < 3 ){ - /* Perform a standard comparison */ - return jx9Builtin_strcasecmp(pCtx, nArg, apArg); - } - /* Desired comparison length */ - n = jx9_value_to_int(apArg[2]); - if( n < 0 ){ - /* Invalid length */ - jx9_result_int(pCtx, -1); - return JX9_OK; - } - /* Perform the comparison */ - z1 = jx9_value_to_string(apArg[0], 0); - z2 = jx9_value_to_string(apArg[1], 0); - res = SyStrnicmp(z1, z2, (sxu32)n); - /* Comparison result */ - jx9_result_int(pCtx, res); - return JX9_OK; -} -/* - * Implode context [i.e: it's private data]. - * A pointer to the following structure is forwarded - * verbatim to the array walker callback defined below. - */ -struct implode_data { - jx9_context *pCtx; /* Call context */ - int bRecursive; /* TRUE if recursive implode [this is a symisc eXtension] */ - const char *zSep; /* Arguments separator if any */ - int nSeplen; /* Separator length */ - int bFirst; /* TRUE if first call */ - int nRecCount; /* Recursion count to avoid infinite loop */ -}; -/* - * Implode walker callback for the [jx9_array_walk()] interface. - * The following routine is invoked for each array entry passed - * to the implode() function. - */ -static int implode_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData) -{ - struct implode_data *pData = (struct implode_data *)pUserData; - const char *zData; - int nLen; - if( pData->bRecursive && jx9_value_is_json_array(pValue) && pData->nRecCount < 32 ){ - if( pData->nSeplen > 0 ){ - if( !pData->bFirst ){ - /* append the separator first */ - jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen); - }else{ - pData->bFirst = 0; - } - } - /* Recurse */ - pData->bFirst = 1; - pData->nRecCount++; - jx9HashmapWalk((jx9_hashmap *)pValue->x.pOther, implode_callback, pData); - pData->nRecCount--; - return JX9_OK; - } - /* Extract the string representation of the entry value */ - zData = jx9_value_to_string(pValue, &nLen); - if( nLen > 0 ){ - if( pData->nSeplen > 0 ){ - if( !pData->bFirst ){ - /* append the separator first */ - jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen); - }else{ - pData->bFirst = 0; - } - } - jx9_result_string(pData->pCtx, zData, nLen); - }else{ - SXUNUSED(pKey); /* cc warning */ - } - return JX9_OK; -} -/* - * string implode(string $glue, array $pieces, ...) - * string implode(array $pieces, ...) - * Join array elements with a string. - * $glue - * Defaults to an empty string. This is not the preferred usage of implode() as glue - * would be the second parameter and thus, the bad prototype would be used. - * $pieces - * The array of strings to implode. - * Return - * Returns a string containing a string representation of all the array elements in the same - * order, with the glue string between each element. - */ -static int jx9Builtin_implode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - struct implode_data imp_data; - int i = 1; - if( nArg < 1 ){ - /* Missing argument, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Prepare the implode context */ - imp_data.pCtx = pCtx; - imp_data.bRecursive = 0; - imp_data.bFirst = 1; - imp_data.nRecCount = 0; - if( !jx9_value_is_json_array(apArg[0]) ){ - imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen); - }else{ - imp_data.zSep = 0; - imp_data.nSeplen = 0; - i = 0; - } - jx9_result_string(pCtx, "", 0); /* Set an empty stirng */ - /* Start the 'join' process */ - while( i < nArg ){ - if( jx9_value_is_json_array(apArg[i]) ){ - /* Iterate throw array entries */ - jx9_array_walk(apArg[i], implode_callback, &imp_data); - }else{ - const char *zData; - int nLen; - /* Extract the string representation of the jx9 value */ - zData = jx9_value_to_string(apArg[i], &nLen); - if( nLen > 0 ){ - if( imp_data.nSeplen > 0 ){ - if( !imp_data.bFirst ){ - /* append the separator first */ - jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen); - }else{ - imp_data.bFirst = 0; - } - } - jx9_result_string(pCtx, zData, nLen); - } - } - i++; - } - return JX9_OK; -} -/* - * string implode_recursive(string $glue, array $pieces, ...) - * Purpose - * Same as implode() but recurse on arrays. - * Example: - * $a = array('usr', array('home', 'dean')); - * print implode_recursive("/", $a); - * Will output - * usr/home/dean. - * While the standard implode would produce. - * usr/Array. - * Parameter - * Refer to implode(). - * Return - * Refer to implode(). - */ -static int jx9Builtin_implode_recursive(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - struct implode_data imp_data; - int i = 1; - if( nArg < 1 ){ - /* Missing argument, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Prepare the implode context */ - imp_data.pCtx = pCtx; - imp_data.bRecursive = 1; - imp_data.bFirst = 1; - imp_data.nRecCount = 0; - if( !jx9_value_is_json_array(apArg[0]) ){ - imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen); - }else{ - imp_data.zSep = 0; - imp_data.nSeplen = 0; - i = 0; - } - jx9_result_string(pCtx, "", 0); /* Set an empty stirng */ - /* Start the 'join' process */ - while( i < nArg ){ - if( jx9_value_is_json_array(apArg[i]) ){ - /* Iterate throw array entries */ - jx9_array_walk(apArg[i], implode_callback, &imp_data); - }else{ - const char *zData; - int nLen; - /* Extract the string representation of the jx9 value */ - zData = jx9_value_to_string(apArg[i], &nLen); - if( nLen > 0 ){ - if( imp_data.nSeplen > 0 ){ - if( !imp_data.bFirst ){ - /* append the separator first */ - jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen); - }else{ - imp_data.bFirst = 0; - } - } - jx9_result_string(pCtx, zData, nLen); - } - } - i++; - } - return JX9_OK; -} -/* - * array explode(string $delimiter, string $string[, int $limit ]) - * Returns an array of strings, each of which is a substring of string - * formed by splitting it on boundaries formed by the string delimiter. - * Parameters - * $delimiter - * The boundary string. - * $string - * The input string. - * $limit - * If limit is set and positive, the returned array will contain a maximum - * of limit elements with the last element containing the rest of string. - * If the limit parameter is negative, all fields except the last -limit are returned. - * If the limit parameter is zero, then this is treated as 1. - * Returns - * Returns an array of strings created by splitting the string parameter - * on boundaries formed by the delimiter. - * If delimiter is an empty string (""), explode() will return FALSE. - * If delimiter contains a value that is not contained in string and a negative - * limit is used, then an empty array will be returned, otherwise an array containing string - * will be returned. - * NOTE: - * Negative limit is not supported. - */ -static int jx9Builtin_explode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zDelim, *zString, *zCur, *zEnd; - int nDelim, nStrlen, iLimit; - jx9_value *pArray; - jx9_value *pValue; - sxu32 nOfft; - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the delimiter */ - zDelim = jx9_value_to_string(apArg[0], &nDelim); - if( nDelim < 1 ){ - /* Empty delimiter, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the string */ - zString = jx9_value_to_string(apArg[1], &nStrlen); - if( nStrlen < 1 ){ - /* Empty delimiter, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the end of the string */ - zEnd = &zString[nStrlen]; - /* Create the array */ - pArray = jx9_context_new_array(pCtx); - pValue = jx9_context_new_scalar(pCtx); - if( pArray == 0 || pValue == 0 ){ - /* Out of memory, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Set a defualt limit */ - iLimit = SXI32_HIGH; - if( nArg > 2 ){ - iLimit = jx9_value_to_int(apArg[2]); - if( iLimit < 0 ){ - iLimit = -iLimit; - } - if( iLimit == 0 ){ - iLimit = 1; - } - iLimit--; - } - /* Start exploding */ - for(;;){ - if( zString >= zEnd ){ - /* No more entry to process */ - break; - } - rc = SyBlobSearch(zString, (sxu32)(zEnd-zString), zDelim, nDelim, &nOfft); - if( rc != SXRET_OK || iLimit <= (int)jx9_array_count(pArray) ){ - /* Limit reached, insert the rest of the string and break */ - if( zEnd > zString ){ - jx9_value_string(pValue, zString, (int)(zEnd-zString)); - jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue); - } - break; - } - /* Point to the desired offset */ - zCur = &zString[nOfft]; - if( zCur > zString ){ - /* Perform the store operation */ - jx9_value_string(pValue, zString, (int)(zCur-zString)); - jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue); - } - /* Point beyond the delimiter */ - zString = &zCur[nDelim]; - /* Reset the cursor */ - jx9_value_reset_string_cursor(pValue); - } - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - /* NOTE that every allocated jx9_value will be automatically - * released as soon we return from this foregin function. - */ - return JX9_OK; -} -/* - * string trim(string $str[, string $charlist ]) - * Strip whitespace (or other characters) from the beginning and end of a string. - * Parameters - * $str - * The string that will be trimmed. - * $charlist - * Optionally, the stripped characters can also be specified using the charlist parameter. - * Simply list all characters that you want to be stripped. - * With .. you can specify a range of characters. - * Returns. - * Thr processed string. - */ -static int jx9Builtin_trim(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string, return */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Start the trim process */ - if( nArg < 2 ){ - SyString sStr; - /* Remove white spaces and NUL bytes */ - SyStringInitFromBuf(&sStr, zString, nLen); - SyStringFullTrimSafe(&sStr); - jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte); - }else{ - /* Char list */ - const char *zList; - int nListlen; - zList = jx9_value_to_string(apArg[1], &nListlen); - if( nListlen < 1 ){ - /* Return the string unchanged */ - jx9_result_string(pCtx, zString, nLen); - }else{ - const char *zEnd = &zString[nLen]; - const char *zCur = zString; - const char *zPtr; - int i; - /* Left trim */ - for(;;){ - if( zCur >= zEnd ){ - break; - } - zPtr = zCur; - for( i = 0 ; i < nListlen ; i++ ){ - if( zCur < zEnd && zCur[0] == zList[i] ){ - zCur++; - } - } - if( zCur == zPtr ){ - /* No match, break immediately */ - break; - } - } - /* Right trim */ - zEnd--; - for(;;){ - if( zEnd <= zCur ){ - break; - } - zPtr = zEnd; - for( i = 0 ; i < nListlen ; i++ ){ - if( zEnd > zCur && zEnd[0] == zList[i] ){ - zEnd--; - } - } - if( zEnd == zPtr ){ - break; - } - } - if( zCur >= zEnd ){ - /* Return the empty string */ - jx9_result_string(pCtx, "", 0); - }else{ - zEnd++; - jx9_result_string(pCtx, zCur, (int)(zEnd-zCur)); - } - } - } - return JX9_OK; -} -/* - * string rtrim(string $str[, string $charlist ]) - * Strip whitespace (or other characters) from the end of a string. - * Parameters - * $str - * The string that will be trimmed. - * $charlist - * Optionally, the stripped characters can also be specified using the charlist parameter. - * Simply list all characters that you want to be stripped. - * With .. you can specify a range of characters. - * Returns. - * Thr processed string. - */ -static int jx9Builtin_rtrim(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string, return */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Start the trim process */ - if( nArg < 2 ){ - SyString sStr; - /* Remove white spaces and NUL bytes*/ - SyStringInitFromBuf(&sStr, zString, nLen); - SyStringRightTrimSafe(&sStr); - jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte); - }else{ - /* Char list */ - const char *zList; - int nListlen; - zList = jx9_value_to_string(apArg[1], &nListlen); - if( nListlen < 1 ){ - /* Return the string unchanged */ - jx9_result_string(pCtx, zString, nLen); - }else{ - const char *zEnd = &zString[nLen - 1]; - const char *zCur = zString; - const char *zPtr; - int i; - /* Right trim */ - for(;;){ - if( zEnd <= zCur ){ - break; - } - zPtr = zEnd; - for( i = 0 ; i < nListlen ; i++ ){ - if( zEnd > zCur && zEnd[0] == zList[i] ){ - zEnd--; - } - } - if( zEnd == zPtr ){ - break; - } - } - if( zEnd <= zCur ){ - /* Return the empty string */ - jx9_result_string(pCtx, "", 0); - }else{ - zEnd++; - jx9_result_string(pCtx, zCur, (int)(zEnd-zCur)); - } - } - } - return JX9_OK; -} -/* - * string ltrim(string $str[, string $charlist ]) - * Strip whitespace (or other characters) from the beginning and end of a string. - * Parameters - * $str - * The string that will be trimmed. - * $charlist - * Optionally, the stripped characters can also be specified using the charlist parameter. - * Simply list all characters that you want to be stripped. - * With .. you can specify a range of characters. - * Returns. - * The processed string. - */ -static int jx9Builtin_ltrim(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string, return */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Start the trim process */ - if( nArg < 2 ){ - SyString sStr; - /* Remove white spaces and NUL byte */ - SyStringInitFromBuf(&sStr, zString, nLen); - SyStringLeftTrimSafe(&sStr); - jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte); - }else{ - /* Char list */ - const char *zList; - int nListlen; - zList = jx9_value_to_string(apArg[1], &nListlen); - if( nListlen < 1 ){ - /* Return the string unchanged */ - jx9_result_string(pCtx, zString, nLen); - }else{ - const char *zEnd = &zString[nLen]; - const char *zCur = zString; - const char *zPtr; - int i; - /* Left trim */ - for(;;){ - if( zCur >= zEnd ){ - break; - } - zPtr = zCur; - for( i = 0 ; i < nListlen ; i++ ){ - if( zCur < zEnd && zCur[0] == zList[i] ){ - zCur++; - } - } - if( zCur == zPtr ){ - /* No match, break immediately */ - break; - } - } - if( zCur >= zEnd ){ - /* Return the empty string */ - jx9_result_string(pCtx, "", 0); - }else{ - jx9_result_string(pCtx, zCur, (int)(zEnd-zCur)); - } - } - } - return JX9_OK; -} -/* - * string strtolower(string $str) - * Make a string lowercase. - * Parameters - * $str - * The input string. - * Returns. - * The lowercased string. - */ -static int jx9Builtin_strtolower(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString, *zCur, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string, return */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Perform the requested operation */ - zEnd = &zString[nLen]; - for(;;){ - if( zString >= zEnd ){ - /* No more input, break immediately */ - break; - } - if( (unsigned char)zString[0] >= 0xc0 ){ - /* UTF-8 stream, output verbatim */ - zCur = zString; - zString++; - while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){ - zString++; - } - /* Append UTF-8 stream */ - jx9_result_string(pCtx, zCur, (int)(zString-zCur)); - }else{ - int c = zString[0]; - if( SyisUpper(c) ){ - c = SyToLower(zString[0]); - } - /* Append character */ - jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - /* Advance the cursor */ - zString++; - } - } - return JX9_OK; -} -/* - * string strtolower(string $str) - * Make a string uppercase. - * Parameters - * $str - * The input string. - * Returns. - * The uppercased string. - */ -static int jx9Builtin_strtoupper(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString, *zCur, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string, return */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Perform the requested operation */ - zEnd = &zString[nLen]; - for(;;){ - if( zString >= zEnd ){ - /* No more input, break immediately */ - break; - } - if( (unsigned char)zString[0] >= 0xc0 ){ - /* UTF-8 stream, output verbatim */ - zCur = zString; - zString++; - while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){ - zString++; - } - /* Append UTF-8 stream */ - jx9_result_string(pCtx, zCur, (int)(zString-zCur)); - }else{ - int c = zString[0]; - if( SyisLower(c) ){ - c = SyToUpper(zString[0]); - } - /* Append character */ - jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - /* Advance the cursor */ - zString++; - } - } - return JX9_OK; -} -/* - * int ord(string $string) - * Returns the ASCII value of the first character of string. - * Parameters - * $str - * The input string. - * Returns. - * The ASCII value as an integer. - */ -static int jx9Builtin_ord(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString; - int nLen, c; - if( nArg < 1 ){ - /* Missing arguments, return -1 */ - jx9_result_int(pCtx, -1); - return JX9_OK; - } - /* Extract the target string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string, return -1 */ - jx9_result_int(pCtx, -1); - return JX9_OK; - } - /* Extract the ASCII value of the first character */ - c = zString[0]; - /* Return that value */ - jx9_result_int(pCtx, c); - return JX9_OK; -} -/* - * string chr(int $ascii) - * Returns a one-character string containing the character specified by ascii. - * Parameters - * $ascii - * The ascii code. - * Returns. - * The specified character. - */ -static int jx9Builtin_chr(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int c; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the ASCII value */ - c = jx9_value_to_int(apArg[0]); - /* Return the specified character */ - jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - return JX9_OK; -} -/* - * Binary to hex consumer callback. - * This callback is the default consumer used by the hash functions - * [i.e: bin2hex(), md5(), sha1(), md5_file() ... ] defined below. - */ -static int HashConsumer(const void *pData, unsigned int nLen, void *pUserData) -{ - /* Append hex chunk verbatim */ - jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen); - return SXRET_OK; -} -/* - * string bin2hex(string $str) - * Convert binary data into hexadecimal representation. - * Parameters - * $str - * The input string. - * Returns. - * Returns the hexadecimal representation of the given string. - */ -static int jx9Builtin_bin2hex(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string, return */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Perform the requested operation */ - SyBinToHexConsumer((const void *)zString, (sxu32)nLen, HashConsumer, pCtx); - return JX9_OK; -} -/* Search callback signature */ -typedef sxi32 (*ProcStringMatch)(const void *, sxu32, const void *, sxu32, sxu32 *); -/* - * Case-insensitive pattern match. - * Brute force is the default search method used here. - * This is due to the fact that brute-forcing works quite - * well for short/medium texts on modern hardware. - */ -static sxi32 iPatternMatch(const void *pText, sxu32 nLen, const void *pPattern, sxu32 iPatLen, sxu32 *pOfft) -{ - const char *zpIn = (const char *)pPattern; - const char *zIn = (const char *)pText; - const char *zpEnd = &zpIn[iPatLen]; - const char *zEnd = &zIn[nLen]; - const char *zPtr, *zPtr2; - int c, d; - if( iPatLen > nLen ){ - /* Don't bother processing */ - return SXERR_NOTFOUND; - } - for(;;){ - if( zIn >= zEnd ){ - break; - } - c = SyToLower(zIn[0]); - d = SyToLower(zpIn[0]); - if( c == d ){ - zPtr = &zIn[1]; - zPtr2 = &zpIn[1]; - for(;;){ - if( zPtr2 >= zpEnd ){ - /* Pattern found */ - if( pOfft ){ *pOfft = (sxu32)(zIn-(const char *)pText); } - return SXRET_OK; - } - if( zPtr >= zEnd ){ - break; - } - c = SyToLower(zPtr[0]); - d = SyToLower(zPtr2[0]); - if( c != d ){ - break; - } - zPtr++; zPtr2++; - } - } - zIn++; - } - /* Pattern not found */ - return SXERR_NOTFOUND; -} -/* - * string strstr(string $haystack, string $needle[, bool $before_needle = false ]) - * Find the first occurrence of a string. - * Parameters - * $haystack - * The input string. - * $needle - * Search pattern (must be a string). - * $before_needle - * If TRUE, strstr() returns the part of the haystack before the first occurrence - * of the needle (excluding the needle). - * Return - * Returns the portion of string, or FALSE if needle is not found. - */ -static int jx9Builtin_strstr(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */ - const char *zBlob, *zPattern; - int nLen, nPatLen; - sxu32 nOfft; - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the needle and the haystack */ - zBlob = jx9_value_to_string(apArg[0], &nLen); - zPattern = jx9_value_to_string(apArg[1], &nPatLen); - nOfft = 0; /* cc warning */ - if( nLen > 0 && nPatLen > 0 ){ - int before = 0; - /* Perform the lookup */ - rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft); - if( rc != SXRET_OK ){ - /* Pattern not found, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Return the portion of the string */ - if( nArg > 2 ){ - before = jx9_value_to_int(apArg[2]); - } - if( before ){ - jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob)); - }else{ - jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft])); - } - }else{ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * string stristr(string $haystack, string $needle[, bool $before_needle = false ]) - * Case-insensitive strstr(). - * Parameters - * $haystack - * The input string. - * $needle - * Search pattern (must be a string). - * $before_needle - * If TRUE, strstr() returns the part of the haystack before the first occurrence - * of the needle (excluding the needle). - * Return - * Returns the portion of string, or FALSE if needle is not found. - */ -static int jx9Builtin_stristr(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */ - const char *zBlob, *zPattern; - int nLen, nPatLen; - sxu32 nOfft; - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the needle and the haystack */ - zBlob = jx9_value_to_string(apArg[0], &nLen); - zPattern = jx9_value_to_string(apArg[1], &nPatLen); - nOfft = 0; /* cc warning */ - if( nLen > 0 && nPatLen > 0 ){ - int before = 0; - /* Perform the lookup */ - rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft); - if( rc != SXRET_OK ){ - /* Pattern not found, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Return the portion of the string */ - if( nArg > 2 ){ - before = jx9_value_to_int(apArg[2]); - } - if( before ){ - jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob)); - }else{ - jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft])); - } - }else{ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * int strpos(string $haystack, string $needle [, int $offset = 0 ] ) - * Returns the numeric position of the first occurrence of needle in the haystack string. - * Parameters - * $haystack - * The input string. - * $needle - * Search pattern (must be a string). - * $offset - * This optional offset parameter allows you to specify which character in haystack - * to start searching. The position returned is still relative to the beginning - * of haystack. - * Return - * Returns the position as an integer.If needle is not found, strpos() will return FALSE. - */ -static int jx9Builtin_strpos(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */ - const char *zBlob, *zPattern; - int nLen, nPatLen, nStart; - sxu32 nOfft; - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the needle and the haystack */ - zBlob = jx9_value_to_string(apArg[0], &nLen); - zPattern = jx9_value_to_string(apArg[1], &nPatLen); - nOfft = 0; /* cc warning */ - nStart = 0; - /* Peek the starting offset if available */ - if( nArg > 2 ){ - nStart = jx9_value_to_int(apArg[2]); - if( nStart < 0 ){ - nStart = -nStart; - } - if( nStart >= nLen ){ - /* Invalid offset */ - nStart = 0; - }else{ - zBlob += nStart; - nLen -= nStart; - } - } - if( nLen > 0 && nPatLen > 0 ){ - /* Perform the lookup */ - rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft); - if( rc != SXRET_OK ){ - /* Pattern not found, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Return the pattern position */ - jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart)); - }else{ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * int stripos(string $haystack, string $needle [, int $offset = 0 ] ) - * Case-insensitive strpos. - * Parameters - * $haystack - * The input string. - * $needle - * Search pattern (must be a string). - * $offset - * This optional offset parameter allows you to specify which character in haystack - * to start searching. The position returned is still relative to the beginning - * of haystack. - * Return - * Returns the position as an integer.If needle is not found, strpos() will return FALSE. - */ -static int jx9Builtin_stripos(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */ - const char *zBlob, *zPattern; - int nLen, nPatLen, nStart; - sxu32 nOfft; - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the needle and the haystack */ - zBlob = jx9_value_to_string(apArg[0], &nLen); - zPattern = jx9_value_to_string(apArg[1], &nPatLen); - nOfft = 0; /* cc warning */ - nStart = 0; - /* Peek the starting offset if available */ - if( nArg > 2 ){ - nStart = jx9_value_to_int(apArg[2]); - if( nStart < 0 ){ - nStart = -nStart; - } - if( nStart >= nLen ){ - /* Invalid offset */ - nStart = 0; - }else{ - zBlob += nStart; - nLen -= nStart; - } - } - if( nLen > 0 && nPatLen > 0 ){ - /* Perform the lookup */ - rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft); - if( rc != SXRET_OK ){ - /* Pattern not found, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Return the pattern position */ - jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart)); - }else{ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * int strrpos(string $haystack, string $needle [, int $offset = 0 ] ) - * Find the numeric position of the last occurrence of needle in the haystack string. - * Parameters - * $haystack - * The input string. - * $needle - * Search pattern (must be a string). - * $offset - * If specified, search will start this number of characters counted from the beginning - * of the string. If the value is negative, search will instead start from that many - * characters from the end of the string, searching backwards. - * Return - * Returns the position as an integer.If needle is not found, strrpos() will return FALSE. - */ -static int jx9Builtin_strrpos(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd; - ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */ - int nLen, nPatLen; - sxu32 nOfft; - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the needle and the haystack */ - zBlob = jx9_value_to_string(apArg[0], &nLen); - zPattern = jx9_value_to_string(apArg[1], &nPatLen); - /* Point to the end of the pattern */ - zPtr = &zBlob[nLen - 1]; - zEnd = &zBlob[nLen]; - /* Save the starting posistion */ - zStart = zBlob; - nOfft = 0; /* cc warning */ - /* Peek the starting offset if available */ - if( nArg > 2 ){ - int nStart; - nStart = jx9_value_to_int(apArg[2]); - if( nStart < 0 ){ - nStart = -nStart; - if( nStart >= nLen ){ - /* Invalid offset */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - }else{ - nLen -= nStart; - zPtr = &zBlob[nLen - 1]; - zEnd = &zBlob[nLen]; - } - }else{ - if( nStart >= nLen ){ - /* Invalid offset */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - }else{ - zBlob += nStart; - nLen -= nStart; - } - } - } - if( nLen > 0 && nPatLen > 0 ){ - /* Perform the lookup */ - for(;;){ - if( zBlob >= zPtr ){ - break; - } - rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft); - if( rc == SXRET_OK ){ - /* Pattern found, return it's position */ - jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart)); - return JX9_OK; - } - zPtr--; - } - /* Pattern not found, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * int strripos(string $haystack, string $needle [, int $offset = 0 ] ) - * Case-insensitive strrpos. - * Parameters - * $haystack - * The input string. - * $needle - * Search pattern (must be a string). - * $offset - * If specified, search will start this number of characters counted from the beginning - * of the string. If the value is negative, search will instead start from that many - * characters from the end of the string, searching backwards. - * Return - * Returns the position as an integer.If needle is not found, strrpos() will return FALSE. - */ -static int jx9Builtin_strripos(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd; - ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */ - int nLen, nPatLen; - sxu32 nOfft; - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the needle and the haystack */ - zBlob = jx9_value_to_string(apArg[0], &nLen); - zPattern = jx9_value_to_string(apArg[1], &nPatLen); - /* Point to the end of the pattern */ - zPtr = &zBlob[nLen - 1]; - zEnd = &zBlob[nLen]; - /* Save the starting posistion */ - zStart = zBlob; - nOfft = 0; /* cc warning */ - /* Peek the starting offset if available */ - if( nArg > 2 ){ - int nStart; - nStart = jx9_value_to_int(apArg[2]); - if( nStart < 0 ){ - nStart = -nStart; - if( nStart >= nLen ){ - /* Invalid offset */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - }else{ - nLen -= nStart; - zPtr = &zBlob[nLen - 1]; - zEnd = &zBlob[nLen]; - } - }else{ - if( nStart >= nLen ){ - /* Invalid offset */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - }else{ - zBlob += nStart; - nLen -= nStart; - } - } - } - if( nLen > 0 && nPatLen > 0 ){ - /* Perform the lookup */ - for(;;){ - if( zBlob >= zPtr ){ - break; - } - rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft); - if( rc == SXRET_OK ){ - /* Pattern found, return it's position */ - jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart)); - return JX9_OK; - } - zPtr--; - } - /* Pattern not found, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * int strrchr(string $haystack, mixed $needle) - * Find the last occurrence of a character in a string. - * Parameters - * $haystack - * The input string. - * $needle - * If needle contains more than one character, only the first is used. - * This behavior is different from that of strstr(). - * If needle is not a string, it is converted to an integer and applied - * as the ordinal value of a character. - * Return - * This function returns the portion of string, or FALSE if needle is not found. - */ -static int jx9Builtin_strrchr(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zBlob; - int nLen, c; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the haystack */ - zBlob = jx9_value_to_string(apArg[0], &nLen); - c = 0; /* cc warning */ - if( nLen > 0 ){ - sxu32 nOfft; - sxi32 rc; - if( jx9_value_is_string(apArg[1]) ){ - const char *zPattern; - zPattern = jx9_value_to_string(apArg[1], 0); /* Never fail, so there is no need to check - * for NULL pointer. - */ - c = zPattern[0]; - }else{ - /* Int cast */ - c = jx9_value_to_int(apArg[1]); - } - /* Perform the lookup */ - rc = SyByteFind2(zBlob, (sxu32)nLen, c, &nOfft); - if( rc != SXRET_OK ){ - /* No such entry, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Return the string portion */ - jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft])); - }else{ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * string strrev(string $string) - * Reverse a string. - * Parameters - * $string - * String to be reversed. - * Return - * The reversed string. - */ -static int jx9Builtin_strrev(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn, *zEnd; - int nLen, c; - if( nArg < 1 ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string Return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Perform the requested operation */ - zEnd = &zIn[nLen - 1]; - for(;;){ - if( zEnd < zIn ){ - /* No more input to process */ - break; - } - /* Append current character */ - c = zEnd[0]; - jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - zEnd--; - } - return JX9_OK; -} -/* - * string str_repeat(string $input, int $multiplier) - * Returns input repeated multiplier times. - * Parameters - * $string - * String to be repeated. - * $multiplier - * Number of time the input string should be repeated. - * multiplier has to be greater than or equal to 0. If the multiplier is set - * to 0, the function will return an empty string. - * Return - * The repeated string. - */ -static int jx9Builtin_str_repeat(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn; - int nLen, nMul; - int rc; - if( nArg < 2 ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string.Return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the multiplier */ - nMul = jx9_value_to_int(apArg[1]); - if( nMul < 1 ){ - /* Return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( nMul < 1 ){ - break; - } - /* Append the copy */ - rc = jx9_result_string(pCtx, zIn, nLen); - if( rc != JX9_OK ){ - /* Out of memory, break immediately */ - break; - } - nMul--; - } - return JX9_OK; -} -/* - * string nl2br(string $string[, bool $is_xhtml = true ]) - * Inserts HTML line breaks before all newlines in a string. - * Parameters - * $string - * The input string. - * $is_xhtml - * Whenever to use XHTML compatible line breaks or not. - * Return - * The processed string. - */ -static int jx9Builtin_nl2br(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn, *zCur, *zEnd; - int is_xhtml = 0; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - if( nArg > 1 ){ - is_xhtml = jx9_value_to_bool(apArg[1]); - } - zEnd = &zIn[nLen]; - /* Perform the requested operation */ - for(;;){ - zCur = zIn; - /* Delimit the string */ - while( zIn < zEnd && (zIn[0] != '\n'&& zIn[0] != '\r') ){ - zIn++; - } - if( zCur < zIn ){ - /* Output chunk verbatim */ - jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); - } - if( zIn >= zEnd ){ - /* No more input to process */ - break; - } - /* Output the HTML line break */ - if( is_xhtml ){ - jx9_result_string(pCtx, "
", (int)sizeof("
")-1); - }else{ - jx9_result_string(pCtx, "
", (int)sizeof("
")-1); - } - zCur = zIn; - /* Append trailing line */ - while( zIn < zEnd && (zIn[0] == '\n' || zIn[0] == '\r') ){ - zIn++; - } - if( zCur < zIn ){ - /* Output chunk verbatim */ - jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); - } - } - return JX9_OK; -} -/* - * Format a given string and invoke the given callback on each processed chunk. - * According to the JX9 reference manual. - * The format string is composed of zero or more directives: ordinary characters - * (excluding %) that are copied directly to the result, and conversion - * specifications, each of which results in fetching its own parameter. - * This applies to both sprintf() and printf(). - * Each conversion specification consists of a percent sign (%), followed by one - * or more of these elements, in order: - * An optional sign specifier that forces a sign (- or +) to be used on a number. - * By default, only the - sign is used on a number if it's negative. This specifier forces - * positive numbers to have the + sign attached as well. - * An optional padding specifier that says what character will be used for padding - * the results to the right string size. This may be a space character or a 0 (zero character). - * The default is to pad with spaces. An alternate padding character can be specified by prefixing - * it with a single quote ('). See the examples below. - * An optional alignment specifier that says if the result should be left-justified or right-justified. - * The default is right-justified; a - character here will make it left-justified. - * An optional number, a width specifier that says how many characters (minimum) this conversion - * should result in. - * An optional precision specifier in the form of a period (`.') followed by an optional decimal - * digit string that says how many decimal digits should be displayed for floating-point numbers. - * When using this specifier on a string, it acts as a cutoff point, setting a maximum character - * limit to the string. - * A type specifier that says what type the argument data should be treated as. Possible types: - * % - a literal percent character. No argument is required. - * b - the argument is treated as an integer, and presented as a binary number. - * c - the argument is treated as an integer, and presented as the character with that ASCII value. - * d - the argument is treated as an integer, and presented as a (signed) decimal number. - * e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands - * for the number of digits after the decimal point. - * E - like %e but uses uppercase letter (e.g. 1.2E+2). - * u - the argument is treated as an integer, and presented as an unsigned decimal number. - * f - the argument is treated as a float, and presented as a floating-point number (locale aware). - * F - the argument is treated as a float, and presented as a floating-point number (non-locale aware). - * g - shorter of %e and %f. - * G - shorter of %E and %f. - * o - the argument is treated as an integer, and presented as an octal number. - * s - the argument is treated as and presented as a string. - * x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters). - * X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters). - */ -/* - * This implementation is based on the one found in the SQLite3 source tree. - */ -#define JX9_FMT_BUFSIZ 1024 /* Conversion buffer size */ -/* -** Conversion types fall into various categories as defined by the -** following enumeration. -*/ -#define JX9_FMT_RADIX 1 /* Integer types.%d, %x, %o, and so forth */ -#define JX9_FMT_FLOAT 2 /* Floating point.%f */ -#define JX9_FMT_EXP 3 /* Exponentional notation.%e and %E */ -#define JX9_FMT_GENERIC 4 /* Floating or exponential, depending on exponent.%g */ -#define JX9_FMT_SIZE 5 /* Total number of characters processed so far.%n */ -#define JX9_FMT_STRING 6 /* Strings.%s */ -#define JX9_FMT_PERCENT 7 /* Percent symbol.%% */ -#define JX9_FMT_CHARX 8 /* Characters.%c */ -#define JX9_FMT_ERROR 9 /* Used to indicate no such conversion type */ -/* -** Allowed values for jx9_fmt_info.flags -*/ -#define JX9_FMT_FLAG_SIGNED 0x01 -#define JX9_FMT_FLAG_UNSIGNED 0x02 -/* -** Each builtin conversion character (ex: the 'd' in "%d") is described -** by an instance of the following structure -*/ -typedef struct jx9_fmt_info jx9_fmt_info; -struct jx9_fmt_info -{ - char fmttype; /* The format field code letter [i.e: 'd', 's', 'x'] */ - sxu8 base; /* The base for radix conversion */ - int flags; /* One or more of JX9_FMT_FLAG_ constants below */ - sxu8 type; /* Conversion paradigm */ - char *charset; /* The character set for conversion */ - char *prefix; /* Prefix on non-zero values in alt format */ -}; -#ifndef JX9_OMIT_FLOATING_POINT -/* -** "*val" is a double such that 0.1 <= *val < 10.0 -** Return the ascii code for the leading digit of *val, then -** multiply "*val" by 10.0 to renormalize. -** -** Example: -** input: *val = 3.14159 -** output: *val = 1.4159 function return = '3' -** -** The counter *cnt is incremented each time. After counter exceeds -** 16 (the number of significant digits in a 64-bit float) '0' is -** always returned. -*/ -static int vxGetdigit(sxlongreal *val, int *cnt) -{ - sxlongreal d; - int digit; - - if( (*cnt)++ >= 16 ){ - return '0'; - } - digit = (int)*val; - d = digit; - *val = (*val - d)*10.0; - return digit + '0' ; -} -#endif /* JX9_OMIT_FLOATING_POINT */ -/* - * The following table is searched linearly, so it is good to put the most frequently - * used conversion types first. - */ -static const jx9_fmt_info aFmt[] = { - { 'd', 10, JX9_FMT_FLAG_SIGNED, JX9_FMT_RADIX, "0123456789", 0 }, - { 's', 0, 0, JX9_FMT_STRING, 0, 0 }, - { 'c', 0, 0, JX9_FMT_CHARX, 0, 0 }, - { 'x', 16, 0, JX9_FMT_RADIX, "0123456789abcdef", "x0" }, - { 'X', 16, 0, JX9_FMT_RADIX, "0123456789ABCDEF", "X0" }, - { 'b', 2, 0, JX9_FMT_RADIX, "01", "b0"}, - { 'o', 8, 0, JX9_FMT_RADIX, "01234567", "0" }, - { 'u', 10, 0, JX9_FMT_RADIX, "0123456789", 0 }, - { 'f', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT, 0, 0 }, - { 'F', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT, 0, 0 }, - { 'e', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP, "e", 0 }, - { 'E', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP, "E", 0 }, - { 'g', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC, "e", 0 }, - { 'G', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC, "E", 0 }, - { '%', 0, 0, JX9_FMT_PERCENT, 0, 0 } -}; -/* - * Format a given string. - * The root program. All variations call this core. - * INPUTS: - * xConsumer This is a pointer to a function taking four arguments - * 1. A pointer to the call context. - * 2. A pointer to the list of characters to be output - * (Note, this list is NOT null terminated.) - * 3. An integer number of characters to be output. - * (Note: This number might be zero.) - * 4. Upper layer private data. - * zIn This is the format string, as in the usual print. - * apArg This is a pointer to a list of arguments. - */ -JX9_PRIVATE sxi32 jx9InputFormat( - int (*xConsumer)(jx9_context *, const char *, int, void *), /* Format consumer */ - jx9_context *pCtx, /* call context */ - const char *zIn, /* Format string */ - int nByte, /* Format string length */ - int nArg, /* Total argument of the given arguments */ - jx9_value **apArg, /* User arguments */ - void *pUserData, /* Last argument to xConsumer() */ - int vf /* TRUE if called from vfprintf, vsprintf context */ - ) -{ - char spaces[] = " "; -#define etSPACESIZE ((int)sizeof(spaces)-1) - const char *zCur, *zEnd = &zIn[nByte]; - char *zBuf, zWorker[JX9_FMT_BUFSIZ]; /* Working buffer */ - const jx9_fmt_info *pInfo; /* Pointer to the appropriate info structure */ - int flag_alternateform; /* True if "#" flag is present */ - int flag_leftjustify; /* True if "-" flag is present */ - int flag_blanksign; /* True if " " flag is present */ - int flag_plussign; /* True if "+" flag is present */ - int flag_zeropad; /* True if field width constant starts with zero */ - jx9_value *pArg; /* Current processed argument */ - jx9_int64 iVal; - int precision; /* Precision of the current field */ - char *zExtra; - int c, rc, n; - int length; /* Length of the field */ - int prefix; - sxu8 xtype; /* Conversion paradigm */ - int width; /* Width of the current field */ - int idx; - n = (vf == TRUE) ? 0 : 1; -#define NEXT_ARG ( n < nArg ? apArg[n++] : 0 ) - /* Start the format process */ - for(;;){ - zCur = zIn; - while( zIn < zEnd && zIn[0] != '%' ){ - zIn++; - } - if( zCur < zIn ){ - /* Consume chunk verbatim */ - rc = xConsumer(pCtx, zCur, (int)(zIn-zCur), pUserData); - if( rc == SXERR_ABORT ){ - /* Callback request an operation abort */ - break; - } - } - if( zIn >= zEnd ){ - /* No more input to process, break immediately */ - break; - } - /* Find out what flags are present */ - flag_leftjustify = flag_plussign = flag_blanksign = - flag_alternateform = flag_zeropad = 0; - zIn++; /* Jump the precent sign */ - do{ - c = zIn[0]; - switch( c ){ - case '-': flag_leftjustify = 1; c = 0; break; - case '+': flag_plussign = 1; c = 0; break; - case ' ': flag_blanksign = 1; c = 0; break; - case '#': flag_alternateform = 1; c = 0; break; - case '0': flag_zeropad = 1; c = 0; break; - case '\'': - zIn++; - if( zIn < zEnd ){ - /* An alternate padding character can be specified by prefixing it with a single quote (') */ - c = zIn[0]; - for(idx = 0 ; idx < etSPACESIZE ; ++idx ){ - spaces[idx] = (char)c; - } - c = 0; - } - break; - default: break; - } - }while( c==0 && (zIn++ < zEnd) ); - /* Get the field width */ - width = 0; - while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){ - width = width*10 + (zIn[0] - '0'); - zIn++; - } - if( zIn < zEnd && zIn[0] == '$' ){ - /* Position specifer */ - if( width > 0 ){ - n = width; - if( vf && n > 0 ){ - n--; - } - } - zIn++; - width = 0; - if( zIn < zEnd && zIn[0] == '0' ){ - flag_zeropad = 1; - zIn++; - } - while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){ - width = width*10 + (zIn[0] - '0'); - zIn++; - } - } - if( width > JX9_FMT_BUFSIZ-10 ){ - width = JX9_FMT_BUFSIZ-10; - } - /* Get the precision */ - precision = -1; - if( zIn < zEnd && zIn[0] == '.' ){ - precision = 0; - zIn++; - while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){ - precision = precision*10 + (zIn[0] - '0'); - zIn++; - } - } - if( zIn >= zEnd ){ - /* No more input */ - break; - } - /* Fetch the info entry for the field */ - pInfo = 0; - xtype = JX9_FMT_ERROR; - c = zIn[0]; - zIn++; /* Jump the format specifer */ - for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){ - if( c==aFmt[idx].fmttype ){ - pInfo = &aFmt[idx]; - xtype = pInfo->type; - break; - } - } - zBuf = zWorker; /* Point to the working buffer */ - length = 0; - zExtra = 0; - /* - ** At this point, variables are initialized as follows: - ** - ** flag_alternateform TRUE if a '#' is present. - ** flag_plussign TRUE if a '+' is present. - ** flag_leftjustify TRUE if a '-' is present or if the - ** field width was negative. - ** flag_zeropad TRUE if the width began with 0. - ** the conversion character. - ** flag_blanksign TRUE if a ' ' is present. - ** width The specified field width. This is - ** always non-negative. Zero is the default. - ** precision The specified precision. The default - ** is -1. - */ - switch(xtype){ - case JX9_FMT_PERCENT: - /* A literal percent character */ - zWorker[0] = '%'; - length = (int)sizeof(char); - break; - case JX9_FMT_CHARX: - /* The argument is treated as an integer, and presented as the character - * with that ASCII value - */ - pArg = NEXT_ARG; - if( pArg == 0 ){ - c = 0; - }else{ - c = jx9_value_to_int(pArg); - } - /* NUL byte is an acceptable value */ - zWorker[0] = (char)c; - length = (int)sizeof(char); - break; - case JX9_FMT_STRING: - /* the argument is treated as and presented as a string */ - pArg = NEXT_ARG; - if( pArg == 0 ){ - length = 0; - }else{ - zBuf = (char *)jx9_value_to_string(pArg, &length); - } - if( length < 1 ){ - zBuf = " "; - length = (int)sizeof(char); - } - if( precision>=0 && precisionJX9_FMT_BUFSIZ-40 ){ - precision = JX9_FMT_BUFSIZ-40; - } -#if 1 - /* For the format %#x, the value zero is printed "0" not "0x0". - ** I think this is stupid.*/ - if( iVal==0 ) flag_alternateform = 0; -#else - /* More sensible: turn off the prefix for octal (to prevent "00"), - ** but leave the prefix for hex.*/ - if( iVal==0 && pInfo->base==8 ) flag_alternateform = 0; -#endif - if( pInfo->flags & JX9_FMT_FLAG_SIGNED ){ - if( iVal<0 ){ - iVal = -iVal; - /* Ticket 1433-003 */ - if( iVal < 0 ){ - /* Overflow */ - iVal= 0x7FFFFFFFFFFFFFFF; - } - prefix = '-'; - }else if( flag_plussign ) prefix = '+'; - else if( flag_blanksign ) prefix = ' '; - else prefix = 0; - }else{ - if( iVal<0 ){ - iVal = -iVal; - /* Ticket 1433-003 */ - if( iVal < 0 ){ - /* Overflow */ - iVal= 0x7FFFFFFFFFFFFFFF; - } - } - prefix = 0; - } - if( flag_zeropad && precisioncharset; - base = pInfo->base; - do{ /* Convert to ascii */ - *(--zBuf) = cset[iVal%base]; - iVal = iVal/base; - }while( iVal>0 ); - } - length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf; - for(idx=precision-length; idx>0; idx--){ - *(--zBuf) = '0'; /* Zero pad */ - } - if( prefix ) *(--zBuf) = (char)prefix; /* Add sign */ - if( flag_alternateform && pInfo->prefix ){ /* Add "0" or "0x" */ - char *pre, x; - pre = pInfo->prefix; - if( *zBuf!=pre[0] ){ - for(pre=pInfo->prefix; (x=(*pre))!=0; pre++) *(--zBuf) = x; - } - } - length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf; - break; - case JX9_FMT_FLOAT: - case JX9_FMT_EXP: - case JX9_FMT_GENERIC:{ -#ifndef JX9_OMIT_FLOATING_POINT - long double realvalue; - int exp; /* exponent of real numbers */ - double rounder; /* Used for rounding floating point values */ - int flag_dp; /* True if decimal point should be shown */ - int flag_rtz; /* True if trailing zeros should be removed */ - int flag_exp; /* True to force display of the exponent */ - int nsd; /* Number of significant digits returned */ - pArg = NEXT_ARG; - if( pArg == 0 ){ - realvalue = 0; - }else{ - realvalue = jx9_value_to_double(pArg); - } - if( precision<0 ) precision = 6; /* Set default precision */ - if( precision>JX9_FMT_BUFSIZ-40) precision = JX9_FMT_BUFSIZ-40; - if( realvalue<0.0 ){ - realvalue = -realvalue; - prefix = '-'; - }else{ - if( flag_plussign ) prefix = '+'; - else if( flag_blanksign ) prefix = ' '; - else prefix = 0; - } - if( pInfo->type==JX9_FMT_GENERIC && precision>0 ) precision--; - rounder = 0.0; -#if 0 - /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */ - for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); -#else - /* It makes more sense to use 0.5 */ - for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1); -#endif - if( pInfo->type==JX9_FMT_FLOAT ) realvalue += rounder; - /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ - exp = 0; - if( realvalue>0.0 ){ - while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } - while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } - while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; } - while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; } - if( exp>350 || exp<-350 ){ - zBuf = "NaN"; - length = 3; - break; - } - } - zBuf = zWorker; - /* - ** If the field type is etGENERIC, then convert to either etEXP - ** or etFLOAT, as appropriate. - */ - flag_exp = xtype==JX9_FMT_EXP; - if( xtype!=JX9_FMT_FLOAT ){ - realvalue += rounder; - if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } - } - if( xtype==JX9_FMT_GENERIC ){ - flag_rtz = !flag_alternateform; - if( exp<-4 || exp>precision ){ - xtype = JX9_FMT_EXP; - }else{ - precision = precision - exp; - xtype = JX9_FMT_FLOAT; - } - }else{ - flag_rtz = 0; - } - /* - ** The "exp+precision" test causes output to be of type etEXP if - ** the precision is too large to fit in buf[]. - */ - nsd = 0; - if( xtype==JX9_FMT_FLOAT && exp+precision0 || flag_alternateform); - if( prefix ) *(zBuf++) = (char)prefix; /* Sign */ - if( exp<0 ) *(zBuf++) = '0'; /* Digits before "." */ - else for(; exp>=0; exp--) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); - if( flag_dp ) *(zBuf++) = '.'; /* The decimal point */ - for(exp++; exp<0 && precision>0; precision--, exp++){ - *(zBuf++) = '0'; - } - while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); - *(zBuf--) = 0; /* Null terminate */ - if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */ - while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0; - if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0; - } - zBuf++; /* point to next free slot */ - }else{ /* etEXP or etGENERIC */ - flag_dp = (precision>0 || flag_alternateform); - if( prefix ) *(zBuf++) = (char)prefix; /* Sign */ - *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); /* First digit */ - if( flag_dp ) *(zBuf++) = '.'; /* Decimal point */ - while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); - zBuf--; /* point to last digit */ - if( flag_rtz && flag_dp ){ /* Remove tail zeros */ - while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0; - if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0; - } - zBuf++; /* point to next free slot */ - if( exp || flag_exp ){ - *(zBuf++) = pInfo->charset[0]; - if( exp<0 ){ *(zBuf++) = '-'; exp = -exp; } /* sign of exp */ - else { *(zBuf++) = '+'; } - if( exp>=100 ){ - *(zBuf++) = (char)((exp/100)+'0'); /* 100's digit */ - exp %= 100; - } - *(zBuf++) = (char)(exp/10+'0'); /* 10's digit */ - *(zBuf++) = (char)(exp%10+'0'); /* 1's digit */ - } - } - /* The converted number is in buf[] and zero terminated.Output it. - ** Note that the number is in the usual order, not reversed as with - ** integer conversions.*/ - length = (int)(zBuf-zWorker); - zBuf = zWorker; - /* Special case: Add leading zeros if the flag_zeropad flag is - ** set and we are not left justified */ - if( flag_zeropad && !flag_leftjustify && length < width){ - int i; - int nPad = width - length; - for(i=width; i>=nPad; i--){ - zBuf[i] = zBuf[i-nPad]; - } - i = prefix!=0; - while( nPad-- ) zBuf[i++] = '0'; - length = width; - } -#else - zBuf = " "; - length = (int)sizeof(char); -#endif /* JX9_OMIT_FLOATING_POINT */ - break; - } - default: - /* Invalid format specifer */ - zWorker[0] = '?'; - length = (int)sizeof(char); - break; - } - /* - ** The text of the conversion is pointed to by "zBuf" and is - ** "length" characters long.The field width is "width".Do - ** the output. - */ - if( !flag_leftjustify ){ - register int nspace; - nspace = width-length; - if( nspace>0 ){ - while( nspace>=etSPACESIZE ){ - rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - nspace -= etSPACESIZE; - } - if( nspace>0 ){ - rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - } - } - } - if( length>0 ){ - rc = xConsumer(pCtx, zBuf, (unsigned int)length, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - } - if( flag_leftjustify ){ - register int nspace; - nspace = width-length; - if( nspace>0 ){ - while( nspace>=etSPACESIZE ){ - rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - nspace -= etSPACESIZE; - } - if( nspace>0 ){ - rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - } - } - } - }/* for(;;) */ - return SXRET_OK; -} -/* - * Callback [i.e: Formatted input consumer] of the sprintf function. - */ -static int sprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData) -{ - /* Consume directly */ - jx9_result_string(pCtx, zInput, nLen); - SXUNUSED(pUserData); /* cc warning */ - return JX9_OK; -} -/* - * string sprintf(string $format[, mixed $args [, mixed $... ]]) - * Return a formatted string. - * Parameters - * $format - * The format string (see block comment above) - * Return - * A string produced according to the formatting string format. - */ -static int jx9Builtin_sprintf(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zFormat; - int nLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Extract the string format */ - zFormat = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Format the string */ - jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, nArg, apArg, 0, FALSE); - return JX9_OK; -} -/* - * Callback [i.e: Formatted input consumer] of the printf function. - */ -static int printfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData) -{ - jx9_int64 *pCounter = (jx9_int64 *)pUserData; - /* Call the VM output consumer directly */ - jx9_context_output(pCtx, zInput, nLen); - /* Increment counter */ - *pCounter += nLen; - return JX9_OK; -} -/* - * int64 printf(string $format[, mixed $args[, mixed $... ]]) - * Output a formatted string. - * Parameters - * $format - * See sprintf() for a description of format. - * Return - * The length of the outputted string. - */ -static int jx9Builtin_printf(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_int64 nCounter = 0; - const char *zFormat; - int nLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Extract the string format */ - zFormat = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Format the string */ - jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, nArg, apArg, (void *)&nCounter, FALSE); - /* Return the length of the outputted string */ - jx9_result_int64(pCtx, nCounter); - return JX9_OK; -} -/* - * int vprintf(string $format, array $args) - * Output a formatted string. - * Parameters - * $format - * See sprintf() for a description of format. - * Return - * The length of the outputted string. - */ -static int jx9Builtin_vprintf(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_int64 nCounter = 0; - const char *zFormat; - jx9_hashmap *pMap; - SySet sArg; - int nLen, n; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){ - /* Missing/Invalid arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Extract the string format */ - zFormat = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Point to the hashmap */ - pMap = (jx9_hashmap *)apArg[1]->x.pOther; - /* Extract arguments from the hashmap */ - n = jx9HashmapValuesToSet(pMap, &sArg); - /* Format the string */ - jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&nCounter, TRUE); - /* Return the length of the outputted string */ - jx9_result_int64(pCtx, nCounter); - /* Release the container */ - SySetRelease(&sArg); - return JX9_OK; -} -/* - * int vsprintf(string $format, array $args) - * Output a formatted string. - * Parameters - * $format - * See sprintf() for a description of format. - * Return - * A string produced according to the formatting string format. - */ -static int jx9Builtin_vsprintf(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zFormat; - jx9_hashmap *pMap; - SySet sArg; - int nLen, n; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){ - /* Missing/Invalid arguments, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Extract the string format */ - zFormat = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Point to hashmap */ - pMap = (jx9_hashmap *)apArg[1]->x.pOther; - /* Extract arguments from the hashmap */ - n = jx9HashmapValuesToSet(pMap, &sArg); - /* Format the string */ - jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), 0, TRUE); - /* Release the container */ - SySetRelease(&sArg); - return JX9_OK; -} -/* - * string size_format(int64 $size) - * Return a smart string represenation of the given size [i.e: 64-bit integer] - * Example: - * print size_format(1*1024*1024*1024);// 1GB - * print size_format(512*1024*1024); // 512 MB - * print size_format(file_size(/path/to/my/file_8192)); //8KB - * Parameter - * $size - * Entity size in bytes. - * Return - * Formatted string representation of the given size. - */ -static int jx9Builtin_size_format(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - /*Kilo*/ /*Mega*/ /*Giga*/ /*Tera*/ /*Peta*/ /*Exa*/ /*Zeta*/ - static const char zUnit[] = {"KMGTPEZ"}; - sxi32 nRest, i_32; - jx9_int64 iSize; - int c = -1; /* index in zUnit[] */ - - if( nArg < 1 ){ - /* Missing argument, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Extract the given size */ - iSize = jx9_value_to_int64(apArg[0]); - if( iSize < 100 /* Bytes */ ){ - /* Don't bother formatting, return immediately */ - jx9_result_string(pCtx, "0.1 KB", (int)sizeof("0.1 KB")-1); - return JX9_OK; - } - for(;;){ - nRest = (sxi32)(iSize & 0x3FF); - iSize >>= 10; - c++; - if( (iSize & (~0 ^ 1023)) == 0 ){ - break; - } - } - nRest /= 100; - if( nRest > 9 ){ - nRest = 9; - } - if( iSize > 999 ){ - c++; - nRest = 9; - iSize = 0; - } - i_32 = (sxi32)iSize; - /* Format */ - jx9_result_string_format(pCtx, "%d.%d %cB", i_32, nRest, zUnit[c]); - return JX9_OK; -} -#if !defined(JX9_DISABLE_HASH_FUNC) -/* - * string md5(string $str[, bool $raw_output = false]) - * Calculate the md5 hash of a string. - * Parameter - * $str - * Input string - * $raw_output - * If the optional raw_output is set to TRUE, then the md5 digest - * is instead returned in raw binary format with a length of 16. - * Return - * MD5 Hash as a 32-character hexadecimal string. - */ -static int jx9Builtin_md5(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - unsigned char zDigest[16]; - int raw_output = FALSE; - const void *pIn; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Extract the input string */ - pIn = (const void *)jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - if( nArg > 1 && jx9_value_is_bool(apArg[1])){ - raw_output = jx9_value_to_bool(apArg[1]); - } - /* Compute the MD5 digest */ - SyMD5Compute(pIn, (sxu32)nLen, zDigest); - if( raw_output ){ - /* Output raw digest */ - jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest)); - }else{ - /* Perform a binary to hex conversion */ - SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx); - } - return JX9_OK; -} -/* - * string sha1(string $str[, bool $raw_output = false]) - * Calculate the sha1 hash of a string. - * Parameter - * $str - * Input string - * $raw_output - * If the optional raw_output is set to TRUE, then the md5 digest - * is instead returned in raw binary format with a length of 16. - * Return - * SHA1 Hash as a 40-character hexadecimal string. - */ -static int jx9Builtin_sha1(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - unsigned char zDigest[20]; - int raw_output = FALSE; - const void *pIn; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Extract the input string */ - pIn = (const void *)jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - if( nArg > 1 && jx9_value_is_bool(apArg[1])){ - raw_output = jx9_value_to_bool(apArg[1]); - } - /* Compute the SHA1 digest */ - SySha1Compute(pIn, (sxu32)nLen, zDigest); - if( raw_output ){ - /* Output raw digest */ - jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest)); - }else{ - /* Perform a binary to hex conversion */ - SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx); - } - return JX9_OK; -} -/* - * int64 crc32(string $str) - * Calculates the crc32 polynomial of a strin. - * Parameter - * $str - * Input string - * Return - * CRC32 checksum of the given input (64-bit integer). - */ -static int jx9Builtin_crc32(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const void *pIn; - sxu32 nCRC; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Extract the input string */ - pIn = (const void *)jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty string */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Calculate the sum */ - nCRC = SyCrc32(pIn, (sxu32)nLen); - /* Return the CRC32 as 64-bit integer */ - jx9_result_int64(pCtx, (jx9_int64)nCRC^ 0xFFFFFFFF); - return JX9_OK; -} -#endif /* JX9_DISABLE_HASH_FUNC */ -/* - * Parse a CSV string and invoke the supplied callback for each processed xhunk. - */ -JX9_PRIVATE sxi32 jx9ProcessCsv( - const char *zInput, /* Raw input */ - int nByte, /* Input length */ - int delim, /* Delimiter */ - int encl, /* Enclosure */ - int escape, /* Escape character */ - sxi32 (*xConsumer)(const char *, int, void *), /* User callback */ - void *pUserData /* Last argument to xConsumer() */ - ) -{ - const char *zEnd = &zInput[nByte]; - const char *zIn = zInput; - const char *zPtr; - int isEnc; - /* Start processing */ - for(;;){ - if( zIn >= zEnd ){ - /* No more input to process */ - break; - } - isEnc = 0; - zPtr = zIn; - /* Find the first delimiter */ - while( zIn < zEnd ){ - if( zIn[0] == delim && !isEnc){ - /* Delimiter found, break imediately */ - break; - }else if( zIn[0] == encl ){ - /* Inside enclosure? */ - isEnc = !isEnc; - }else if( zIn[0] == escape ){ - /* Escape sequence */ - zIn++; - } - /* Advance the cursor */ - zIn++; - } - if( zIn > zPtr ){ - int nByte = (int)(zIn-zPtr); - sxi32 rc; - /* Invoke the supllied callback */ - if( zPtr[0] == encl ){ - zPtr++; - nByte-=2; - } - if( nByte > 0 ){ - rc = xConsumer(zPtr, nByte, pUserData); - if( rc == SXERR_ABORT ){ - /* User callback request an operation abort */ - break; - } - } - } - /* Ignore trailing delimiter */ - while( zIn < zEnd && zIn[0] == delim ){ - zIn++; - } - } - return SXRET_OK; -} -/* - * Default consumer callback for the CSV parsing routine defined above. - * All the processed input is insereted into an array passed as the last - * argument to this callback. - */ -JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData) -{ - jx9_value *pArray = (jx9_value *)pUserData; - jx9_value sEntry; - SyString sToken; - /* Insert the token in the given array */ - SyStringInitFromBuf(&sToken, zToken, nTokenLen); - /* Remove trailing and leading white spcaces and null bytes */ - SyStringFullTrimSafe(&sToken); - if( sToken.nByte < 1){ - return SXRET_OK; - } - jx9MemObjInitFromString(pArray->pVm, &sEntry, &sToken); - jx9_array_add_elem(pArray, 0, &sEntry); - jx9MemObjRelease(&sEntry); - return SXRET_OK; -} -/* - * array str_getcsv(string $input[, string $delimiter = ', '[, string $enclosure = '"' [, string $escape='\\']]]) - * Parse a CSV string into an array. - * Parameters - * $input - * The string to parse. - * $delimiter - * Set the field delimiter (one character only). - * $enclosure - * Set the field enclosure character (one character only). - * $escape - * Set the escape character (one character only). Defaults as a backslash (\) - * Return - * An indexed array containing the CSV fields or NULL on failure. - */ -static int jx9Builtin_str_getcsv(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zInput, *zPtr; - jx9_value *pArray; - int delim = ','; /* Delimiter */ - int encl = '"' ; /* Enclosure */ - int escape = '\\'; /* Escape character */ - int nLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the raw input */ - zInput = jx9_value_to_string(apArg[0], &nLen); - if( nArg > 1 ){ - int i; - if( jx9_value_is_string(apArg[1]) ){ - /* Extract the delimiter */ - zPtr = jx9_value_to_string(apArg[1], &i); - if( i > 0 ){ - delim = zPtr[0]; - } - } - if( nArg > 2 ){ - if( jx9_value_is_string(apArg[2]) ){ - /* Extract the enclosure */ - zPtr = jx9_value_to_string(apArg[2], &i); - if( i > 0 ){ - encl = zPtr[0]; - } - } - if( nArg > 3 ){ - if( jx9_value_is_string(apArg[3]) ){ - /* Extract the escape character */ - zPtr = jx9_value_to_string(apArg[3], &i); - if( i > 0 ){ - escape = zPtr[0]; - } - } - } - } - } - /* Create our array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_null(pCtx); - return JX9_OK; - } - /* Parse the raw input */ - jx9ProcessCsv(zInput, nLen, delim, encl, escape, jx9CsvConsumer, pArray); - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * Extract a tag name from a raw HTML input and insert it in the given - * container. - * Refer to [strip_tags()]. - */ -static sxi32 AddTag(SySet *pSet, const char *zTag, int nByte) -{ - const char *zEnd = &zTag[nByte]; - const char *zPtr; - SyString sEntry; - /* Strip tags */ - for(;;){ - while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' - || zTag[0] == '!' || zTag[0] == '-' || ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){ - zTag++; - } - if( zTag >= zEnd ){ - break; - } - zPtr = zTag; - /* Delimit the tag */ - while(zTag < zEnd ){ - if( (unsigned char)zTag[0] >= 0xc0 ){ - /* UTF-8 stream */ - zTag++; - SX_JMP_UTF8(zTag, zEnd); - }else if( !SyisAlphaNum(zTag[0]) ){ - break; - }else{ - zTag++; - } - } - if( zTag > zPtr ){ - /* Perform the insertion */ - SyStringInitFromBuf(&sEntry, zPtr, (int)(zTag-zPtr)); - SyStringFullTrim(&sEntry); - SySetPut(pSet, (const void *)&sEntry); - } - /* Jump the trailing '>' */ - zTag++; - } - return SXRET_OK; -} -/* - * Check if the given HTML tag name is present in the given container. - * Return SXRET_OK if present.SXERR_NOTFOUND otherwise. - * Refer to [strip_tags()]. - */ -static sxi32 FindTag(SySet *pSet, const char *zTag, int nByte) -{ - if( SySetUsed(pSet) > 0 ){ - const char *zCur, *zEnd = &zTag[nByte]; - SyString sTag; - while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' || - ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){ - zTag++; - } - /* Delimit the tag */ - zCur = zTag; - while(zTag < zEnd ){ - if( (unsigned char)zTag[0] >= 0xc0 ){ - /* UTF-8 stream */ - zTag++; - SX_JMP_UTF8(zTag, zEnd); - }else if( !SyisAlphaNum(zTag[0]) ){ - break; - }else{ - zTag++; - } - } - SyStringInitFromBuf(&sTag, zCur, zTag-zCur); - /* Trim leading white spaces and null bytes */ - SyStringLeftTrimSafe(&sTag); - if( sTag.nByte > 0 ){ - SyString *aEntry, *pEntry; - sxi32 rc; - sxu32 n; - /* Perform the lookup */ - aEntry = (SyString *)SySetBasePtr(pSet); - for( n = 0 ; n < SySetUsed(pSet) ; ++n ){ - pEntry = &aEntry[n]; - /* Do the comparison */ - rc = SyStringCmp(pEntry, &sTag, SyStrnicmp); - if( !rc ){ - return SXRET_OK; - } - } - } - } - /* No such tag */ - return SXERR_NOTFOUND; -} -/* - * This function tries to return a string [i.e: in the call context result buffer] - * with all NUL bytes, HTML and JX9 tags stripped from a given string. - * Refer to [strip_tags()]. - */ -JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen) -{ - const char *zEnd = &zIn[nByte]; - const char *zPtr, *zTag; - SySet sSet; - /* initialize the set of allowed tags */ - SySetInit(&sSet, &pCtx->pVm->sAllocator, sizeof(SyString)); - if( nTaglen > 0 ){ - /* Set of allowed tags */ - AddTag(&sSet, zTaglist, nTaglen); - } - /* Set the empty string */ - jx9_result_string(pCtx, "", 0); - /* Start processing */ - for(;;){ - if(zIn >= zEnd){ - /* No more input to process */ - break; - } - zPtr = zIn; - /* Find a tag */ - while( zIn < zEnd && zIn[0] != '<' && zIn[0] != 0 /* NUL byte */ ){ - zIn++; - } - if( zIn > zPtr ){ - /* Consume raw input */ - jx9_result_string(pCtx, zPtr, (int)(zIn-zPtr)); - } - /* Ignore trailing null bytes */ - while( zIn < zEnd && zIn[0] == 0 ){ - zIn++; - } - if(zIn >= zEnd){ - /* No more input to process */ - break; - } - if( zIn[0] == '<' ){ - sxi32 rc; - zTag = zIn++; - /* Delimit the tag */ - while( zIn < zEnd && zIn[0] != '>' ){ - zIn++; - } - if( zIn < zEnd ){ - zIn++; /* Ignore the trailing closing tag */ - } - /* Query the set */ - rc = FindTag(&sSet, zTag, (int)(zIn-zTag)); - if( rc == SXRET_OK ){ - /* Keep the tag */ - jx9_result_string(pCtx, zTag, (int)(zIn-zTag)); - } - } - } - /* Cleanup */ - SySetRelease(&sSet); - return SXRET_OK; -} -/* - * string strip_tags(string $str[, string $allowable_tags]) - * Strip HTML and JX9 tags from a string. - * Parameters - * $str - * The input string. - * $allowable_tags - * You can use the optional second parameter to specify tags which should not be stripped. - * Return - * Returns the stripped string. - */ -static int jx9Builtin_strip_tags(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zTaglist = 0; - const char *zString; - int nTaglen = 0; - int nLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Point to the raw string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nArg > 1 && jx9_value_is_string(apArg[1]) ){ - /* Allowed tag */ - zTaglist = jx9_value_to_string(apArg[1], &nTaglen); - } - /* Process input */ - jx9StripTagsFromString(pCtx, zString, nLen, zTaglist, nTaglen); - return JX9_OK; -} -/* - * array str_split(string $string[, int $split_length = 1 ]) - * Convert a string to an array. - * Parameters - * $str - * The input string. - * $split_length - * Maximum length of the chunk. - * Return - * If the optional split_length parameter is specified, the returned array - * will be broken down into chunks with each being split_length in length, otherwise - * each chunk will be one character in length. FALSE is returned if split_length is less than 1. - * If the split_length length exceeds the length of string, the entire string is returned - * as the first (and only) array element. - */ -static int jx9Builtin_str_split(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString, *zEnd; - jx9_value *pArray, *pValue; - int split_len; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target string */ - zString = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Nothing to process, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - split_len = (int)sizeof(char); - if( nArg > 1 ){ - /* Split length */ - split_len = jx9_value_to_int(apArg[1]); - if( split_len < 1 ){ - /* Invalid length, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( split_len > nLen ){ - split_len = nLen; - } - } - /* Create the array and the scalar value */ - pArray = jx9_context_new_array(pCtx); - /*Chunk value */ - pValue = jx9_context_new_scalar(pCtx); - if( pValue == 0 || pArray == 0 ){ - /* Return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the end of the string */ - zEnd = &zString[nLen]; - /* Perform the requested operation */ - for(;;){ - int nMax; - if( zString >= zEnd ){ - /* No more input to process */ - break; - } - nMax = (int)(zEnd-zString); - if( nMax < split_len ){ - split_len = nMax; - } - /* Copy the current chunk */ - jx9_value_string(pValue, zString, split_len); - /* Insert it */ - jx9_array_add_elem(pArray, 0, pValue); /* Will make it's own copy */ - /* reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - /* Update position */ - zString += split_len; - } - /* - * Return the array. - * Don't worry about freeing memory, everything will be automatically released - * upon we return from this function. - */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * Tokenize a raw string and extract the first non-space token. - * Refer to [strspn()]. - */ -static sxi32 ExtractNonSpaceToken(const char **pzIn, const char *zEnd, SyString *pOut) -{ - const char *zIn = *pzIn; - const char *zPtr; - /* Ignore leading white spaces */ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ - zIn++; - } - if( zIn >= zEnd ){ - /* End of input */ - return SXERR_EOF; - } - zPtr = zIn; - /* Extract the token */ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && !SyisSpace(zIn[0]) ){ - zIn++; - } - SyStringInitFromBuf(pOut, zPtr, zIn-zPtr); - /* Synchronize pointers */ - *pzIn = zIn; - /* Return to the caller */ - return SXRET_OK; -} -/* - * Check if the given string contains only characters from the given mask. - * return the longest match. - * Refer to [strspn()]. - */ -static int LongestStringMask(const char *zString, int nLen, const char *zMask, int nMaskLen) -{ - const char *zEnd = &zString[nLen]; - const char *zIn = zString; - int i, c; - for(;;){ - if( zString >= zEnd ){ - break; - } - /* Extract current character */ - c = zString[0]; - /* Perform the lookup */ - for( i = 0 ; i < nMaskLen ; i++ ){ - if( c == zMask[i] ){ - /* Character found */ - break; - } - } - if( i >= nMaskLen ){ - /* Character not in the current mask, break immediately */ - break; - } - /* Advance cursor */ - zString++; - } - /* Longest match */ - return (int)(zString-zIn); -} -/* - * Do the reverse operation of the previous function [i.e: LongestStringMask()]. - * Refer to [strcspn()]. - */ -static int LongestStringMask2(const char *zString, int nLen, const char *zMask, int nMaskLen) -{ - const char *zEnd = &zString[nLen]; - const char *zIn = zString; - int i, c; - for(;;){ - if( zString >= zEnd ){ - break; - } - /* Extract current character */ - c = zString[0]; - /* Perform the lookup */ - for( i = 0 ; i < nMaskLen ; i++ ){ - if( c == zMask[i] ){ - break; - } - } - if( i < nMaskLen ){ - /* Character in the current mask, break immediately */ - break; - } - /* Advance cursor */ - zString++; - } - /* Longest match */ - return (int)(zString-zIn); -} -/* - * int strspn(string $str, string $mask[, int $start[, int $length]]) - * Finds the length of the initial segment of a string consisting entirely - * of characters contained within a given mask. - * Parameters - * $str - * The input string. - * $mask - * The list of allowable characters. - * $start - * The position in subject to start searching. - * If start is given and is non-negative, then strspn() will begin examining - * subject at the start'th position. For instance, in the string 'abcdef', the character - * at position 0 is 'a', the character at position 2 is 'c', and so forth. - * If start is given and is negative, then strspn() will begin examining subject at the - * start'th position from the end of subject. - * $length - * The length of the segment from subject to examine. - * If length is given and is non-negative, then subject will be examined for length - * characters after the starting position. - * If lengthis given and is negative, then subject will be examined from the starting - * position up to length characters from the end of subject. - * Return - * Returns the length of the initial segment of subject which consists entirely of characters - * in mask. - */ -static int jx9Builtin_strspn(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString, *zMask, *zEnd; - int iMasklen, iLen; - SyString sToken; - int iCount = 0; - int rc; - if( nArg < 2 ){ - /* Missing agruments, return zero */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zString = jx9_value_to_string(apArg[0], &iLen); - /* Extract the mask */ - zMask = jx9_value_to_string(apArg[1], &iMasklen); - if( iLen < 1 || iMasklen < 1 ){ - /* Nothing to process, return zero */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - if( nArg > 2 ){ - int nOfft; - /* Extract the offset */ - nOfft = jx9_value_to_int(apArg[2]); - if( nOfft < 0 ){ - const char *zBase = &zString[iLen + nOfft]; - if( zBase > zString ){ - iLen = (int)(&zString[iLen]-zBase); - zString = zBase; - }else{ - /* Invalid offset */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - }else{ - if( nOfft >= iLen ){ - /* Invalid offset */ - jx9_result_int(pCtx, 0); - return JX9_OK; - }else{ - /* Update offset */ - zString += nOfft; - iLen -= nOfft; - } - } - if( nArg > 3 ){ - int iUserlen; - /* Extract the desired length */ - iUserlen = jx9_value_to_int(apArg[3]); - if( iUserlen > 0 && iUserlen < iLen ){ - iLen = iUserlen; - } - } - } - /* Point to the end of the string */ - zEnd = &zString[iLen]; - /* Extract the first non-space token */ - rc = ExtractNonSpaceToken(&zString, zEnd, &sToken); - if( rc == SXRET_OK && sToken.nByte > 0 ){ - /* Compare against the current mask */ - iCount = LongestStringMask(sToken.zString, (int)sToken.nByte, zMask, iMasklen); - } - /* Longest match */ - jx9_result_int(pCtx, iCount); - return JX9_OK; -} -/* - * int strcspn(string $str, string $mask[, int $start[, int $length]]) - * Find length of initial segment not matching mask. - * Parameters - * $str - * The input string. - * $mask - * The list of not allowed characters. - * $start - * The position in subject to start searching. - * If start is given and is non-negative, then strspn() will begin examining - * subject at the start'th position. For instance, in the string 'abcdef', the character - * at position 0 is 'a', the character at position 2 is 'c', and so forth. - * If start is given and is negative, then strspn() will begin examining subject at the - * start'th position from the end of subject. - * $length - * The length of the segment from subject to examine. - * If length is given and is non-negative, then subject will be examined for length - * characters after the starting position. - * If lengthis given and is negative, then subject will be examined from the starting - * position up to length characters from the end of subject. - * Return - * Returns the length of the segment as an integer. - */ -static int jx9Builtin_strcspn(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString, *zMask, *zEnd; - int iMasklen, iLen; - SyString sToken; - int iCount = 0; - int rc; - if( nArg < 2 ){ - /* Missing agruments, return zero */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zString = jx9_value_to_string(apArg[0], &iLen); - /* Extract the mask */ - zMask = jx9_value_to_string(apArg[1], &iMasklen); - if( iLen < 1 ){ - /* Nothing to process, return zero */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - if( iMasklen < 1 ){ - /* No given mask, return the string length */ - jx9_result_int(pCtx, iLen); - return JX9_OK; - } - if( nArg > 2 ){ - int nOfft; - /* Extract the offset */ - nOfft = jx9_value_to_int(apArg[2]); - if( nOfft < 0 ){ - const char *zBase = &zString[iLen + nOfft]; - if( zBase > zString ){ - iLen = (int)(&zString[iLen]-zBase); - zString = zBase; - }else{ - /* Invalid offset */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - }else{ - if( nOfft >= iLen ){ - /* Invalid offset */ - jx9_result_int(pCtx, 0); - return JX9_OK; - }else{ - /* Update offset */ - zString += nOfft; - iLen -= nOfft; - } - } - if( nArg > 3 ){ - int iUserlen; - /* Extract the desired length */ - iUserlen = jx9_value_to_int(apArg[3]); - if( iUserlen > 0 && iUserlen < iLen ){ - iLen = iUserlen; - } - } - } - /* Point to the end of the string */ - zEnd = &zString[iLen]; - /* Extract the first non-space token */ - rc = ExtractNonSpaceToken(&zString, zEnd, &sToken); - if( rc == SXRET_OK && sToken.nByte > 0 ){ - /* Compare against the current mask */ - iCount = LongestStringMask2(sToken.zString, (int)sToken.nByte, zMask, iMasklen); - } - /* Longest match */ - jx9_result_int(pCtx, iCount); - return JX9_OK; -} -/* - * string strpbrk(string $haystack, string $char_list) - * Search a string for any of a set of characters. - * Parameters - * $haystack - * The string where char_list is looked for. - * $char_list - * This parameter is case sensitive. - * Return - * Returns a string starting from the character found, or FALSE if it is not found. - */ -static int jx9Builtin_strpbrk(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString, *zList, *zEnd; - int iLen, iListLen, i, c; - sxu32 nOfft, nMax; - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the haystack and the char list */ - zString = jx9_value_to_string(apArg[0], &iLen); - zList = jx9_value_to_string(apArg[1], &iListLen); - if( iLen < 1 ){ - /* Nothing to process, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the end of the string */ - zEnd = &zString[iLen]; - nOfft = nMax = SXU32_HIGH; - /* perform the requested operation */ - for( i = 0 ; i < iListLen ; i++ ){ - c = zList[i]; - rc = SyByteFind(zString, (sxu32)iLen, c, &nMax); - if( rc == SXRET_OK ){ - if( nMax < nOfft ){ - nOfft = nMax; - } - } - } - if( nOfft == SXU32_HIGH ){ - /* No such substring, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* Return the substring */ - jx9_result_string(pCtx, &zString[nOfft], (int)(zEnd-&zString[nOfft])); - } - return JX9_OK; -} -/* - * string soundex(string $str) - * Calculate the soundex key of a string. - * Parameters - * $str - * The input string. - * Return - * Returns the soundex key as a string. - * Note: - * This implementation is based on the one found in the SQLite3 - * source tree. - */ -static int jx9Builtin_soundex(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn; - char zResult[8]; - int i, j; - static const unsigned char iCode[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, - 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, - 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, - }; - if( nArg < 1 ){ - /* Missing arguments, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - zIn = (unsigned char *)jx9_value_to_string(apArg[0], 0); - for(i=0; zIn[i] && zIn[i] < 0xc0 && !SyisAlpha(zIn[i]); i++){} - if( zIn[i] ){ - unsigned char prevcode = iCode[zIn[i]&0x7f]; - zResult[0] = (char)SyToUpper(zIn[i]); - for(j=1; j<4 && zIn[i]; i++){ - int code = iCode[zIn[i]&0x7f]; - if( code>0 ){ - if( code!=prevcode ){ - prevcode = (unsigned char)code; - zResult[j++] = (char)code + '0'; - } - }else{ - prevcode = 0; - } - } - while( j<4 ){ - zResult[j++] = '0'; - } - jx9_result_string(pCtx, zResult, 4); - }else{ - jx9_result_string(pCtx, "?000", 4); - } - return JX9_OK; -} -/* - * string wordwrap(string $str[, int $width = 75[, string $break = "\n"]]) - * Wraps a string to a given number of characters. - * Parameters - * $str - * The input string. - * $width - * The column width. - * $break - * The line is broken using the optional break parameter. - * Return - * Returns the given string wrapped at the specified column. - */ -static int jx9Builtin_wordwrap(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn, *zEnd, *zBreak; - int iLen, iBreaklen, iChunk; - if( nArg < 1 ){ - /* Missing arguments, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Extract the input string */ - zIn = jx9_value_to_string(apArg[0], &iLen); - if( iLen < 1 ){ - /* Nothing to process, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Chunk length */ - iChunk = 75; - iBreaklen = 0; - zBreak = ""; /* cc warning */ - if( nArg > 1 ){ - iChunk = jx9_value_to_int(apArg[1]); - if( iChunk < 1 ){ - iChunk = 75; - } - if( nArg > 2 ){ - zBreak = jx9_value_to_string(apArg[2], &iBreaklen); - } - } - if( iBreaklen < 1 ){ - /* Set a default column break */ -#ifdef __WINNT__ - zBreak = "\r\n"; - iBreaklen = (int)sizeof("\r\n")-1; -#else - zBreak = "\n"; - iBreaklen = (int)sizeof(char); -#endif - } - /* Perform the requested operation */ - zEnd = &zIn[iLen]; - for(;;){ - int nMax; - if( zIn >= zEnd ){ - /* No more input to process */ - break; - } - nMax = (int)(zEnd-zIn); - if( iChunk > nMax ){ - iChunk = nMax; - } - /* Append the column first */ - jx9_result_string(pCtx, zIn, iChunk); /* Will make it's own copy */ - /* Advance the cursor */ - zIn += iChunk; - if( zIn < zEnd ){ - /* Append the line break */ - jx9_result_string(pCtx, zBreak, iBreaklen); - } - } - return JX9_OK; -} -/* - * Check if the given character is a member of the given mask. - * Return TRUE on success. FALSE otherwise. - * Refer to [strtok()]. - */ -static int CheckMask(int c, const char *zMask, int nMasklen, int *pOfft) -{ - int i; - for( i = 0 ; i < nMasklen ; ++i ){ - if( c == zMask[i] ){ - if( pOfft ){ - *pOfft = i; - } - return TRUE; - } - } - return FALSE; -} -/* - * Extract a single token from the input stream. - * Refer to [strtok()]. - */ -static sxi32 ExtractToken(const char **pzIn, const char *zEnd, const char *zMask, int nMasklen, SyString *pOut) -{ - const char *zIn = *pzIn; - const char *zPtr; - /* Ignore leading delimiter */ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && CheckMask(zIn[0], zMask, nMasklen, 0) ){ - zIn++; - } - if( zIn >= zEnd ){ - /* End of input */ - return SXERR_EOF; - } - zPtr = zIn; - /* Extract the token */ - while( zIn < zEnd ){ - if( (unsigned char)zIn[0] >= 0xc0 ){ - /* UTF-8 stream */ - zIn++; - SX_JMP_UTF8(zIn, zEnd); - }else{ - if( CheckMask(zIn[0], zMask, nMasklen, 0) ){ - break; - } - zIn++; - } - } - SyStringInitFromBuf(pOut, zPtr, zIn-zPtr); - /* Update the cursor */ - *pzIn = zIn; - /* Return to the caller */ - return SXRET_OK; -} -/* strtok auxiliary private data */ -typedef struct strtok_aux_data strtok_aux_data; -struct strtok_aux_data -{ - const char *zDup; /* Complete duplicate of the input */ - const char *zIn; /* Current input stream */ - const char *zEnd; /* End of input */ -}; -/* - * string strtok(string $str, string $token) - * string strtok(string $token) - * strtok() splits a string (str) into smaller strings (tokens), with each token - * being delimited by any character from token. That is, if you have a string like - * "This is an example string" you could tokenize this string into its individual - * words by using the space character as the token. - * Note that only the first call to strtok uses the string argument. Every subsequent - * call to strtok only needs the token to use, as it keeps track of where it is in - * the current string. To start over, or to tokenize a new string you simply call strtok - * with the string argument again to initialize it. Note that you may put multiple tokens - * in the token parameter. The string will be tokenized when any one of the characters in - * the argument are found. - * Parameters - * $str - * The string being split up into smaller strings (tokens). - * $token - * The delimiter used when splitting up str. - * Return - * Current token or FALSE on EOF. - */ -static int jx9Builtin_strtok(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - strtok_aux_data *pAux; - const char *zMask; - SyString sToken; - int nMasklen; - sxi32 rc; - if( nArg < 2 ){ - /* Extract top aux data */ - pAux = (strtok_aux_data *)jx9_context_peek_aux_data(pCtx); - if( pAux == 0 ){ - /* No aux data, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - nMasklen = 0; - zMask = ""; /* cc warning */ - if( nArg > 0 ){ - /* Extract the mask */ - zMask = jx9_value_to_string(apArg[0], &nMasklen); - } - if( nMasklen < 1 ){ - /* Invalid mask, return FALSE */ - jx9_context_free_chunk(pCtx, (void *)pAux->zDup); - jx9_context_free_chunk(pCtx, pAux); - (void)jx9_context_pop_aux_data(pCtx); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the token */ - rc = ExtractToken(&pAux->zIn, pAux->zEnd, zMask, nMasklen, &sToken); - if( rc != SXRET_OK ){ - /* EOF , discard the aux data */ - jx9_context_free_chunk(pCtx, (void *)pAux->zDup); - jx9_context_free_chunk(pCtx, pAux); - (void)jx9_context_pop_aux_data(pCtx); - jx9_result_bool(pCtx, 0); - }else{ - /* Return the extracted token */ - jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte); - } - }else{ - const char *zInput, *zCur; - char *zDup; - int nLen; - /* Extract the raw input */ - zCur = zInput = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Empty input, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the mask */ - zMask = jx9_value_to_string(apArg[1], &nMasklen); - if( nMasklen < 1 ){ - /* Set a default mask */ -#define TOK_MASK " \n\t\r\f" - zMask = TOK_MASK; - nMasklen = (int)sizeof(TOK_MASK) - 1; -#undef TOK_MASK - } - /* Extract a single token */ - rc = ExtractToken(&zInput, &zInput[nLen], zMask, nMasklen, &sToken); - if( rc != SXRET_OK ){ - /* Empty input */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - }else{ - /* Return the extracted token */ - jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte); - } - /* Create our auxilliary data and copy the input */ - pAux = (strtok_aux_data *)jx9_context_alloc_chunk(pCtx, sizeof(strtok_aux_data), TRUE, FALSE); - if( pAux ){ - nLen -= (int)(zInput-zCur); - if( nLen < 1 ){ - jx9_context_free_chunk(pCtx, pAux); - return JX9_OK; - } - /* Duplicate input */ - zDup = (char *)jx9_context_alloc_chunk(pCtx, (unsigned int)(nLen+1), TRUE, FALSE); - if( zDup ){ - SyMemcpy(zInput, zDup, (sxu32)nLen); - /* Register the aux data */ - pAux->zDup = pAux->zIn = zDup; - pAux->zEnd = &zDup[nLen]; - jx9_context_push_aux_data(pCtx, pAux); - } - } - } - return JX9_OK; -} -/* - * string str_pad(string $input, int $pad_length[, string $pad_string = " " [, int $pad_type = STR_PAD_RIGHT]]) - * Pad a string to a certain length with another string - * Parameters - * $input - * The input string. - * $pad_length - * If the value of pad_length is negative, less than, or equal to the length of the input - * string, no padding takes place. - * $pad_string - * Note: - * The pad_string WIIL NOT BE truncated if the required number of padding characters can't be evenly - * divided by the pad_string's length. - * $pad_type - * Optional argument pad_type can be STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH. If pad_type - * is not specified it is assumed to be STR_PAD_RIGHT. - * Return - * The padded string. - */ -static int jx9Builtin_str_pad(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int iLen, iPadlen, iType, i, iDiv, iStrpad, iRealPad, jPad; - const char *zIn, *zPad; - if( nArg < 2 ){ - /* Missing arguments, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = jx9_value_to_string(apArg[0], &iLen); - /* Padding length */ - iRealPad = iPadlen = jx9_value_to_int(apArg[1]); - if( iPadlen > 0 ){ - iPadlen -= iLen; - } - if( iPadlen < 1 ){ - /* Return the string verbatim */ - jx9_result_string(pCtx, zIn, iLen); - return JX9_OK; - } - zPad = " "; /* Whitespace padding */ - iStrpad = (int)sizeof(char); - iType = 1 ; /* STR_PAD_RIGHT */ - if( nArg > 2 ){ - /* Padding string */ - zPad = jx9_value_to_string(apArg[2], &iStrpad); - if( iStrpad < 1 ){ - /* Empty string */ - zPad = " "; /* Whitespace padding */ - iStrpad = (int)sizeof(char); - } - if( nArg > 3 ){ - /* Padd type */ - iType = jx9_value_to_int(apArg[3]); - if( iType != 0 /* STR_PAD_LEFT */ && iType != 2 /* STR_PAD_BOTH */ ){ - iType = 1 ; /* STR_PAD_RIGHT */ - } - } - } - iDiv = 1; - if( iType == 2 ){ - iDiv = 2; /* STR_PAD_BOTH */ - } - /* Perform the requested operation */ - if( iType == 0 /* STR_PAD_LEFT */ || iType == 2 /* STR_PAD_BOTH */ ){ - jPad = iStrpad; - for( i = 0 ; i < iPadlen/iDiv ; i += jPad ){ - /* Padding */ - if( (int)jx9_context_result_buf_length(pCtx) + iLen + jPad >= iRealPad ){ - break; - } - jx9_result_string(pCtx, zPad, jPad); - } - if( iType == 0 /* STR_PAD_LEFT */ ){ - while( (int)jx9_context_result_buf_length(pCtx) + iLen < iRealPad ){ - jPad = iRealPad - (iLen + (int)jx9_context_result_buf_length(pCtx) ); - if( jPad > iStrpad ){ - jPad = iStrpad; - } - if( jPad < 1){ - break; - } - jx9_result_string(pCtx, zPad, jPad); - } - } - } - if( iLen > 0 ){ - /* Append the input string */ - jx9_result_string(pCtx, zIn, iLen); - } - if( iType == 1 /* STR_PAD_RIGHT */ || iType == 2 /* STR_PAD_BOTH */ ){ - for( i = 0 ; i < iPadlen/iDiv ; i += iStrpad ){ - /* Padding */ - if( (int)jx9_context_result_buf_length(pCtx) + iStrpad >= iRealPad ){ - break; - } - jx9_result_string(pCtx, zPad, iStrpad); - } - while( (int)jx9_context_result_buf_length(pCtx) < iRealPad ){ - jPad = iRealPad - (int)jx9_context_result_buf_length(pCtx); - if( jPad > iStrpad ){ - jPad = iStrpad; - } - if( jPad < 1){ - break; - } - jx9_result_string(pCtx, zPad, jPad); - } - } - return JX9_OK; -} -/* - * String replacement private data. - */ -typedef struct str_replace_data str_replace_data; -struct str_replace_data -{ - /* The following two fields are only used by the strtr function */ - SyBlob *pWorker; /* Working buffer */ - ProcStringMatch xMatch; /* Pattern match routine */ - /* The following two fields are only used by the str_replace function */ - SySet *pCollector; /* Argument collector*/ - jx9_context *pCtx; /* Call context */ -}; -/* - * Remove a substring. - */ -#define STRDEL(SRC, SLEN, OFFT, ILEN){\ - for(;;){\ - if( OFFT + ILEN >= SLEN ) break; SRC[OFFT] = SRC[OFFT+ILEN]; ++OFFT;\ - }\ -} -/* - * Shift right and insert algorithm. - */ -#define SHIFTRANDINSERT(SRC, LEN, OFFT, ENTRY, ELEN){\ - sxu32 INLEN = LEN - OFFT;\ - for(;;){\ - if( LEN > 0 ){ LEN--; } if(INLEN < 1 ) break; SRC[LEN + ELEN] = SRC[LEN] ; --INLEN; \ - }\ - for(;;){\ - if(ELEN < 1)break; SRC[OFFT] = ENTRY[0]; OFFT++; ENTRY++; --ELEN;\ - }\ -} -/* - * Replace all occurrences of the search string at offset (nOfft) with the given - * replacement string [i.e: zReplace]. - */ -static int StringReplace(SyBlob *pWorker, sxu32 nOfft, int nLen, const char *zReplace, int nReplen) -{ - char *zInput = (char *)SyBlobData(pWorker); - sxu32 n, m; - n = SyBlobLength(pWorker); - m = nOfft; - /* Delete the old entry */ - STRDEL(zInput, n, m, nLen); - SyBlobLength(pWorker) -= nLen; - if( nReplen > 0 ){ - sxi32 iRep = nReplen; - sxi32 rc; - /* - * Make sure the working buffer is big enough to hold the replacement - * string. - */ - rc = SyBlobAppend(pWorker, 0/* Grow without an append operation*/, (sxu32)nReplen); - if( rc != SXRET_OK ){ - /* Simply ignore any memory failure problem */ - return SXRET_OK; - } - /* Perform the insertion now */ - zInput = (char *)SyBlobData(pWorker); - n = SyBlobLength(pWorker); - SHIFTRANDINSERT(zInput, n, nOfft, zReplace, iRep); - SyBlobLength(pWorker) += nReplen; - } - return SXRET_OK; -} -/* - * String replacement walker callback. - * The following callback is invoked for each array entry that hold - * the replace string. - * Refer to the strtr() implementation for more information. - */ -static int StringReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData) -{ - str_replace_data *pRepData = (str_replace_data *)pUserData; - const char *zTarget, *zReplace; - SyBlob *pWorker; - int tLen, nLen; - sxu32 nOfft; - sxi32 rc; - /* Point to the working buffer */ - pWorker = pRepData->pWorker; - if( !jx9_value_is_string(pKey) ){ - /* Target and replace must be a string */ - return JX9_OK; - } - /* Extract the target and the replace */ - zTarget = jx9_value_to_string(pKey, &tLen); - if( tLen < 1 ){ - /* Empty target, return immediately */ - return JX9_OK; - } - /* Perform a pattern search */ - rc = pRepData->xMatch(SyBlobData(pWorker), SyBlobLength(pWorker), (const void *)zTarget, (sxu32)tLen, &nOfft); - if( rc != SXRET_OK ){ - /* Pattern not found */ - return JX9_OK; - } - /* Extract the replace string */ - zReplace = jx9_value_to_string(pData, &nLen); - /* Perform the replace process */ - StringReplace(pWorker, nOfft, tLen, zReplace, nLen); - /* All done */ - return JX9_OK; -} -/* - * The following walker callback is invoked by the str_rplace() function inorder - * to collect search/replace string. - * This callback is invoked only if the given argument is of type array. - */ -static int StrReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData) -{ - str_replace_data *pRep = (str_replace_data *)pUserData; - SyString sWorker; - const char *zIn; - int nByte; - /* Extract a string representation of the given argument */ - zIn = jx9_value_to_string(pData, &nByte); - SyStringInitFromBuf(&sWorker, 0, 0); - if( nByte > 0 ){ - char *zDup; - /* Duplicate the chunk */ - zDup = (char *)jx9_context_alloc_chunk(pRep->pCtx, (unsigned int)nByte, FALSE, - TRUE /* Release the chunk automatically, upon this context is destroyd */ - ); - if( zDup == 0 ){ - /* Ignore any memory failure problem */ - jx9_context_throw_error(pRep->pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - return JX9_OK; - } - SyMemcpy(zIn, zDup, (sxu32)nByte); - /* Save the chunk */ - SyStringInitFromBuf(&sWorker, zDup, nByte); - } - /* Save for later processing */ - SySetPut(pRep->pCollector, (const void *)&sWorker); - /* All done */ - SXUNUSED(pKey); /* cc warning */ - return JX9_OK; -} -/* - * mixed str_replace(mixed $search, mixed $replace, mixed $subject[, int &$count ]) - * mixed str_ireplace(mixed $search, mixed $replace, mixed $subject[, int &$count ]) - * Replace all occurrences of the search string with the replacement string. - * Parameters - * If search and replace are arrays, then str_replace() takes a value from each - * array and uses them to search and replace on subject. If replace has fewer values - * than search, then an empty string is used for the rest of replacement values. - * If search is an array and replace is a string, then this replacement string is used - * for every value of search. The converse would not make sense, though. - * If search or replace are arrays, their elements are processed first to last. - * $search - * The value being searched for, otherwise known as the needle. An array may be used - * to designate multiple needles. - * $replace - * The replacement value that replaces found search values. An array may be used - * to designate multiple replacements. - * $subject - * The string or array being searched and replaced on, otherwise known as the haystack. - * If subject is an array, then the search and replace is performed with every entry - * of subject, and the return value is an array as well. - * $count (Not used) - * If passed, this will be set to the number of replacements performed. - * Return - * This function returns a string or an array with the replaced values. - */ -static int jx9Builtin_str_replace(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyString sTemp, *pSearch, *pReplace; - ProcStringMatch xMatch; - const char *zIn, *zFunc; - str_replace_data sRep; - SyBlob sWorker; - SySet sReplace; - SySet sSearch; - int rep_str; - int nByte; - sxi32 rc; - if( nArg < 3 ){ - /* Missing/Invalid arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Initialize fields */ - SySetInit(&sSearch, &pCtx->pVm->sAllocator, sizeof(SyString)); - SySetInit(&sReplace, &pCtx->pVm->sAllocator, sizeof(SyString)); - SyBlobInit(&sWorker, &pCtx->pVm->sAllocator); - SyZero(&sRep, sizeof(str_replace_data)); - sRep.pCtx = pCtx; - sRep.pCollector = &sSearch; - rep_str = 0; - /* Extract the subject */ - zIn = jx9_value_to_string(apArg[2], &nByte); - if( nByte < 1 ){ - /* Nothing to replace, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Copy the subject */ - SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nByte); - /* Search string */ - if( jx9_value_is_json_array(apArg[0]) ){ - /* Collect search string */ - jx9_array_walk(apArg[0], StrReplaceWalker, &sRep); - }else{ - /* Single pattern */ - zIn = jx9_value_to_string(apArg[0], &nByte); - if( nByte < 1 ){ - /* Return the subject untouched since no search string is available */ - jx9_result_value(pCtx, apArg[2]/* Subject as thrird argument*/); - return JX9_OK; - } - SyStringInitFromBuf(&sTemp, zIn, nByte); - /* Save for later processing */ - SySetPut(&sSearch, (const void *)&sTemp); - } - /* Replace string */ - if( jx9_value_is_json_array(apArg[1]) ){ - /* Collect replace string */ - sRep.pCollector = &sReplace; - jx9_array_walk(apArg[1], StrReplaceWalker, &sRep); - }else{ - /* Single needle */ - zIn = jx9_value_to_string(apArg[1], &nByte); - rep_str = 1; - SyStringInitFromBuf(&sTemp, zIn, nByte); - /* Save for later processing */ - SySetPut(&sReplace, (const void *)&sTemp); - } - /* Reset loop cursors */ - SySetResetCursor(&sSearch); - SySetResetCursor(&sReplace); - pReplace = pSearch = 0; /* cc warning */ - SyStringInitFromBuf(&sTemp, "", 0); - /* Extract function name */ - zFunc = jx9_function_name(pCtx); - /* Set the default pattern match routine */ - xMatch = SyBlobSearch; - if( SyStrncmp(zFunc, "str_ireplace", sizeof("str_ireplace") - 1) == 0 ){ - /* Case insensitive pattern match */ - xMatch = iPatternMatch; - } - /* Start the replace process */ - while( SXRET_OK == SySetGetNextEntry(&sSearch, (void **)&pSearch) ){ - sxu32 nCount, nOfft; - if( pSearch->nByte < 1 ){ - /* Empty string, ignore */ - continue; - } - /* Extract the replace string */ - if( rep_str ){ - pReplace = (SyString *)SySetPeek(&sReplace); - }else{ - if( SXRET_OK != SySetGetNextEntry(&sReplace, (void **)&pReplace) ){ - /* Sepecial case when 'replace set' has fewer values than the search set. - * An empty string is used for the rest of replacement values - */ - pReplace = 0; - } - } - if( pReplace == 0 ){ - /* Use an empty string instead */ - pReplace = &sTemp; - } - nOfft = nCount = 0; - for(;;){ - if( nCount >= SyBlobLength(&sWorker) ){ - break; - } - /* Perform a pattern lookup */ - rc = xMatch(SyBlobDataAt(&sWorker, nCount), SyBlobLength(&sWorker) - nCount, (const void *)pSearch->zString, - pSearch->nByte, &nOfft); - if( rc != SXRET_OK ){ - /* Pattern not found */ - break; - } - /* Perform the replace operation */ - StringReplace(&sWorker, nCount+nOfft, (int)pSearch->nByte, pReplace->zString, (int)pReplace->nByte); - /* Increment offset counter */ - nCount += nOfft + pReplace->nByte; - } - } - /* All done, clean-up the mess left behind */ - jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), (int)SyBlobLength(&sWorker)); - SySetRelease(&sSearch); - SySetRelease(&sReplace); - SyBlobRelease(&sWorker); - return JX9_OK; -} -/* - * string strtr(string $str, string $from, string $to) - * string strtr(string $str, array $replace_pairs) - * Translate characters or replace substrings. - * Parameters - * $str - * The string being translated. - * $from - * The string being translated to to. - * $to - * The string replacing from. - * $replace_pairs - * The replace_pairs parameter may be used instead of to and - * from, in which case it's an array in the form array('from' => 'to', ...). - * Return - * The translated string. - * If replace_pairs contains a key which is an empty string (""), FALSE will be returned. - */ -static int jx9Builtin_strtr(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn; - int nLen; - if( nArg < 1 ){ - /* Nothing to replace, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - zIn = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 || nArg < 2 ){ - /* Invalid arguments */ - jx9_result_string(pCtx, zIn, nLen); - return JX9_OK; - } - if( nArg == 2 && jx9_value_is_json_array(apArg[1]) ){ - str_replace_data sRepData; - SyBlob sWorker; - /* Initilaize the working buffer */ - SyBlobInit(&sWorker, &pCtx->pVm->sAllocator); - /* Copy raw string */ - SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nLen); - /* Init our replace data instance */ - sRepData.pWorker = &sWorker; - sRepData.xMatch = SyBlobSearch; - /* Iterate throw array entries and perform the replace operation.*/ - jx9_array_walk(apArg[1], StringReplaceWalker, &sRepData); - /* All done, return the result string */ - jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), - (int)SyBlobLength(&sWorker)); /* Will make it's own copy */ - /* Clean-up */ - SyBlobRelease(&sWorker); - }else{ - int i, flen, tlen, c, iOfft; - const char *zFrom, *zTo; - if( nArg < 3 ){ - /* Nothing to replace */ - jx9_result_string(pCtx, zIn, nLen); - return JX9_OK; - } - /* Extract given arguments */ - zFrom = jx9_value_to_string(apArg[1], &flen); - zTo = jx9_value_to_string(apArg[2], &tlen); - if( flen < 1 || tlen < 1 ){ - /* Nothing to replace */ - jx9_result_string(pCtx, zIn, nLen); - return JX9_OK; - } - /* Start the replace process */ - for( i = 0 ; i < nLen ; ++i ){ - c = zIn[i]; - if( CheckMask(c, zFrom, flen, &iOfft) ){ - if ( iOfft < tlen ){ - c = zTo[iOfft]; - } - } - jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - - } - } - return JX9_OK; -} -/* - * Parse an INI string. - * According to wikipedia - * The INI file format is an informal standard for configuration files for some platforms or software. - * INI files are simple text files with a basic structure composed of "sections" and "properties". - * Format -* Properties -* The basic element contained in an INI file is the property. Every property has a name and a value -* delimited by an equals sign (=). The name appears to the left of the equals sign. -* Example: -* name=value -* Sections -* Properties may be grouped into arbitrarily named sections. The section name appears on a line by itself -* in square brackets ([ and ]). All properties after the section declaration are associated with that section. -* There is no explicit "end of section" delimiter; sections end at the next section declaration -* or the end of the file. Sections may not be nested. -* Example: -* [section] -* Comments -* Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored. -* This function return an array holding parsed values on success.FALSE otherwise. -*/ -JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection) -{ - jx9_value *pCur, *pArray, *pSection, *pWorker, *pValue; - const char *zCur, *zEnd = &zIn[nByte]; - SyHashEntry *pEntry; - SyString sEntry; - SyHash sHash; - int c; - /* Create an empty array and worker variables */ - pArray = jx9_context_new_array(pCtx); - pWorker = jx9_context_new_scalar(pCtx); - pValue = jx9_context_new_scalar(pCtx); - if( pArray == 0 || pWorker == 0 || pValue == 0){ - /* Out of memory */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - /* Return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - SyHashInit(&sHash, &pCtx->pVm->sAllocator, 0, 0); - pCur = pArray; - /* Start the parse process */ - for(;;){ - /* Ignore leading white spaces */ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0])){ - zIn++; - } - if( zIn >= zEnd ){ - /* No more input to process */ - break; - } - if( zIn[0] == ';' || zIn[0] == '#' ){ - /* Comment til the end of line */ - zIn++; - while(zIn < zEnd && zIn[0] != '\n' ){ - zIn++; - } - continue; - } - /* Reset the string cursor of the working variable */ - jx9_value_reset_string_cursor(pWorker); - if( zIn[0] == '[' ){ - /* Section: Extract the section name */ - zIn++; - zCur = zIn; - while( zIn < zEnd && zIn[0] != ']' ){ - zIn++; - } - if( zIn > zCur && bProcessSection ){ - /* Save the section name */ - SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur)); - SyStringFullTrim(&sEntry); - jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte); - if( sEntry.nByte > 0 ){ - /* Associate an array with the section */ - pSection = jx9_context_new_array(pCtx); - if( pSection ){ - jx9_array_add_elem(pArray, pWorker/*Section name*/, pSection); - pCur = pSection; - } - } - } - zIn++; /* Trailing square brackets ']' */ - }else{ - jx9_value *pOldCur; - int is_array; - int iLen; - /* Properties */ - is_array = 0; - zCur = zIn; - iLen = 0; /* cc warning */ - pOldCur = pCur; - while( zIn < zEnd && zIn[0] != '=' ){ - if( zIn[0] == '[' && !is_array ){ - /* Array */ - iLen = (int)(zIn-zCur); - is_array = 1; - if( iLen > 0 ){ - jx9_value *pvArr = 0; /* cc warning */ - /* Query the hashtable */ - SyStringInitFromBuf(&sEntry, zCur, iLen); - SyStringFullTrim(&sEntry); - pEntry = SyHashGet(&sHash, (const void *)sEntry.zString, sEntry.nByte); - if( pEntry ){ - pvArr = (jx9_value *)SyHashEntryGetUserData(pEntry); - }else{ - /* Create an empty array */ - pvArr = jx9_context_new_array(pCtx); - if( pvArr ){ - /* Save the entry */ - SyHashInsert(&sHash, (const void *)sEntry.zString, sEntry.nByte, pvArr); - /* Insert the entry */ - jx9_value_reset_string_cursor(pWorker); - jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte); - jx9_array_add_elem(pCur, pWorker, pvArr); - jx9_value_reset_string_cursor(pWorker); - } - } - if( pvArr ){ - pCur = pvArr; - } - } - while ( zIn < zEnd && zIn[0] != ']' ){ - zIn++; - } - } - zIn++; - } - if( !is_array ){ - iLen = (int)(zIn-zCur); - } - /* Trim the key */ - SyStringInitFromBuf(&sEntry, zCur, iLen); - SyStringFullTrim(&sEntry); - if( sEntry.nByte > 0 ){ - if( !is_array ){ - /* Save the key name */ - jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte); - } - /* extract key value */ - jx9_value_reset_string_cursor(pValue); - zIn++; /* '=' */ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ - zIn++; - } - if( zIn < zEnd ){ - zCur = zIn; - c = zIn[0]; - if( c == '"' || c == '\'' ){ - zIn++; - /* Delimit the value */ - while( zIn < zEnd ){ - if ( zIn[0] == c && zIn[-1] != '\\' ){ - break; - } - zIn++; - } - if( zIn < zEnd ){ - zIn++; - } - }else{ - while( zIn < zEnd ){ - if( zIn[0] == '\n' ){ - if( zIn[-1] != '\\' ){ - break; - } - }else if( zIn[0] == ';' || zIn[0] == '#' ){ - /* Inline comments */ - break; - } - zIn++; - } - } - /* Trim the value */ - SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur)); - SyStringFullTrim(&sEntry); - if( c == '"' || c == '\'' ){ - SyStringTrimLeadingChar(&sEntry, c); - SyStringTrimTrailingChar(&sEntry, c); - } - if( sEntry.nByte > 0 ){ - jx9_value_string(pValue, sEntry.zString, (int)sEntry.nByte); - } - /* Insert the key and it's value */ - jx9_array_add_elem(pCur, is_array ? 0 /*Automatic index assign */: pWorker, pValue); - } - }else{ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && ( SyisSpace(zIn[0]) || zIn[0] == '=' ) ){ - zIn++; - } - } - pCur = pOldCur; - } - } - SyHashRelease(&sHash); - /* Return the parse of the INI string */ - jx9_result_value(pCtx, pArray); - return SXRET_OK; -} -/* - * array parse_ini_string(string $ini[, bool $process_sections = false[, int $scanner_mode = INI_SCANNER_NORMAL ]]) - * Parse a configuration string. - * Parameters - * $ini - * The contents of the ini file being parsed. - * $process_sections - * By setting the process_sections parameter to TRUE, you get a multidimensional array, with the section names - * and settings included. The default for process_sections is FALSE. - * $scanner_mode (Not used) - * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. If INI_SCANNER_RAW is supplied - * then option values will not be parsed. - * Return - * The settings are returned as an associative array on success, and FALSE on failure. - */ -static int jx9Builtin_parse_ini_string(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIni; - int nByte; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE*/ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the raw INI buffer */ - zIni = jx9_value_to_string(apArg[0], &nByte); - /* Process the INI buffer*/ - jx9ParseIniString(pCtx, zIni, (sxu32)nByte, (nArg > 1) ? jx9_value_to_bool(apArg[1]) : 0); - return JX9_OK; -} -/* - * Ctype Functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * bool ctype_alnum(string $text) - * Checks if all of the characters in the provided string, text, are alphanumeric. - * Parameters - * $text - * The tested string. - * Return - * TRUE if every character in text is either a letter or a digit, FALSE otherwise. - */ -static int jx9Builtin_ctype_alnum(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( !SyisAlphaNum(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * bool ctype_alpha(string $text) - * Checks if all of the characters in the provided string, text, are alphabetic. - * Parameters - * $text - * The tested string. - * Return - * TRUE if every character in text is a letter from the current locale, FALSE otherwise. - */ -static int jx9Builtin_ctype_alpha(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( !SyisAlpha(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * bool ctype_cntrl(string $text) - * Checks if all of the characters in the provided string, text, are control characters. - * Parameters - * $text - * The tested string. - * Return - * TRUE if every character in text is a control characters, FALSE otherwise. - */ -static int jx9Builtin_ctype_cntrl(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( zIn[0] >= 0xc0 ){ - /* UTF-8 stream */ - break; - } - if( !SyisCtrl(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * bool ctype_digit(string $text) - * Checks if all of the characters in the provided string, text, are numerical. - * Parameters - * $text - * The tested string. - * Return - * TRUE if every character in the string text is a decimal digit, FALSE otherwise. - */ -static int jx9Builtin_ctype_digit(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( zIn[0] >= 0xc0 ){ - /* UTF-8 stream */ - break; - } - if( !SyisDigit(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * bool ctype_xdigit(string $text) - * Check for character(s) representing a hexadecimal digit. - * Parameters - * $text - * The tested string. - * Return - * Returns TRUE if every character in text is a hexadecimal 'digit', that is - * a decimal digit or a character from [A-Fa-f] , FALSE otherwise. - */ -static int jx9Builtin_ctype_xdigit(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( zIn[0] >= 0xc0 ){ - /* UTF-8 stream */ - break; - } - if( !SyisHex(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * bool ctype_graph(string $text) - * Checks if all of the characters in the provided string, text, creates visible output. - * Parameters - * $text - * The tested string. - * Return - * Returns TRUE if every character in text is printable and actually creates visible output - * (no white space), FALSE otherwise. - */ -static int jx9Builtin_ctype_graph(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( zIn[0] >= 0xc0 ){ - /* UTF-8 stream */ - break; - } - if( !SyisGraph(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * bool ctype_print(string $text) - * Checks if all of the characters in the provided string, text, are printable. - * Parameters - * $text - * The tested string. - * Return - * Returns TRUE if every character in text will actually create output (including blanks). - * Returns FALSE if text contains control characters or characters that do not have any output - * or control function at all. - */ -static int jx9Builtin_ctype_print(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( zIn[0] >= 0xc0 ){ - /* UTF-8 stream */ - break; - } - if( !SyisPrint(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * bool ctype_punct(string $text) - * Checks if all of the characters in the provided string, text, are punctuation character. - * Parameters - * $text - * The tested string. - * Return - * Returns TRUE if every character in text is printable, but neither letter - * digit or blank, FALSE otherwise. - */ -static int jx9Builtin_ctype_punct(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( zIn[0] >= 0xc0 ){ - /* UTF-8 stream */ - break; - } - if( !SyisPunct(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * bool ctype_space(string $text) - * Checks if all of the characters in the provided string, text, creates whitespace. - * Parameters - * $text - * The tested string. - * Return - * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. - * Besides the blank character this also includes tab, vertical tab, line feed, carriage return - * and form feed characters. - */ -static int jx9Builtin_ctype_space(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( zIn[0] >= 0xc0 ){ - /* UTF-8 stream */ - break; - } - if( !SyisSpace(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * bool ctype_lower(string $text) - * Checks if all of the characters in the provided string, text, are lowercase letters. - * Parameters - * $text - * The tested string. - * Return - * Returns TRUE if every character in text is a lowercase letter in the current locale. - */ -static int jx9Builtin_ctype_lower(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( !SyisLower(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * bool ctype_upper(string $text) - * Checks if all of the characters in the provided string, text, are uppercase letters. - * Parameters - * $text - * The tested string. - * Return - * Returns TRUE if every character in text is a uppercase letter in the current locale. - */ -static int jx9Builtin_ctype_upper(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); - zEnd = &zIn[nLen]; - if( nLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - if( zIn >= zEnd ){ - /* If we reach the end of the string, then the test succeeded. */ - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - if( !SyisUpper(zIn[0]) ){ - break; - } - /* Point to the next character */ - zIn++; - } - /* The test failed, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; -} -/* - * Date/Time functions - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Devel. - */ -#include -#ifdef __WINNT__ -/* GetSystemTime() */ -#include -#ifdef _WIN32_WCE -/* -** WindowsCE does not have a localtime() function. So create a -** substitute. -** Taken from the SQLite3 source tree. -** Status: Public domain -*/ -struct tm *__cdecl localtime(const time_t *t) -{ - static struct tm y; - FILETIME uTm, lTm; - SYSTEMTIME pTm; - jx9_int64 t64; - t64 = *t; - t64 = (t64 + 11644473600)*10000000; - uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); - uTm.dwHighDateTime= (DWORD)(t64 >> 32); - FileTimeToLocalFileTime(&uTm, &lTm); - FileTimeToSystemTime(&lTm, &pTm); - y.tm_year = pTm.wYear - 1900; - y.tm_mon = pTm.wMonth - 1; - y.tm_wday = pTm.wDayOfWeek; - y.tm_mday = pTm.wDay; - y.tm_hour = pTm.wHour; - y.tm_min = pTm.wMinute; - y.tm_sec = pTm.wSecond; - return &y; -} -#endif /*_WIN32_WCE */ -#elif defined(__UNIXES__) -#include -#endif /* __WINNT__*/ - /* - * int64 time(void) - * Current Unix timestamp - * Parameters - * None. - * Return - * Returns the current time measured in the number of seconds - * since the Unix Epoch (January 1 1970 00:00:00 GMT). - */ -static int jx9Builtin_time(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - time_t tt; - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* Extract the current time */ - time(&tt); - /* Return as 64-bit integer */ - jx9_result_int64(pCtx, (jx9_int64)tt); - return JX9_OK; -} -/* - * string/float microtime([ bool $get_as_float = false ]) - * microtime() returns the current Unix timestamp with microseconds. - * Parameters - * $get_as_float - * If used and set to TRUE, microtime() will return a float instead of a string - * as described in the return values section below. - * Return - * By default, microtime() returns a string in the form "msec sec", where sec - * is the current time measured in the number of seconds since the Unix - * epoch (0:00:00 January 1, 1970 GMT), and msec is the number of microseconds - * that have elapsed since sec expressed in seconds. - * If get_as_float is set to TRUE, then microtime() returns a float, which represents - * the current time in seconds since the Unix epoch accurate to the nearest microsecond. - */ -static int jx9Builtin_microtime(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int bFloat = 0; - sytime sTime; -#if defined(__UNIXES__) - struct timeval tv; - gettimeofday(&tv, 0); - sTime.tm_sec = (long)tv.tv_sec; - sTime.tm_usec = (long)tv.tv_usec; -#else - time_t tt; - time(&tt); - sTime.tm_sec = (long)tt; - sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC); -#endif /* __UNIXES__ */ - if( nArg > 0 ){ - bFloat = jx9_value_to_bool(apArg[0]); - } - if( bFloat ){ - /* Return as float */ - jx9_result_double(pCtx, (double)sTime.tm_sec); - }else{ - /* Return as string */ - jx9_result_string_format(pCtx, "%ld %ld", sTime.tm_usec, sTime.tm_sec); - } - return JX9_OK; -} -/* - * array getdate ([ int $timestamp = time() ]) - * Get date/time information. - * Parameter - * $timestamp: The optional timestamp parameter is an integer Unix timestamp - * that defaults to the current local time if a timestamp is not given. - * In other words, it defaults to the value of time(). - * Returns - * Returns an associative array of information related to the timestamp. - * Elements from the returned associative array are as follows: - * KEY VALUE - * --------- ------- - * "seconds" Numeric representation of seconds 0 to 59 - * "minutes" Numeric representation of minutes 0 to 59 - * "hours" Numeric representation of hours 0 to 23 - * "mday" Numeric representation of the day of the month 1 to 31 - * "wday" Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday) - * "mon" Numeric representation of a month 1 through 12 - * "year" A full numeric representation of a year, 4 digits Examples: 1999 or 2003 - * "yday" Numeric representation of the day of the year 0 through 365 - * "weekday" A full textual representation of the day of the week Sunday through Saturday - * "month" A full textual representation of a month, such as January or March January through December - * 0 Seconds since the Unix Epoch, similar to the values returned by time() and used by date(). - * NOTE: - * NULL is returned on failure. - */ -static int jx9Builtin_getdate(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pValue, *pArray; - Sytm sTm; - if( nArg < 1 ){ -#ifdef __WINNT__ - SYSTEMTIME sOS; - GetSystemTime(&sOS); - SYSTEMTIME_TO_SYTM(&sOS, &sTm); -#else - struct tm *pTm; - time_t t; - time(&t); - pTm = localtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); -#endif - }else{ - /* Use the given timestamp */ - time_t t; - struct tm *pTm; -#ifdef __WINNT__ -#ifdef _MSC_VER -#if _MSC_VER >= 1400 /* Visual Studio 2005 and up */ -#pragma warning(disable:4996) /* _CRT_SECURE...*/ -#endif -#endif -#endif - if( jx9_value_is_int(apArg[0]) ){ - t = (time_t)jx9_value_to_int64(apArg[0]); - pTm = localtime(&t); - if( pTm == 0 ){ - time(&t); - } - }else{ - time(&t); - } - pTm = localtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); - } - /* Element value */ - pValue = jx9_context_new_scalar(pCtx); - if( pValue == 0 ){ - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Fill the array */ - /* Seconds */ - jx9_value_int(pValue, sTm.tm_sec); - jx9_array_add_strkey_elem(pArray, "seconds", pValue); - /* Minutes */ - jx9_value_int(pValue, sTm.tm_min); - jx9_array_add_strkey_elem(pArray, "minutes", pValue); - /* Hours */ - jx9_value_int(pValue, sTm.tm_hour); - jx9_array_add_strkey_elem(pArray, "hours", pValue); - /* mday */ - jx9_value_int(pValue, sTm.tm_mday); - jx9_array_add_strkey_elem(pArray, "mday", pValue); - /* wday */ - jx9_value_int(pValue, sTm.tm_wday); - jx9_array_add_strkey_elem(pArray, "wday", pValue); - /* mon */ - jx9_value_int(pValue, sTm.tm_mon+1); - jx9_array_add_strkey_elem(pArray, "mon", pValue); - /* year */ - jx9_value_int(pValue, sTm.tm_year); - jx9_array_add_strkey_elem(pArray, "year", pValue); - /* yday */ - jx9_value_int(pValue, sTm.tm_yday); - jx9_array_add_strkey_elem(pArray, "yday", pValue); - /* Weekday */ - jx9_value_string(pValue, SyTimeGetDay(sTm.tm_wday), -1); - jx9_array_add_strkey_elem(pArray, "weekday", pValue); - /* Month */ - jx9_value_reset_string_cursor(pValue); - jx9_value_string(pValue, SyTimeGetMonth(sTm.tm_mon), -1); - jx9_array_add_strkey_elem(pArray, "month", pValue); - /* Seconds since the epoch */ - jx9_value_int64(pValue, (jx9_int64)time(0)); - jx9_array_add_elem(pArray, 0 /* Index zero */, pValue); - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * mixed gettimeofday([ bool $return_float = false ] ) - * Returns an associative array containing the data returned from the system call. - * Parameters - * $return_float - * When set to TRUE, a float instead of an array is returned. - * Return - * By default an array is returned. If return_float is set, then - * a float is returned. - */ -static int jx9Builtin_gettimeofday(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int bFloat = 0; - sytime sTime; -#if defined(__UNIXES__) - struct timeval tv; - gettimeofday(&tv, 0); - sTime.tm_sec = (long)tv.tv_sec; - sTime.tm_usec = (long)tv.tv_usec; -#else - time_t tt; - time(&tt); - sTime.tm_sec = (long)tt; - sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC); -#endif /* __UNIXES__ */ - if( nArg > 0 ){ - bFloat = jx9_value_to_bool(apArg[0]); - } - if( bFloat ){ - /* Return as float */ - jx9_result_double(pCtx, (double)sTime.tm_sec); - }else{ - /* Return an associative array */ - jx9_value *pValue, *pArray; - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - /* Element value */ - pValue = jx9_context_new_scalar(pCtx); - if( pValue == 0 || pArray == 0 ){ - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Fill the array */ - /* sec */ - jx9_value_int64(pValue, sTime.tm_sec); - jx9_array_add_strkey_elem(pArray, "sec", pValue); - /* usec */ - jx9_value_int64(pValue, sTime.tm_usec); - jx9_array_add_strkey_elem(pArray, "usec", pValue); - /* Return the array */ - jx9_result_value(pCtx, pArray); - } - return JX9_OK; -} -/* Check if the given year is leap or not */ -#define IS_LEAP_YEAR(YEAR) (YEAR % 400 ? ( YEAR % 100 ? ( YEAR % 4 ? 0 : 1 ) : 0 ) : 1) -/* ISO-8601 numeric representation of the day of the week */ -static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 }; -/* - * Format a given date string. - * Supported format: (Taken from JX9 online docs) - * character Description - * d Day of the month - * D A textual representation of a days - * j Day of the month without leading zeros - * l A full textual representation of the day of the week - * N ISO-8601 numeric representation of the day of the week - * w Numeric representation of the day of the week - * z The day of the year (starting from 0) - * F A full textual representation of a month, such as January or March - * m Numeric representation of a month, with leading zeros 01 through 12 - * M A short textual representation of a month, three letters Jan through Dec - * n Numeric representation of a month, without leading zeros 1 through 12 - * t Number of days in the given month 28 through 31 - * L Whether it's a leap year 1 if it is a leap year, 0 otherwise. - * o ISO-8601 year number. This has the same value as Y, except that if the ISO week number - * (W) belongs to the previous or next year, that year is used instead. (added in JX9 5.1.0) Examples: 1999 or 2003 - * Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003 - * y A two digit representation of a year Examples: 99 or 03 - * a Lowercase Ante meridiem and Post meridiem am or pm - * A Uppercase Ante meridiem and Post meridiem AM or PM - * g 12-hour format of an hour without leading zeros 1 through 12 - * G 24-hour format of an hour without leading zeros 0 through 23 - * h 12-hour format of an hour with leading zeros 01 through 12 - * H 24-hour format of an hour with leading zeros 00 through 23 - * i Minutes with leading zeros 00 to 59 - * s Seconds, with leading zeros 00 through 59 - * u Microseconds Example: 654321 - * e Timezone identifier Examples: UTC, GMT, Atlantic/Azores - * I (capital i) Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise. - * r RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200 - * U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) - * S English ordinal suffix for the day of the month, 2 characters - * O Difference to Greenwich time (GMT) in hours - * Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those - * east of UTC is always positive. - * c ISO 8601 date - */ -static sxi32 DateFormat(jx9_context *pCtx, const char *zIn, int nLen, Sytm *pTm) -{ - const char *zEnd = &zIn[nLen]; - const char *zCur; - /* Start the format process */ - for(;;){ - if( zIn >= zEnd ){ - /* No more input to process */ - break; - } - switch(zIn[0]){ - case 'd': - /* Day of the month, 2 digits with leading zeros */ - jx9_result_string_format(pCtx, "%02d", pTm->tm_mday); - break; - case 'D': - /*A textual representation of a day, three letters*/ - zCur = SyTimeGetDay(pTm->tm_wday); - jx9_result_string(pCtx, zCur, 3); - break; - case 'j': - /* Day of the month without leading zeros */ - jx9_result_string_format(pCtx, "%d", pTm->tm_mday); - break; - case 'l': - /* A full textual representation of the day of the week */ - zCur = SyTimeGetDay(pTm->tm_wday); - jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/); - break; - case 'N':{ - /* ISO-8601 numeric representation of the day of the week */ - jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]); - break; - } - case 'w': - /*Numeric representation of the day of the week*/ - jx9_result_string_format(pCtx, "%d", pTm->tm_wday); - break; - case 'z': - /*The day of the year*/ - jx9_result_string_format(pCtx, "%d", pTm->tm_yday); - break; - case 'F': - /*A full textual representation of a month, such as January or March*/ - zCur = SyTimeGetMonth(pTm->tm_mon); - jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/); - break; - case 'm': - /*Numeric representation of a month, with leading zeros*/ - jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1); - break; - case 'M': - /*A short textual representation of a month, three letters*/ - zCur = SyTimeGetMonth(pTm->tm_mon); - jx9_result_string(pCtx, zCur, 3); - break; - case 'n': - /*Numeric representation of a month, without leading zeros*/ - jx9_result_string_format(pCtx, "%d", pTm->tm_mon + 1); - break; - case 't':{ - static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - int nDays = aMonDays[pTm->tm_mon % 12 ]; - if( pTm->tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(pTm->tm_year) ){ - nDays = 28; - } - /*Number of days in the given month*/ - jx9_result_string_format(pCtx, "%d", nDays); - break; - } - case 'L':{ - int isLeap = IS_LEAP_YEAR(pTm->tm_year); - /* Whether it's a leap year */ - jx9_result_string_format(pCtx, "%d", isLeap); - break; - } - case 'o': - /* ISO-8601 year number.*/ - jx9_result_string_format(pCtx, "%4d", pTm->tm_year); - break; - case 'Y': - /* A full numeric representation of a year, 4 digits */ - jx9_result_string_format(pCtx, "%4d", pTm->tm_year); - break; - case 'y': - /*A two digit representation of a year*/ - jx9_result_string_format(pCtx, "%02d", pTm->tm_year%100); - break; - case 'a': - /* Lowercase Ante meridiem and Post meridiem */ - jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", 2); - break; - case 'A': - /* Uppercase Ante meridiem and Post meridiem */ - jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", 2); - break; - case 'g': - /* 12-hour format of an hour without leading zeros*/ - jx9_result_string_format(pCtx, "%d", 1+(pTm->tm_hour%12)); - break; - case 'G': - /* 24-hour format of an hour without leading zeros */ - jx9_result_string_format(pCtx, "%d", pTm->tm_hour); - break; - case 'h': - /* 12-hour format of an hour with leading zeros */ - jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12)); - break; - case 'H': - /* 24-hour format of an hour with leading zeros */ - jx9_result_string_format(pCtx, "%02d", pTm->tm_hour); - break; - case 'i': - /* Minutes with leading zeros */ - jx9_result_string_format(pCtx, "%02d", pTm->tm_min); - break; - case 's': - /* second with leading zeros */ - jx9_result_string_format(pCtx, "%02d", pTm->tm_sec); - break; - case 'u': - /* Microseconds */ - jx9_result_string_format(pCtx, "%u", pTm->tm_sec * SX_USEC_PER_SEC); - break; - case 'S':{ - /* English ordinal suffix for the day of the month, 2 characters */ - static const char zSuffix[] = "thstndrdthththththth"; - int v = pTm->tm_mday; - jx9_result_string(pCtx, &zSuffix[2 * (int)(v / 10 % 10 != 1 ? v % 10 : 0)], (int)sizeof(char) * 2); - break; - } - case 'e': - /* Timezone identifier */ - zCur = pTm->tm_zone; - if( zCur == 0 ){ - /* Assume GMT */ - zCur = "GMT"; - } - jx9_result_string(pCtx, zCur, -1); - break; - case 'I': - /* Whether or not the date is in daylight saving time */ -#ifdef __WINNT__ -#ifdef _MSC_VER -#ifndef _WIN32_WCE - _get_daylight(&pTm->tm_isdst); -#endif -#endif -#endif - jx9_result_string_format(pCtx, "%d", pTm->tm_isdst == 1); - break; - case 'r': - /* RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 */ - jx9_result_string_format(pCtx, "%.3s, %02d %.3s %4d %02d:%02d:%02d", - SyTimeGetDay(pTm->tm_wday), - pTm->tm_mday, - SyTimeGetMonth(pTm->tm_mon), - pTm->tm_year, - pTm->tm_hour, - pTm->tm_min, - pTm->tm_sec - ); - break; - case 'U':{ - time_t tt; - /* Seconds since the Unix Epoch */ - time(&tt); - jx9_result_string_format(pCtx, "%u", (unsigned int)tt); - break; - } - case 'O': - case 'P': - /* Difference to Greenwich time (GMT) in hours */ - jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff); - break; - case 'Z': - /* Timezone offset in seconds. The offset for timezones west of UTC - * is always negative, and for those east of UTC is always positive. - */ - jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff); - break; - case 'c': - /* ISO 8601 date */ - jx9_result_string_format(pCtx, "%4d-%02d-%02dT%02d:%02d:%02d%+05d", - pTm->tm_year, - pTm->tm_mon+1, - pTm->tm_mday, - pTm->tm_hour, - pTm->tm_min, - pTm->tm_sec, - pTm->tm_gmtoff - ); - break; - case '\\': - zIn++; - /* Expand verbatim */ - if( zIn < zEnd ){ - jx9_result_string(pCtx, zIn, (int)sizeof(char)); - } - break; - default: - /* Unknown format specifer, expand verbatim */ - jx9_result_string(pCtx, zIn, (int)sizeof(char)); - break; - } - /* Point to the next character */ - zIn++; - } - return SXRET_OK; -} -/* - * JX9 implementation of the strftime() function. - * The following formats are supported: - * %a An abbreviated textual representation of the day - * %A A full textual representation of the day - * %d Two-digit day of the month (with leading zeros) - * %e Day of the month, with a space preceding single digits. - * %j Day of the year, 3 digits with leading zeros - * %u ISO-8601 numeric representation of the day of the week 1 (for Monday) though 7 (for Sunday) - * %w Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday) - * %U Week number of the given year, starting with the first Sunday as the first week - * %V ISO-8601:1988 week number of the given year, starting with the first week of the year with at least - * 4 weekdays, with Monday being the start of the week. - * %W A numeric representation of the week of the year - * %b Abbreviated month name, based on the locale - * %B Full month name, based on the locale - * %h Abbreviated month name, based on the locale (an alias of %b) - * %m Two digit representation of the month - * %C Two digit representation of the century (year divided by 100, truncated to an integer) - * %g Two digit representation of the year going by ISO-8601:1988 standards (see %V) - * %G The full four-digit version of %g - * %y Two digit representation of the year - * %Y Four digit representation for the year - * %H Two digit representation of the hour in 24-hour format - * %I Two digit representation of the hour in 12-hour format - * %l (lower-case 'L') Hour in 12-hour format, with a space preceeding single digits - * %M Two digit representation of the minute - * %p UPPER-CASE 'AM' or 'PM' based on the given time - * %P lower-case 'am' or 'pm' based on the given time - * %r Same as "%I:%M:%S %p" - * %R Same as "%H:%M" - * %S Two digit representation of the second - * %T Same as "%H:%M:%S" - * %X Preferred time representation based on locale, without the date - * %z Either the time zone offset from UTC or the abbreviation - * %Z The time zone offset/abbreviation option NOT given by %z - * %c Preferred date and time stamp based on local - * %D Same as "%m/%d/%y" - * %F Same as "%Y-%m-%d" - * %s Unix Epoch Time timestamp (same as the time() function) - * %x Preferred date representation based on locale, without the time - * %n A newline character ("\n") - * %t A Tab character ("\t") - * %% A literal percentage character ("%") - */ -static int jx9Strftime( - jx9_context *pCtx, /* Call context */ - const char *zIn, /* Input string */ - int nLen, /* Input length */ - Sytm *pTm /* Parse of the given time */ - ) -{ - const char *zCur, *zEnd = &zIn[nLen]; - int c; - /* Start the format process */ - for(;;){ - zCur = zIn; - while(zIn < zEnd && zIn[0] != '%' ){ - zIn++; - } - if( zIn > zCur ){ - /* Consume input verbatim */ - jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); - } - zIn++; /* Jump the percent sign */ - if( zIn >= zEnd ){ - /* No more input to process */ - break; - } - c = zIn[0]; - /* Act according to the current specifer */ - switch(c){ - case '%': - /* A literal percentage character ("%") */ - jx9_result_string(pCtx, "%", (int)sizeof(char)); - break; - case 't': - /* A Tab character */ - jx9_result_string(pCtx, "\t", (int)sizeof(char)); - break; - case 'n': - /* A newline character */ - jx9_result_string(pCtx, "\n", (int)sizeof(char)); - break; - case 'a': - /* An abbreviated textual representation of the day */ - jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), (int)sizeof(char)*3); - break; - case 'A': - /* A full textual representation of the day */ - jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), -1/*Compute length automatically*/); - break; - case 'e': - /* Day of the month, 2 digits with leading space for single digit*/ - jx9_result_string_format(pCtx, "%2d", pTm->tm_mday); - break; - case 'd': - /* Two-digit day of the month (with leading zeros) */ - jx9_result_string_format(pCtx, "%02d", pTm->tm_mon+1); - break; - case 'j': - /*The day of the year, 3 digits with leading zeros*/ - jx9_result_string_format(pCtx, "%03d", pTm->tm_yday); - break; - case 'u': - /* ISO-8601 numeric representation of the day of the week */ - jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]); - break; - case 'w': - /* Numeric representation of the day of the week */ - jx9_result_string_format(pCtx, "%d", pTm->tm_wday); - break; - case 'b': - case 'h': - /*A short textual representation of a month, three letters (Not based on locale)*/ - jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), (int)sizeof(char)*3); - break; - case 'B': - /* Full month name (Not based on locale) */ - jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), -1/*Compute length automatically*/); - break; - case 'm': - /*Numeric representation of a month, with leading zeros*/ - jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1); - break; - case 'C': - /* Two digit representation of the century */ - jx9_result_string_format(pCtx, "%2d", pTm->tm_year/100); - break; - case 'y': - case 'g': - /* Two digit representation of the year */ - jx9_result_string_format(pCtx, "%2d", pTm->tm_year%100); - break; - case 'Y': - case 'G': - /* Four digit representation of the year */ - jx9_result_string_format(pCtx, "%4d", pTm->tm_year); - break; - case 'I': - /* 12-hour format of an hour with leading zeros */ - jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12)); - break; - case 'l': - /* 12-hour format of an hour with leading space */ - jx9_result_string_format(pCtx, "%2d", 1+(pTm->tm_hour%12)); - break; - case 'H': - /* 24-hour format of an hour with leading zeros */ - jx9_result_string_format(pCtx, "%02d", pTm->tm_hour); - break; - case 'M': - /* Minutes with leading zeros */ - jx9_result_string_format(pCtx, "%02d", pTm->tm_min); - break; - case 'S': - /* Seconds with leading zeros */ - jx9_result_string_format(pCtx, "%02d", pTm->tm_sec); - break; - case 'z': - case 'Z': - /* Timezone identifier */ - zCur = pTm->tm_zone; - if( zCur == 0 ){ - /* Assume GMT */ - zCur = "GMT"; - } - jx9_result_string(pCtx, zCur, -1); - break; - case 'T': - case 'X': - /* Same as "%H:%M:%S" */ - jx9_result_string_format(pCtx, "%02d:%02d:%02d", pTm->tm_hour, pTm->tm_min, pTm->tm_sec); - break; - case 'R': - /* Same as "%H:%M" */ - jx9_result_string_format(pCtx, "%02d:%02d", pTm->tm_hour, pTm->tm_min); - break; - case 'P': - /* Lowercase Ante meridiem and Post meridiem */ - jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", (int)sizeof(char)*2); - break; - case 'p': - /* Uppercase Ante meridiem and Post meridiem */ - jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", (int)sizeof(char)*2); - break; - case 'r': - /* Same as "%I:%M:%S %p" */ - jx9_result_string_format(pCtx, "%02d:%02d:%02d %s", - 1+(pTm->tm_hour%12), - pTm->tm_min, - pTm->tm_sec, - pTm->tm_hour > 12 ? "PM" : "AM" - ); - break; - case 'D': - case 'x': - /* Same as "%m/%d/%y" */ - jx9_result_string_format(pCtx, "%02d/%02d/%02d", - pTm->tm_mon+1, - pTm->tm_mday, - pTm->tm_year%100 - ); - break; - case 'F': - /* Same as "%Y-%m-%d" */ - jx9_result_string_format(pCtx, "%d-%02d-%02d", - pTm->tm_year, - pTm->tm_mon+1, - pTm->tm_mday - ); - break; - case 'c': - jx9_result_string_format(pCtx, "%d-%02d-%02d %02d:%02d:%02d", - pTm->tm_year, - pTm->tm_mon+1, - pTm->tm_mday, - pTm->tm_hour, - pTm->tm_min, - pTm->tm_sec - ); - break; - case 's':{ - time_t tt; - /* Seconds since the Unix Epoch */ - time(&tt); - jx9_result_string_format(pCtx, "%u", (unsigned int)tt); - break; - } - default: - /* unknown specifer, simply ignore*/ - break; - } - /* Advance the cursor */ - zIn++; - } - return SXRET_OK; -} -/* - * string date(string $format [, int $timestamp = time() ] ) - * Returns a string formatted according to the given format string using - * the given integer timestamp or the current time if no timestamp is given. - * In other words, timestamp is optional and defaults to the value of time(). - * Parameters - * $format - * The format of the outputted date string (See code above) - * $timestamp - * The optional timestamp parameter is an integer Unix timestamp - * that defaults to the current local time if a timestamp is not given. - * In other words, it defaults to the value of time(). - * Return - * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned. - */ -static int jx9Builtin_date(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zFormat; - int nLen; - Sytm sTm; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - zFormat = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Don't bother processing return the empty string */ - jx9_result_string(pCtx, "", 0); - } - if( nArg < 2 ){ -#ifdef __WINNT__ - SYSTEMTIME sOS; - GetSystemTime(&sOS); - SYSTEMTIME_TO_SYTM(&sOS, &sTm); -#else - struct tm *pTm; - time_t t; - time(&t); - pTm = localtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); -#endif - }else{ - /* Use the given timestamp */ - time_t t; - struct tm *pTm; - if( jx9_value_is_int(apArg[1]) ){ - t = (time_t)jx9_value_to_int64(apArg[1]); - pTm = localtime(&t); - if( pTm == 0 ){ - time(&t); - } - }else{ - time(&t); - } - pTm = localtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); - } - /* Format the given string */ - DateFormat(pCtx, zFormat, nLen, &sTm); - return JX9_OK; -} -/* - * string strftime(string $format [, int $timestamp = time() ] ) - * Format a local time/date (PLATFORM INDEPENDANT IMPLEENTATION NOT BASED ON LOCALE) - * Parameters - * $format - * The format of the outputted date string (See code above) - * $timestamp - * The optional timestamp parameter is an integer Unix timestamp - * that defaults to the current local time if a timestamp is not given. - * In other words, it defaults to the value of time(). - * Return - * Returns a string formatted according format using the given timestamp - * or the current local time if no timestamp is given. - */ -static int jx9Builtin_strftime(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zFormat; - int nLen; - Sytm sTm; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - zFormat = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Don't bother processing return FALSE */ - jx9_result_bool(pCtx, 0); - } - if( nArg < 2 ){ -#ifdef __WINNT__ - SYSTEMTIME sOS; - GetSystemTime(&sOS); - SYSTEMTIME_TO_SYTM(&sOS, &sTm); -#else - struct tm *pTm; - time_t t; - time(&t); - pTm = localtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); -#endif - }else{ - /* Use the given timestamp */ - time_t t; - struct tm *pTm; - if( jx9_value_is_int(apArg[1]) ){ - t = (time_t)jx9_value_to_int64(apArg[1]); - pTm = localtime(&t); - if( pTm == 0 ){ - time(&t); - } - }else{ - time(&t); - } - pTm = localtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); - } - /* Format the given string */ - jx9Strftime(pCtx, zFormat, nLen, &sTm); - if( jx9_context_result_buf_length(pCtx) < 1 ){ - /* Nothing was formatted, return FALSE */ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * string gmdate(string $format [, int $timestamp = time() ] ) - * Identical to the date() function except that the time returned - * is Greenwich Mean Time (GMT). - * Parameters - * $format - * The format of the outputted date string (See code above) - * $timestamp - * The optional timestamp parameter is an integer Unix timestamp - * that defaults to the current local time if a timestamp is not given. - * In other words, it defaults to the value of time(). - * Return - * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned. - */ -static int jx9Builtin_gmdate(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zFormat; - int nLen; - Sytm sTm; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - zFormat = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Don't bother processing return the empty string */ - jx9_result_string(pCtx, "", 0); - } - if( nArg < 2 ){ -#ifdef __WINNT__ - SYSTEMTIME sOS; - GetSystemTime(&sOS); - SYSTEMTIME_TO_SYTM(&sOS, &sTm); -#else - struct tm *pTm; - time_t t; - time(&t); - pTm = gmtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); -#endif - }else{ - /* Use the given timestamp */ - time_t t; - struct tm *pTm; - if( jx9_value_is_int(apArg[1]) ){ - t = (time_t)jx9_value_to_int64(apArg[1]); - pTm = gmtime(&t); - if( pTm == 0 ){ - time(&t); - } - }else{ - time(&t); - } - pTm = gmtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); - } - /* Format the given string */ - DateFormat(pCtx, zFormat, nLen, &sTm); - return JX9_OK; -} -/* - * array localtime([ int $timestamp = time() [, bool $is_associative = false ]]) - * Return the local time. - * Parameter - * $timestamp: The optional timestamp parameter is an integer Unix timestamp - * that defaults to the current local time if a timestamp is not given. - * In other words, it defaults to the value of time(). - * $is_associative - * If set to FALSE or not supplied then the array is returned as a regular, numerically - * indexed array. If the argument is set to TRUE then localtime() returns an associative - * array containing all the different elements of the structure returned by the C function - * call to localtime. The names of the different keys of the associative array are as follows: - * "tm_sec" - seconds, 0 to 59 - * "tm_min" - minutes, 0 to 59 - * "tm_hour" - hours, 0 to 23 - * "tm_mday" - day of the month, 1 to 31 - * "tm_mon" - month of the year, 0 (Jan) to 11 (Dec) - * "tm_year" - years since 1900 - * "tm_wday" - day of the week, 0 (Sun) to 6 (Sat) - * "tm_yday" - day of the year, 0 to 365 - * "tm_isdst" - is daylight savings time in effect? Positive if yes, 0 if not, negative if unknown. - * Returns - * An associative array of information related to the timestamp. - */ -static int jx9Builtin_localtime(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pValue, *pArray; - int isAssoc = 0; - Sytm sTm; - if( nArg < 1 ){ -#ifdef __WINNT__ - SYSTEMTIME sOS; - GetSystemTime(&sOS); /* TODO(chems): GMT not local */ - SYSTEMTIME_TO_SYTM(&sOS, &sTm); -#else - struct tm *pTm; - time_t t; - time(&t); - pTm = localtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); -#endif - }else{ - /* Use the given timestamp */ - time_t t; - struct tm *pTm; - if( jx9_value_is_int(apArg[0]) ){ - t = (time_t)jx9_value_to_int64(apArg[0]); - pTm = localtime(&t); - if( pTm == 0 ){ - time(&t); - } - }else{ - time(&t); - } - pTm = localtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); - } - /* Element value */ - pValue = jx9_context_new_scalar(pCtx); - if( pValue == 0 ){ - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - if( nArg > 1 ){ - isAssoc = jx9_value_to_bool(apArg[1]); - } - /* Fill the array */ - /* Seconds */ - jx9_value_int(pValue, sTm.tm_sec); - if( isAssoc ){ - jx9_array_add_strkey_elem(pArray, "tm_sec", pValue); - }else{ - jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); - } - /* Minutes */ - jx9_value_int(pValue, sTm.tm_min); - if( isAssoc ){ - jx9_array_add_strkey_elem(pArray, "tm_min", pValue); - }else{ - jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); - } - /* Hours */ - jx9_value_int(pValue, sTm.tm_hour); - if( isAssoc ){ - jx9_array_add_strkey_elem(pArray, "tm_hour", pValue); - }else{ - jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); - } - /* mday */ - jx9_value_int(pValue, sTm.tm_mday); - if( isAssoc ){ - jx9_array_add_strkey_elem(pArray, "tm_mday", pValue); - }else{ - jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); - } - /* mon */ - jx9_value_int(pValue, sTm.tm_mon); - if( isAssoc ){ - jx9_array_add_strkey_elem(pArray, "tm_mon", pValue); - }else{ - jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); - } - /* year since 1900 */ - jx9_value_int(pValue, sTm.tm_year-1900); - if( isAssoc ){ - jx9_array_add_strkey_elem(pArray, "tm_year", pValue); - }else{ - jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); - } - /* wday */ - jx9_value_int(pValue, sTm.tm_wday); - if( isAssoc ){ - jx9_array_add_strkey_elem(pArray, "tm_wday", pValue); - }else{ - jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); - } - /* yday */ - jx9_value_int(pValue, sTm.tm_yday); - if( isAssoc ){ - jx9_array_add_strkey_elem(pArray, "tm_yday", pValue); - }else{ - jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); - } - /* isdst */ -#ifdef __WINNT__ -#ifdef _MSC_VER -#ifndef _WIN32_WCE - _get_daylight(&sTm.tm_isdst); -#endif -#endif -#endif - jx9_value_int(pValue, sTm.tm_isdst); - if( isAssoc ){ - jx9_array_add_strkey_elem(pArray, "tm_isdst", pValue); - }else{ - jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); - } - /* Return the array */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * int idate(string $format [, int $timestamp = time() ]) - * Returns a number formatted according to the given format string - * using the given integer timestamp or the current local time if - * no timestamp is given. In other words, timestamp is optional and defaults - * to the value of time(). - * Unlike the function date(), idate() accepts just one char in the format - * parameter. - * $Parameters - * Supported format - * d Day of the month - * h Hour (12 hour format) - * H Hour (24 hour format) - * i Minutes - * I (uppercase i)1 if DST is activated, 0 otherwise - * L (uppercase l) returns 1 for leap year, 0 otherwise - * m Month number - * s Seconds - * t Days in current month - * U Seconds since the Unix Epoch - January 1 1970 00:00:00 UTC - this is the same as time() - * w Day of the week (0 on Sunday) - * W ISO-8601 week number of year, weeks starting on Monday - * y Year (1 or 2 digits - check note below) - * Y Year (4 digits) - * z Day of the year - * Z Timezone offset in seconds - * $timestamp - * The optional timestamp parameter is an integer Unix timestamp that defaults - * to the current local time if a timestamp is not given. In other words, it defaults - * to the value of time(). - * Return - * An integer. - */ -static int jx9Builtin_idate(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zFormat; - jx9_int64 iVal = 0; - int nLen; - Sytm sTm; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return -1 */ - jx9_result_int(pCtx, -1); - return JX9_OK; - } - zFormat = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Don't bother processing return -1*/ - jx9_result_int(pCtx, -1); - } - if( nArg < 2 ){ -#ifdef __WINNT__ - SYSTEMTIME sOS; - GetSystemTime(&sOS); - SYSTEMTIME_TO_SYTM(&sOS, &sTm); -#else - struct tm *pTm; - time_t t; - time(&t); - pTm = localtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); -#endif - }else{ - /* Use the given timestamp */ - time_t t; - struct tm *pTm; - if( jx9_value_is_int(apArg[1]) ){ - t = (time_t)jx9_value_to_int64(apArg[1]); - pTm = localtime(&t); - if( pTm == 0 ){ - time(&t); - } - }else{ - time(&t); - } - pTm = localtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); - } - /* Perform the requested operation */ - switch(zFormat[0]){ - case 'd': - /* Day of the month */ - iVal = sTm.tm_mday; - break; - case 'h': - /* Hour (12 hour format)*/ - iVal = 1 + (sTm.tm_hour % 12); - break; - case 'H': - /* Hour (24 hour format)*/ - iVal = sTm.tm_hour; - break; - case 'i': - /*Minutes*/ - iVal = sTm.tm_min; - break; - case 'I': - /* returns 1 if DST is activated, 0 otherwise */ -#ifdef __WINNT__ -#ifdef _MSC_VER -#ifndef _WIN32_WCE - _get_daylight(&sTm.tm_isdst); -#endif -#endif -#endif - iVal = sTm.tm_isdst; - break; - case 'L': - /* returns 1 for leap year, 0 otherwise */ - iVal = IS_LEAP_YEAR(sTm.tm_year); - break; - case 'm': - /* Month number*/ - iVal = sTm.tm_mon; - break; - case 's': - /*Seconds*/ - iVal = sTm.tm_sec; - break; - case 't':{ - /*Days in current month*/ - static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - int nDays = aMonDays[sTm.tm_mon % 12 ]; - if( sTm.tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(sTm.tm_year) ){ - nDays = 28; - } - iVal = nDays; - break; - } - case 'U': - /*Seconds since the Unix Epoch*/ - iVal = (jx9_int64)time(0); - break; - case 'w': - /* Day of the week (0 on Sunday) */ - iVal = sTm.tm_wday; - break; - case 'W': { - /* ISO-8601 week number of year, weeks starting on Monday */ - static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 }; - iVal = aISO8601[sTm.tm_wday % 7 ]; - break; - } - case 'y': - /* Year (2 digits) */ - iVal = sTm.tm_year % 100; - break; - case 'Y': - /* Year (4 digits) */ - iVal = sTm.tm_year; - break; - case 'z': - /* Day of the year */ - iVal = sTm.tm_yday; - break; - case 'Z': - /*Timezone offset in seconds*/ - iVal = sTm.tm_gmtoff; - break; - default: - /* unknown format, throw a warning */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Unknown date format token"); - break; - } - /* Return the time value */ - jx9_result_int64(pCtx, iVal); - return JX9_OK; -} -/* - * int mktime/gmmktime([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s") - * [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] ) - * Returns the Unix timestamp corresponding to the arguments given. This timestamp is a 64bit integer - * containing the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time - * specified. - * Arguments may be left out in order from right to left; any arguments thus omitted will be set to - * the current value according to the local date and time. - * Parameters - * $hour - * The number of the hour relevant to the start of the day determined by month, day and year. - * Negative values reference the hour before midnight of the day in question. Values greater - * than 23 reference the appropriate hour in the following day(s). - * $minute - * The number of the minute relevant to the start of the hour. Negative values reference - * the minute in the previous hour. Values greater than 59 reference the appropriate minute - * in the following hour(s). - * $second - * The number of seconds relevant to the start of the minute. Negative values reference - * the second in the previous minute. Values greater than 59 reference the appropriate - * second in the following minute(s). - * $month - * The number of the month relevant to the end of the previous year. Values 1 to 12 reference - * the normal calendar months of the year in question. Values less than 1 (including negative values) - * reference the months in the previous year in reverse order, so 0 is December, -1 is November)... - * $day - * The number of the day relevant to the end of the previous month. Values 1 to 28, 29, 30 or 31 - * (depending upon the month) reference the normal days in the relevant month. Values less than 1 - * (including negative values) reference the days in the previous month, so 0 is the last day - * of the previous month, -1 is the day before that, etc. Values greater than the number of days - * in the relevant month reference the appropriate day in the following month(s). - * $year - * The number of the year, may be a two or four digit value, with values between 0-69 mapping - * to 2000-2069 and 70-100 to 1970-2000. On systems where time_t is a 32bit signed integer, as - * most common today, the valid range for year is somewhere between 1901 and 2038. - * $is_dst - * This parameter can be set to 1 if the time is during daylight savings time (DST), 0 if it is not, - * or -1 (the default) if it is unknown whether the time is within daylight savings time or not. - * Return - * mktime() returns the Unix timestamp of the arguments given. - * If the arguments are invalid, the function returns FALSE - */ -static int jx9Builtin_mktime(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zFunction; - jx9_int64 iVal = 0; - struct tm *pTm; - time_t t; - /* Extract function name */ - zFunction = jx9_function_name(pCtx); - /* Get the current time */ - time(&t); - if( zFunction[0] == 'g' /* gmmktime */ ){ - pTm = gmtime(&t); - }else{ - /* localtime */ - pTm = localtime(&t); - } - if( nArg > 0 ){ - int iVal; - /* Hour */ - iVal = jx9_value_to_int(apArg[0]); - pTm->tm_hour = iVal; - if( nArg > 1 ){ - /* Minutes */ - iVal = jx9_value_to_int(apArg[1]); - pTm->tm_min = iVal; - if( nArg > 2 ){ - /* Seconds */ - iVal = jx9_value_to_int(apArg[2]); - pTm->tm_sec = iVal; - if( nArg > 3 ){ - /* Month */ - iVal = jx9_value_to_int(apArg[3]); - pTm->tm_mon = iVal - 1; - if( nArg > 4 ){ - /* mday */ - iVal = jx9_value_to_int(apArg[4]); - pTm->tm_mday = iVal; - if( nArg > 5 ){ - /* Year */ - iVal = jx9_value_to_int(apArg[5]); - if( iVal > 1900 ){ - iVal -= 1900; - } - pTm->tm_year = iVal; - if( nArg > 6 ){ - /* is_dst */ - iVal = jx9_value_to_bool(apArg[6]); - pTm->tm_isdst = iVal; - } - } - } - } - } - } - } - /* Make the time */ - iVal = (jx9_int64)mktime(pTm); - /* Return the timesatmp as a 64bit integer */ - jx9_result_int64(pCtx, iVal); - return JX9_OK; -} -/* - * Section: - * URL handling Functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * Output consumer callback for the standard Symisc routines. - * [i.e: SyBase64Encode(), SyBase64Decode(), SyUriEncode(), ...]. - */ -static int Consumer(const void *pData, unsigned int nLen, void *pUserData) -{ - /* Store in the call context result buffer */ - jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen); - return SXRET_OK; -} -/* - * string base64_encode(string $data) - * string convert_uuencode(string $data) - * Encodes data with MIME base64 - * Parameter - * $data - * Data to encode - * Return - * Encoded data or FALSE on failure. - */ -static int jx9Builtin_base64_encode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the input string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Nothing to process, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the BASE64 encoding */ - SyBase64Encode(zIn, (sxu32)nLen, Consumer, pCtx); - return JX9_OK; -} -/* - * string base64_decode(string $data) - * string convert_uudecode(string $data) - * Decodes data encoded with MIME base64 - * Parameter - * $data - * Encoded data. - * Return - * Returns the original data or FALSE on failure. - */ -static int jx9Builtin_base64_decode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the input string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Nothing to process, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the BASE64 decoding */ - SyBase64Decode(zIn, (sxu32)nLen, Consumer, pCtx); - return JX9_OK; -} -/* - * string urlencode(string $str) - * URL encoding - * Parameter - * $data - * Input string. - * Return - * Returns a string in which all non-alphanumeric characters except -_. have - * been replaced with a percent (%) sign followed by two hex digits and spaces - * encoded as plus (+) signs. - */ -static int jx9Builtin_urlencode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the input string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Nothing to process, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the URL encoding */ - SyUriEncode(zIn, (sxu32)nLen, Consumer, pCtx); - return JX9_OK; -} -/* - * string urldecode(string $str) - * Decodes any %## encoding in the given string. - * Plus symbols ('+') are decoded to a space character. - * Parameter - * $data - * Input string. - * Return - * Decoded URL or FALSE on failure. - */ -static int jx9Builtin_urldecode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn; - int nLen; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the input string */ - zIn = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Nothing to process, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the URL decoding */ - SyUriDecode(zIn, (sxu32)nLen, Consumer, pCtx, TRUE); - return JX9_OK; -} -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -/* Table of the built-in functions */ -static const jx9_builtin_func aBuiltInFunc[] = { - /* Variable handling functions */ - { "is_bool" , jx9Builtin_is_bool }, - { "is_float" , jx9Builtin_is_float }, - { "is_real" , jx9Builtin_is_float }, - { "is_double" , jx9Builtin_is_float }, - { "is_int" , jx9Builtin_is_int }, - { "is_integer" , jx9Builtin_is_int }, - { "is_long" , jx9Builtin_is_int }, - { "is_string" , jx9Builtin_is_string }, - { "is_null" , jx9Builtin_is_null }, - { "is_numeric" , jx9Builtin_is_numeric }, - { "is_scalar" , jx9Builtin_is_scalar }, - { "is_array" , jx9Builtin_is_array }, - { "is_object" , jx9Builtin_is_object }, - { "is_resource", jx9Builtin_is_resource }, - { "douleval" , jx9Builtin_floatval }, - { "floatval" , jx9Builtin_floatval }, - { "intval" , jx9Builtin_intval }, - { "strval" , jx9Builtin_strval }, - { "empty" , jx9Builtin_empty }, -#ifndef JX9_DISABLE_BUILTIN_FUNC -#ifdef JX9_ENABLE_MATH_FUNC - /* Math functions */ - { "abs" , jx9Builtin_abs }, - { "sqrt" , jx9Builtin_sqrt }, - { "exp" , jx9Builtin_exp }, - { "floor", jx9Builtin_floor }, - { "cos" , jx9Builtin_cos }, - { "sin" , jx9Builtin_sin }, - { "acos" , jx9Builtin_acos }, - { "asin" , jx9Builtin_asin }, - { "cosh" , jx9Builtin_cosh }, - { "sinh" , jx9Builtin_sinh }, - { "ceil" , jx9Builtin_ceil }, - { "tan" , jx9Builtin_tan }, - { "tanh" , jx9Builtin_tanh }, - { "atan" , jx9Builtin_atan }, - { "atan2", jx9Builtin_atan2 }, - { "log" , jx9Builtin_log }, - { "log10" , jx9Builtin_log10 }, - { "pow" , jx9Builtin_pow }, - { "pi", jx9Builtin_pi }, - { "fmod", jx9Builtin_fmod }, - { "hypot", jx9Builtin_hypot }, -#endif /* JX9_ENABLE_MATH_FUNC */ - { "round", jx9Builtin_round }, - { "dechex", jx9Builtin_dechex }, - { "decoct", jx9Builtin_decoct }, - { "decbin", jx9Builtin_decbin }, - { "hexdec", jx9Builtin_hexdec }, - { "bindec", jx9Builtin_bindec }, - { "octdec", jx9Builtin_octdec }, - { "base_convert", jx9Builtin_base_convert }, - /* String handling functions */ - { "substr", jx9Builtin_substr }, - { "substr_compare", jx9Builtin_substr_compare }, - { "substr_count", jx9Builtin_substr_count }, - { "chunk_split", jx9Builtin_chunk_split}, - { "htmlspecialchars", jx9Builtin_htmlspecialchars }, - { "htmlspecialchars_decode", jx9Builtin_htmlspecialchars_decode }, - { "get_html_translation_table", jx9Builtin_get_html_translation_table }, - { "htmlentities", jx9Builtin_htmlentities}, - { "html_entity_decode", jx9Builtin_html_entity_decode}, - { "strlen" , jx9Builtin_strlen }, - { "strcmp" , jx9Builtin_strcmp }, - { "strcoll" , jx9Builtin_strcmp }, - { "strncmp" , jx9Builtin_strncmp }, - { "strcasecmp" , jx9Builtin_strcasecmp }, - { "strncasecmp", jx9Builtin_strncasecmp}, - { "implode" , jx9Builtin_implode }, - { "join" , jx9Builtin_implode }, - { "implode_recursive" , jx9Builtin_implode_recursive }, - { "join_recursive" , jx9Builtin_implode_recursive }, - { "explode" , jx9Builtin_explode }, - { "trim" , jx9Builtin_trim }, - { "rtrim" , jx9Builtin_rtrim }, - { "chop" , jx9Builtin_rtrim }, - { "ltrim" , jx9Builtin_ltrim }, - { "strtolower", jx9Builtin_strtolower }, - { "mb_strtolower", jx9Builtin_strtolower }, /* Only UTF-8 encoding is supported */ - { "strtoupper", jx9Builtin_strtoupper }, - { "mb_strtoupper", jx9Builtin_strtoupper }, /* Only UTF-8 encoding is supported */ - { "ord", jx9Builtin_ord }, - { "chr", jx9Builtin_chr }, - { "bin2hex", jx9Builtin_bin2hex }, - { "strstr", jx9Builtin_strstr }, - { "stristr", jx9Builtin_stristr }, - { "strchr", jx9Builtin_strstr }, - { "strpos", jx9Builtin_strpos }, - { "stripos", jx9Builtin_stripos }, - { "strrpos", jx9Builtin_strrpos }, - { "strripos", jx9Builtin_strripos }, - { "strrchr", jx9Builtin_strrchr }, - { "strrev", jx9Builtin_strrev }, - { "str_repeat", jx9Builtin_str_repeat }, - { "nl2br", jx9Builtin_nl2br }, - { "sprintf", jx9Builtin_sprintf }, - { "printf", jx9Builtin_printf }, - { "vprintf", jx9Builtin_vprintf }, - { "vsprintf", jx9Builtin_vsprintf }, - { "size_format", jx9Builtin_size_format}, -#if !defined(JX9_DISABLE_HASH_FUNC) - { "md5", jx9Builtin_md5 }, - { "sha1", jx9Builtin_sha1 }, - { "crc32", jx9Builtin_crc32 }, -#endif /* JX9_DISABLE_HASH_FUNC */ - { "str_getcsv", jx9Builtin_str_getcsv }, - { "strip_tags", jx9Builtin_strip_tags }, - { "str_split", jx9Builtin_str_split }, - { "strspn", jx9Builtin_strspn }, - { "strcspn", jx9Builtin_strcspn }, - { "strpbrk", jx9Builtin_strpbrk }, - { "soundex", jx9Builtin_soundex }, - { "wordwrap", jx9Builtin_wordwrap }, - { "strtok", jx9Builtin_strtok }, - { "str_pad", jx9Builtin_str_pad }, - { "str_replace", jx9Builtin_str_replace}, - { "str_ireplace", jx9Builtin_str_replace}, - { "strtr", jx9Builtin_strtr }, - { "parse_ini_string", jx9Builtin_parse_ini_string}, - /* Ctype functions */ - { "ctype_alnum", jx9Builtin_ctype_alnum }, - { "ctype_alpha", jx9Builtin_ctype_alpha }, - { "ctype_cntrl", jx9Builtin_ctype_cntrl }, - { "ctype_digit", jx9Builtin_ctype_digit }, - { "ctype_xdigit", jx9Builtin_ctype_xdigit}, - { "ctype_graph", jx9Builtin_ctype_graph }, - { "ctype_print", jx9Builtin_ctype_print }, - { "ctype_punct", jx9Builtin_ctype_punct }, - { "ctype_space", jx9Builtin_ctype_space }, - { "ctype_lower", jx9Builtin_ctype_lower }, - { "ctype_upper", jx9Builtin_ctype_upper }, - /* Time functions */ - { "time" , jx9Builtin_time }, - { "microtime", jx9Builtin_microtime }, - { "getdate" , jx9Builtin_getdate }, - { "gettimeofday", jx9Builtin_gettimeofday }, - { "date", jx9Builtin_date }, - { "strftime", jx9Builtin_strftime }, - { "idate", jx9Builtin_idate }, - { "gmdate", jx9Builtin_gmdate }, - { "localtime", jx9Builtin_localtime }, - { "mktime", jx9Builtin_mktime }, - { "gmmktime", jx9Builtin_mktime }, - /* URL functions */ - { "base64_encode", jx9Builtin_base64_encode }, - { "base64_decode", jx9Builtin_base64_decode }, - { "convert_uuencode", jx9Builtin_base64_encode }, - { "convert_uudecode", jx9Builtin_base64_decode }, - { "urlencode", jx9Builtin_urlencode }, - { "urldecode", jx9Builtin_urldecode }, - { "rawurlencode", jx9Builtin_urlencode }, - { "rawurldecode", jx9Builtin_urldecode }, -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -}; -/* - * Register the built-in functions defined above, the array functions - * defined in hashmap.c and the IO functions defined in vfs.c. - */ -JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm) -{ - sxu32 n; - for( n = 0 ; n < SX_ARRAYSIZE(aBuiltInFunc) ; ++n ){ - jx9_create_function(&(*pVm), aBuiltInFunc[n].zName, aBuiltInFunc[n].xFunc, 0); - } - /* Register hashmap functions [i.e: sort(), count(), array_diff(), ...] */ - jx9RegisterHashmapFunctions(&(*pVm)); - /* Register IO functions [i.e: fread(), fwrite(), chdir(), mkdir(), file(), ...] */ - jx9RegisterIORoutine(&(*pVm)); -} - -/* - * ---------------------------------------------------------- - * File: jx9_compile.c - * MD5: 562e73eb7214f890e71713c6b97a7863 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: compile.c v1.7 FreeBSD 2012-12-11 21:46 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* - * This file implement a thread-safe and full-reentrant compiler for the JX9 engine. - * That is, routines defined in this file takes a stream of tokens and output - * JX9 bytecode instructions. - */ -/* Forward declaration */ -typedef struct LangConstruct LangConstruct; -typedef struct JumpFixup JumpFixup; -/* Block [i.e: set of statements] control flags */ -#define GEN_BLOCK_LOOP 0x001 /* Loop block [i.e: for, while, ...] */ -#define GEN_BLOCK_PROTECTED 0x002 /* Protected block */ -#define GEN_BLOCK_COND 0x004 /* Conditional block [i.e: if(condition){} ]*/ -#define GEN_BLOCK_FUNC 0x008 /* Function body */ -#define GEN_BLOCK_GLOBAL 0x010 /* Global block (always set)*/ -#define GEN_BLOC_NESTED_FUNC 0x020 /* Nested function body */ -#define GEN_BLOCK_EXPR 0x040 /* Expression */ -#define GEN_BLOCK_STD 0x080 /* Standard block */ -#define GEN_BLOCK_SWITCH 0x100 /* Switch statement */ -/* - * Compilation of some JX9 constructs such as if, for, while, the logical or - * (||) and logical and (&&) operators in expressions requires the - * generation of forward jumps. - * Since the destination PC target of these jumps isn't known when the jumps - * are emitted, we record each forward jump in an instance of the following - * structure. Those jumps are fixed later when the jump destination is resolved. - */ -struct JumpFixup -{ - sxi32 nJumpType; /* Jump type. Either TRUE jump, FALSE jump or Unconditional jump */ - sxu32 nInstrIdx; /* Instruction index to fix later when the jump destination is resolved. */ -}; -/* - * Each language construct is represented by an instance - * of the following structure. - */ -struct LangConstruct -{ - sxu32 nID; /* Language construct ID [i.e: JX9_TKWRD_WHILE, JX9_TKWRD_FOR, JX9_TKWRD_IF...] */ - ProcLangConstruct xConstruct; /* C function implementing the language construct */ -}; -/* Compilation flags */ -#define JX9_COMPILE_SINGLE_STMT 0x001 /* Compile a single statement */ -/* Token stream synchronization macros */ -#define SWAP_TOKEN_STREAM(GEN, START, END)\ - pTmp = GEN->pEnd;\ - pGen->pIn = START;\ - pGen->pEnd = END -#define UPDATE_TOKEN_STREAM(GEN)\ - if( GEN->pIn < pTmp ){\ - GEN->pIn++;\ - }\ - GEN->pEnd = pTmp -#define SWAP_DELIMITER(GEN, START, END)\ - pTmpIn = GEN->pIn;\ - pTmpEnd = GEN->pEnd;\ - GEN->pIn = START;\ - GEN->pEnd = END -#define RE_SWAP_DELIMITER(GEN)\ - GEN->pIn = pTmpIn;\ - GEN->pEnd = pTmpEnd -/* Flags related to expression compilation */ -#define EXPR_FLAG_LOAD_IDX_STORE 0x001 /* Set the iP2 flag when dealing with the LOAD_IDX instruction */ -#define EXPR_FLAG_RDONLY_LOAD 0x002 /* Read-only load, refer to the 'JX9_OP_LOAD' VM instruction for more information */ -#define EXPR_FLAG_COMMA_STATEMENT 0x004 /* Treat comma expression as a single statement (used by object attributes) */ -/* Forward declaration */ -static sxi32 jx9CompileExpr( - jx9_gen_state *pGen, /* Code generator state */ - sxi32 iFlags, /* Control flags */ - sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */ - ); - -/* - * Recover from a compile-time error. In other words synchronize - * the token stream cursor with the first semi-colon seen. - */ -static sxi32 jx9ErrorRecover(jx9_gen_state *pGen) -{ - /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI /*';'*/) == 0){ - pGen->pIn++; - } - return SXRET_OK; -} -/* - * Check if the given identifier name is reserved or not. - * Return TRUE if reserved.FALSE otherwise. - */ -static int GenStateIsReservedID(SyString *pName) -{ - if( pName->nByte == sizeof("null") - 1 ){ - if( SyStrnicmp(pName->zString, "null", sizeof("null")-1) == 0 ){ - return TRUE; - }else if( SyStrnicmp(pName->zString, "true", sizeof("true")-1) == 0 ){ - return TRUE; - } - }else if( pName->nByte == sizeof("false") - 1 ){ - if( SyStrnicmp(pName->zString, "false", sizeof("false")-1) == 0 ){ - return TRUE; - } - } - /* Not a reserved constant */ - return FALSE; -} -/* - * Check if a given token value is installed in the literal table. - */ -static sxi32 GenStateFindLiteral(jx9_gen_state *pGen, const SyString *pValue, sxu32 *pIdx) -{ - SyHashEntry *pEntry; - pEntry = SyHashGet(&pGen->hLiteral, (const void *)pValue->zString, pValue->nByte); - if( pEntry == 0 ){ - return SXERR_NOTFOUND; - } - *pIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); - return SXRET_OK; -} -/* - * Install a given constant index in the literal table. - * In order to be installed, the jx9_value must be of type string. - */ -static sxi32 GenStateInstallLiteral(jx9_gen_state *pGen,jx9_value *pObj, sxu32 nIdx) -{ - if( SyBlobLength(&pObj->sBlob) > 0 ){ - SyHashInsert(&pGen->hLiteral, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), SX_INT_TO_PTR(nIdx)); - } - return SXRET_OK; -} -/* - * Generate a fatal error. - */ -static sxi32 GenStateOutOfMem(jx9_gen_state *pGen) -{ - jx9GenCompileError(pGen,E_ERROR,1,"Fatal, Jx9 compiler is running out of memory"); - /* Abort compilation immediately */ - return SXERR_ABORT; -} -/* - * Fetch a block that correspond to the given criteria from the stack of - * compiled blocks. - * Return a pointer to that block on success. NULL otherwise. - */ -static GenBlock * GenStateFetchBlock(GenBlock *pCurrent, sxi32 iBlockType, sxi32 iCount) -{ - GenBlock *pBlock = pCurrent; - for(;;){ - if( pBlock->iFlags & iBlockType ){ - iCount--; /* Decrement nesting level */ - if( iCount < 1 ){ - /* Block meet with the desired criteria */ - return pBlock; - } - } - /* Point to the upper block */ - pBlock = pBlock->pParent; - if( pBlock == 0 || (pBlock->iFlags & (GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC)) ){ - /* Forbidden */ - break; - } - } - /* No such block */ - return 0; -} -/* - * Initialize a freshly allocated block instance. - */ -static void GenStateInitBlock( - jx9_gen_state *pGen, /* Code generator state */ - GenBlock *pBlock, /* Target block */ - sxi32 iType, /* Block type [i.e: loop, conditional, function body, etc.]*/ - sxu32 nFirstInstr, /* First instruction to compile */ - void *pUserData /* Upper layer private data */ - ) -{ - /* Initialize block fields */ - pBlock->nFirstInstr = nFirstInstr; - pBlock->pUserData = pUserData; - pBlock->pGen = pGen; - pBlock->iFlags = iType; - pBlock->pParent = 0; - pBlock->bPostContinue = 0; - SySetInit(&pBlock->aJumpFix, &pGen->pVm->sAllocator, sizeof(JumpFixup)); - SySetInit(&pBlock->aPostContFix, &pGen->pVm->sAllocator, sizeof(JumpFixup)); -} -/* - * Allocate a new block instance. - * Return SXRET_OK and write a pointer to the new instantiated block - * on success.Otherwise generate a compile-time error and abort - * processing on failure. - */ -static sxi32 GenStateEnterBlock( - jx9_gen_state *pGen, /* Code generator state */ - sxi32 iType, /* Block type [i.e: loop, conditional, function body, etc.]*/ - sxu32 nFirstInstr, /* First instruction to compile */ - void *pUserData, /* Upper layer private data */ - GenBlock **ppBlock /* OUT: instantiated block */ - ) -{ - GenBlock *pBlock; - /* Allocate a new block instance */ - pBlock = (GenBlock *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(GenBlock)); - if( pBlock == 0 ){ - /* If the supplied memory subsystem is so sick that we are unable to allocate - * a tiny chunk of memory, there is no much we can do here. - */ - return GenStateOutOfMem(pGen); - } - /* Zero the structure */ - SyZero(pBlock, sizeof(GenBlock)); - GenStateInitBlock(&(*pGen), pBlock, iType, nFirstInstr, pUserData); - /* Link to the parent block */ - pBlock->pParent = pGen->pCurrent; - /* Mark as the current block */ - pGen->pCurrent = pBlock; - if( ppBlock ){ - /* Write a pointer to the new instance */ - *ppBlock = pBlock; - } - return SXRET_OK; -} -/* - * Release block fields without freeing the whole instance. - */ -static void GenStateReleaseBlock(GenBlock *pBlock) -{ - SySetRelease(&pBlock->aPostContFix); - SySetRelease(&pBlock->aJumpFix); -} -/* - * Release a block. - */ -static void GenStateFreeBlock(GenBlock *pBlock) -{ - jx9_gen_state *pGen = pBlock->pGen; - GenStateReleaseBlock(&(*pBlock)); - /* Free the instance */ - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pBlock); -} -/* - * POP and release a block from the stack of compiled blocks. - */ -static sxi32 GenStateLeaveBlock(jx9_gen_state *pGen, GenBlock **ppBlock) -{ - GenBlock *pBlock = pGen->pCurrent; - if( pBlock == 0 ){ - /* No more block to pop */ - return SXERR_EMPTY; - } - /* Point to the upper block */ - pGen->pCurrent = pBlock->pParent; - if( ppBlock ){ - /* Write a pointer to the popped block */ - *ppBlock = pBlock; - }else{ - /* Safely release the block */ - GenStateFreeBlock(&(*pBlock)); - } - return SXRET_OK; -} -/* - * Emit a forward jump. - * Notes on forward jumps - * Compilation of some JX9 constructs such as if, for, while and the logical or - * (||) and logical and (&&) operators in expressions requires the - * generation of forward jumps. - * Since the destination PC target of these jumps isn't known when the jumps - * are emitted, we record each forward jump in an instance of the following - * structure. Those jumps are fixed later when the jump destination is resolved. - */ -static sxi32 GenStateNewJumpFixup(GenBlock *pBlock, sxi32 nJumpType, sxu32 nInstrIdx) -{ - JumpFixup sJumpFix; - sxi32 rc; - /* Init the JumpFixup structure */ - sJumpFix.nJumpType = nJumpType; - sJumpFix.nInstrIdx = nInstrIdx; - /* Insert in the jump fixup table */ - rc = SySetPut(&pBlock->aJumpFix,(const void *)&sJumpFix); - return rc; -} -/* - * Fix a forward jump now the jump destination is resolved. - * Return the total number of fixed jumps. - * Notes on forward jumps: - * Compilation of some JX9 constructs such as if, for, while and the logical or - * (||) and logical and (&&) operators in expressions requires the - * generation of forward jumps. - * Since the destination PC target of these jumps isn't known when the jumps - * are emitted, we record each forward jump in an instance of the following - * structure.Those jumps are fixed later when the jump destination is resolved. - */ -static sxu32 GenStateFixJumps(GenBlock *pBlock, sxi32 nJumpType, sxu32 nJumpDest) -{ - JumpFixup *aFix; - VmInstr *pInstr; - sxu32 nFixed; - sxu32 n; - /* Point to the jump fixup table */ - aFix = (JumpFixup *)SySetBasePtr(&pBlock->aJumpFix); - /* Fix the desired jumps */ - for( nFixed = n = 0 ; n < SySetUsed(&pBlock->aJumpFix) ; ++n ){ - if( aFix[n].nJumpType < 0 ){ - /* Already fixed */ - continue; - } - if( nJumpType > 0 && aFix[n].nJumpType != nJumpType ){ - /* Not of our interest */ - continue; - } - /* Point to the instruction to fix */ - pInstr = jx9VmGetInstr(pBlock->pGen->pVm, aFix[n].nInstrIdx); - if( pInstr ){ - pInstr->iP2 = nJumpDest; - nFixed++; - /* Mark as fixed */ - aFix[n].nJumpType = -1; - } - } - /* Total number of fixed jumps */ - return nFixed; -} -/* - * Reserve a room for a numeric constant [i.e: 64-bit integer or real number] - * in the constant table. - */ -static jx9_value * GenStateInstallNumLiteral(jx9_gen_state *pGen, sxu32 *pIdx) -{ - jx9_value *pObj; - sxu32 nIdx = 0; /* cc warning */ - /* Reserve a new constant */ - pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx); - if( pObj == 0 ){ - GenStateOutOfMem(pGen); - return 0; - } - *pIdx = nIdx; - /* TODO(chems): Create a numeric table (64bit int keys) same as - * the constant string iterals table [optimization purposes]. - */ - return pObj; -} -/* - * Compile a numeric [i.e: integer or real] literal. - * Notes on the integer type. - * According to the JX9 language reference manual - * Integers can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8) - * or binary (base 2) notation, optionally preceded by a sign (- or +). - * To use octal notation, precede the number with a 0 (zero). To use hexadecimal - * notation precede the number with 0x. To use binary notation precede the number with 0b. - */ -static sxi32 jx9CompileNumLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag) -{ - SyToken *pToken = pGen->pIn; /* Raw token */ - sxu32 nIdx = 0; - if( pToken->nType & JX9_TK_INTEGER ){ - jx9_value *pObj; - sxi64 iValue; - iValue = jx9TokenValueToInt64(&pToken->sData); - pObj = GenStateInstallNumLiteral(&(*pGen), &nIdx); - if( pObj == 0 ){ - SXUNUSED(iCompileFlag); /* cc warning */ - return SXERR_ABORT; - } - jx9MemObjInitFromInt(pGen->pVm, pObj, iValue); - }else{ - /* Real number */ - jx9_value *pObj; - /* Reserve a new constant */ - pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx); - if( pObj == 0 ){ - return GenStateOutOfMem(pGen); - } - jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData); - jx9MemObjToReal(pObj); - } - /* Emit the load constant instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0); - /* Node successfully compiled */ - return SXRET_OK; -} -/* - * Compile a nowdoc string. - * According to the JX9 language reference manual: - * - * Nowdocs are to single-quoted strings what heredocs are to double-quoted strings. - * A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc. - * The construct is ideal for embedding JX9 code or other large blocks of text without the - * need for escaping. It shares some features in common with the SGML - * construct, in that it declares a block of text which is not for parsing. - * A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier - * which follows is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc - * identifiers also apply to nowdoc identifiers, especially those regarding the appearance - * of the closing identifier. - */ -static sxi32 jx9CompileNowdoc(jx9_gen_state *pGen,sxi32 iCompileFlag) -{ - SyString *pStr = &pGen->pIn->sData; /* Constant string literal */ - jx9_value *pObj; - sxu32 nIdx; - nIdx = 0; /* Prevent compiler warning */ - if( pStr->nByte <= 0 ){ - /* Empty string, load NULL */ - jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC, 0, 0, 0, 0); - return SXRET_OK; - } - /* Reserve a new constant */ - pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx); - if( pObj == 0 ){ - jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "JX9 engine is running out of memory"); - SXUNUSED(iCompileFlag); /* cc warning */ - return SXERR_ABORT; - } - /* No processing is done here, simply a memcpy() operation */ - jx9MemObjInitFromString(pGen->pVm, pObj, pStr); - /* Emit the load constant instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0); - /* Node successfully compiled */ - return SXRET_OK; -} -/* - * Compile a single quoted string. - * According to the JX9 language reference manual: - * - * The simplest way to specify a string is to enclose it in single quotes (the character ' ). - * To specify a literal single quote, escape it with a backslash (\). To specify a literal - * backslash, double it (\\). All other instances of backslash will be treated as a literal - * backslash: this means that the other escape sequences you might be used to, such as \r - * or \n, will be output literally as specified rather than having any special meaning. - * - */ -JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag) -{ - SyString *pStr = &pGen->pIn->sData; /* Constant string literal */ - const char *zIn, *zCur, *zEnd; - jx9_value *pObj; - sxu32 nIdx; - nIdx = 0; /* Prevent compiler warning */ - /* Delimit the string */ - zIn = pStr->zString; - zEnd = &zIn[pStr->nByte]; - if( zIn >= zEnd ){ - /* Empty string, load NULL */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0); - return SXRET_OK; - } - if( SXRET_OK == GenStateFindLiteral(&(*pGen), pStr, &nIdx) ){ - /* Already processed, emit the load constant instruction - * and return. - */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0); - return SXRET_OK; - } - /* Reserve a new constant */ - pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx); - if( pObj == 0 ){ - jx9GenCompileError(&(*pGen), E_ERROR, 1, "JX9 engine is running out of memory"); - SXUNUSED(iCompileFlag); /* cc warning */ - return SXERR_ABORT; - } - jx9MemObjInitFromString(pGen->pVm, pObj, 0); - /* Compile the node */ - for(;;){ - if( zIn >= zEnd ){ - /* End of input */ - break; - } - zCur = zIn; - while( zIn < zEnd && zIn[0] != '\\' ){ - zIn++; - } - if( zIn > zCur ){ - /* Append raw contents*/ - jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur)); - } - zIn++; - if( zIn < zEnd ){ - if( zIn[0] == '\\' ){ - /* A literal backslash */ - jx9MemObjStringAppend(pObj, "\\", sizeof(char)); - }else if( zIn[0] == '\'' ){ - /* A single quote */ - jx9MemObjStringAppend(pObj, "'", sizeof(char)); - }else{ - /* verbatim copy */ - zIn--; - jx9MemObjStringAppend(pObj, zIn, sizeof(char)*2); - zIn++; - } - } - /* Advance the stream cursor */ - zIn++; - } - /* Emit the load constant instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0); - if( pStr->nByte < 1024 ){ - /* Install in the literal table */ - GenStateInstallLiteral(pGen, pObj, nIdx); - } - /* Node successfully compiled */ - return SXRET_OK; -} -/* - * Process variable expression [i.e: "$var", "${var}"] embedded in a double quoted/heredoc string. - * According to the JX9 language reference manual - * When a string is specified in double quotes or with heredoc, variables are parsed within it. - * There are two types of syntax: a simple one and a complex one. The simple syntax is the most - * common and convenient. It provides a way to embed a variable, an array value, or an object - * property in a string with a minimum of effort. - * Simple syntax - * If a dollar sign ($) is encountered, the parser will greedily take as many tokens as possible - * to form a valid variable name. Enclose the variable name in curly braces to explicitly specify - * the end of the name. - * Similarly, an array index or an object property can be parsed. With array indices, the closing - * square bracket (]) marks the end of the index. The same rules apply to object properties - * as to simple variables. - * Complex (curly) syntax - * This isn't called complex because the syntax is complex, but because it allows for the use - * of complex expressions. - * Any scalar variable, array element or object property with a string representation can be - * included via this syntax. Simply write the expression the same way as it would appear outside - * the string, and then wrap it in { and }. Since { can not be escaped, this syntax will only - * be recognised when the $ immediately follows the {. Use {\$ to get a literal {$ - */ -static sxi32 GenStateProcessStringExpression( - jx9_gen_state *pGen, /* Code generator state */ - const char *zIn, /* Raw expression */ - const char *zEnd /* End of the expression */ - ) -{ - SyToken *pTmpIn, *pTmpEnd; - SySet sToken; - sxi32 rc; - /* Initialize the token set */ - SySetInit(&sToken, &pGen->pVm->sAllocator, sizeof(SyToken)); - /* Preallocate some slots */ - SySetAlloc(&sToken, 0x08); - /* Tokenize the text */ - jx9Tokenize(zIn,(sxu32)(zEnd-zIn),&sToken); - /* Swap delimiter */ - pTmpIn = pGen->pIn; - pTmpEnd = pGen->pEnd; - pGen->pIn = (SyToken *)SySetBasePtr(&sToken); - pGen->pEnd = &pGen->pIn[SySetUsed(&sToken)]; - /* Compile the expression */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - /* Restore token stream */ - pGen->pIn = pTmpIn; - pGen->pEnd = pTmpEnd; - /* Release the token set */ - SySetRelease(&sToken); - /* Compilation result */ - return rc; -} -/* - * Reserve a new constant for a double quoted/heredoc string. - */ -static jx9_value * GenStateNewStrObj(jx9_gen_state *pGen,sxi32 *pCount) -{ - jx9_value *pConstObj; - sxu32 nIdx = 0; - /* Reserve a new constant */ - pConstObj = jx9VmReserveConstObj(pGen->pVm, &nIdx); - if( pConstObj == 0 ){ - GenStateOutOfMem(&(*pGen)); - return 0; - } - (*pCount)++; - jx9MemObjInitFromString(pGen->pVm, pConstObj, 0); - /* Emit the load constant instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0); - return pConstObj; -} -/* - * Compile a double quoted/heredoc string. - * According to the JX9 language reference manual - * Heredoc - * A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier - * is provided, then a newline. The string itself follows, and then the same identifier again - * to close the quotation. - * The closing identifier must begin in the first column of the line. Also, the identifier must - * follow the same naming rules as any other label in JX9: it must contain only alphanumeric - * characters and underscores, and must start with a non-digit character or underscore. - * Warning - * It is very important to note that the line with the closing identifier must contain - * no other characters, except possibly a semicolon (;). That means especially that the identifier - * may not be indented, and there may not be any spaces or tabs before or after the semicolon. - * It's also important to realize that the first character before the closing identifier must - * be a newline as defined by the local operating system. This is \n on UNIX systems, including Mac OS X. - * The closing delimiter (possibly followed by a semicolon) must also be followed by a newline. - * If this rule is broken and the closing identifier is not "clean", it will not be considered a closing - * identifier, and JX9 will continue looking for one. If a proper closing identifier is not found before - * the end of the current file, a parse error will result at the last line. - * Heredocs can not be used for initializing object properties. - * Double quoted - * If the string is enclosed in double-quotes ("), JX9 will interpret more escape sequences for special characters: - * Escaped characters Sequence Meaning - * \n linefeed (LF or 0x0A (10) in ASCII) - * \r carriage return (CR or 0x0D (13) in ASCII) - * \t horizontal tab (HT or 0x09 (9) in ASCII) - * \v vertical tab (VT or 0x0B (11) in ASCII) - * \f form feed (FF or 0x0C (12) in ASCII) - * \\ backslash - * \$ dollar sign - * \" double-quote - * \[0-7]{1, 3} the sequence of characters matching the regular expression is a character in octal notation - * \x[0-9A-Fa-f]{1, 2} the sequence of characters matching the regular expression is a character in hexadecimal notation - * As in single quoted strings, escaping any other character will result in the backslash being printed too. - * The most important feature of double-quoted strings is the fact that variable names will be expanded. - * See string parsing for details. - */ -static sxi32 GenStateCompileString(jx9_gen_state *pGen) -{ - SyString *pStr = &pGen->pIn->sData; /* Raw token value */ - const char *zIn, *zCur, *zEnd; - jx9_value *pObj = 0; - sxi32 iCons; - sxi32 rc; - /* Delimit the string */ - zIn = pStr->zString; - zEnd = &zIn[pStr->nByte]; - if( zIn >= zEnd ){ - /* Empty string, load NULL */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0); - return SXRET_OK; - } - zCur = 0; - /* Compile the node */ - iCons = 0; - for(;;){ - zCur = zIn; - while( zIn < zEnd && zIn[0] != '\\' ){ - if(zIn[0] == '$' && &zIn[1] < zEnd && - (((unsigned char)zIn[1] >= 0xc0 || SyisAlpha(zIn[1]) || zIn[1] == '_')) ){ - break; - } - zIn++; - } - if( zIn > zCur ){ - if( pObj == 0 ){ - pObj = GenStateNewStrObj(&(*pGen), &iCons); - if( pObj == 0 ){ - return SXERR_ABORT; - } - } - jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur)); - } - if( zIn >= zEnd ){ - break; - } - if( zIn[0] == '\\' ){ - const char *zPtr = 0; - sxu32 n; - zIn++; - if( zIn >= zEnd ){ - break; - } - if( pObj == 0 ){ - pObj = GenStateNewStrObj(&(*pGen), &iCons); - if( pObj == 0 ){ - return SXERR_ABORT; - } - } - n = sizeof(char); /* size of conversion */ - switch( zIn[0] ){ - case '$': - /* Dollar sign */ - jx9MemObjStringAppend(pObj, "$", sizeof(char)); - break; - case '\\': - /* A literal backslash */ - jx9MemObjStringAppend(pObj, "\\", sizeof(char)); - break; - case 'a': - /* The "alert" character (BEL)[ctrl+g] ASCII code 7 */ - jx9MemObjStringAppend(pObj, "\a", sizeof(char)); - break; - case 'b': - /* Backspace (BS)[ctrl+h] ASCII code 8 */ - jx9MemObjStringAppend(pObj, "\b", sizeof(char)); - break; - case 'f': - /* Form-feed (FF)[ctrl+l] ASCII code 12 */ - jx9MemObjStringAppend(pObj, "\f", sizeof(char)); - break; - case 'n': - /* Line feed(new line) (LF)[ctrl+j] ASCII code 10 */ - jx9MemObjStringAppend(pObj, "\n", sizeof(char)); - break; - case 'r': - /* Carriage return (CR)[ctrl+m] ASCII code 13 */ - jx9MemObjStringAppend(pObj, "\r", sizeof(char)); - break; - case 't': - /* Horizontal tab (HT)[ctrl+i] ASCII code 9 */ - jx9MemObjStringAppend(pObj, "\t", sizeof(char)); - break; - case 'v': - /* Vertical tab(VT)[ctrl+k] ASCII code 11 */ - jx9MemObjStringAppend(pObj, "\v", sizeof(char)); - break; - case '\'': - /* Single quote */ - jx9MemObjStringAppend(pObj, "'", sizeof(char)); - break; - case '"': - /* Double quote */ - jx9MemObjStringAppend(pObj, "\"", sizeof(char)); - break; - case '0': - /* NUL byte */ - jx9MemObjStringAppend(pObj, "\0", sizeof(char)); - break; - case 'x': - if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1]) ){ - int c; - /* Hex digit */ - c = SyHexToint(zIn[1]) << 4; - if( &zIn[2] < zEnd ){ - c += SyHexToint(zIn[2]); - } - /* Output char */ - jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char)); - n += sizeof(char) * 2; - }else{ - /* Output literal character */ - jx9MemObjStringAppend(pObj, "x", sizeof(char)); - } - break; - case 'o': - if( &zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8 ){ - /* Octal digit stream */ - int c; - c = 0; - zIn++; - for( zPtr = zIn ; zPtr < &zIn[3*sizeof(char)] ; zPtr++ ){ - if( zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7 ){ - break; - } - c = c * 8 + (zPtr[0] - '0'); - } - if ( c > 0 ){ - jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char)); - } - n = (sxu32)(zPtr-zIn); - }else{ - /* Output literal character */ - jx9MemObjStringAppend(pObj, "o", sizeof(char)); - } - break; - default: - /* Output without a slash */ - jx9MemObjStringAppend(pObj, zIn, sizeof(char)); - break; - } - /* Advance the stream cursor */ - zIn += n; - continue; - } - if( zIn[0] == '{' ){ - /* Curly syntax */ - const char *zExpr; - sxi32 iNest = 1; - zIn++; - zExpr = zIn; - /* Synchronize with the next closing curly braces */ - while( zIn < zEnd ){ - if( zIn[0] == '{' ){ - /* Increment nesting level */ - iNest++; - }else if(zIn[0] == '}' ){ - /* Decrement nesting level */ - iNest--; - if( iNest <= 0 ){ - break; - } - } - zIn++; - } - /* Process the expression */ - rc = GenStateProcessStringExpression(&(*pGen),zExpr,zIn); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - if( rc != SXERR_EMPTY ){ - ++iCons; - } - if( zIn < zEnd ){ - /* Jump the trailing curly */ - zIn++; - } - }else{ - /* Simple syntax */ - const char *zExpr = zIn; - /* Assemble variable name */ - for(;;){ - /* Jump leading dollars */ - while( zIn < zEnd && zIn[0] == '$' ){ - zIn++; - } - for(;;){ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_' ) ){ - zIn++; - } - if((unsigned char)zIn[0] >= 0xc0 ){ - /* UTF-8 stream */ - zIn++; - while( zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){ - zIn++; - } - continue; - } - break; - } - if( zIn >= zEnd ){ - break; - } - if( zIn[0] == '[' ){ - sxi32 iSquare = 1; - zIn++; - while( zIn < zEnd ){ - if( zIn[0] == '[' ){ - iSquare++; - }else if (zIn[0] == ']' ){ - iSquare--; - if( iSquare <= 0 ){ - break; - } - } - zIn++; - } - if( zIn < zEnd ){ - zIn++; - } - break; - }else if( zIn[0] == '.' ){ - /* Member access operator '.' */ - zIn++; - }else{ - break; - } - } - /* Process the expression */ - rc = GenStateProcessStringExpression(&(*pGen),zExpr, zIn); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - if( rc != SXERR_EMPTY ){ - ++iCons; - } - } - /* Invalidate the previously used constant */ - pObj = 0; - }/*for(;;)*/ - if( iCons > 1 ){ - /* Concatenate all compiled constants */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_CAT, iCons, 0, 0, 0); - } - /* Node successfully compiled */ - return SXRET_OK; -} -/* - * Compile a double quoted string. - * See the block-comment above for more information. - */ -JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag) -{ - sxi32 rc; - rc = GenStateCompileString(&(*pGen)); - SXUNUSED(iCompileFlag); /* cc warning */ - /* Compilation result */ - return rc; -} -/* - * Compile a literal which is an identifier(name) for simple values. - */ -JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag) -{ - SyToken *pToken = pGen->pIn; - jx9_value *pObj; - SyString *pStr; - sxu32 nIdx; - /* Extract token value */ - pStr = &pToken->sData; - /* Deal with the reserved literals [i.e: null, false, true, ...] first */ - if( pStr->nByte == sizeof("NULL") - 1 ){ - if( SyStrnicmp(pStr->zString, "null", sizeof("NULL")-1) == 0 ){ - /* NULL constant are always indexed at 0 */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0); - return SXRET_OK; - }else if( SyStrnicmp(pStr->zString, "true", sizeof("TRUE")-1) == 0 ){ - /* TRUE constant are always indexed at 1 */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1, 0, 0); - return SXRET_OK; - } - }else if (pStr->nByte == sizeof("FALSE") - 1 && - SyStrnicmp(pStr->zString, "false", sizeof("FALSE")-1) == 0 ){ - /* FALSE constant are always indexed at 2 */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 2, 0, 0); - return SXRET_OK; - }else if(pStr->nByte == sizeof("__LINE__") - 1 && - SyMemcmp(pStr->zString, "__LINE__", sizeof("__LINE__")-1) == 0 ){ - /* TICKET 1433-004: __LINE__ constant must be resolved at compile time, not run time */ - pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx); - if( pObj == 0 ){ - SXUNUSED(iCompileFlag); /* cc warning */ - return GenStateOutOfMem(pGen); - } - jx9MemObjInitFromInt(pGen->pVm, pObj, pToken->nLine); - /* Emit the load constant instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0); - return SXRET_OK; - }else if( pStr->nByte == sizeof("__FUNCTION__") - 1 && - SyMemcmp(pStr->zString, "__FUNCTION__", sizeof("__FUNCTION__")-1) == 0 ){ - GenBlock *pBlock = pGen->pCurrent; - /* TICKET 1433-004: __FUNCTION__/__METHOD__ constants must be resolved at compile time, not run time */ - while( pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0 ){ - /* Point to the upper block */ - pBlock = pBlock->pParent; - } - if( pBlock == 0 ){ - /* Called in the global scope, load NULL */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0); - }else{ - /* Extract the target function/method */ - jx9_vm_func *pFunc = (jx9_vm_func *)pBlock->pUserData; - pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx); - if( pObj == 0 ){ - return GenStateOutOfMem(pGen); - } - jx9MemObjInitFromString(pGen->pVm, pObj, &pFunc->sName); - /* Emit the load constant instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0); - } - return SXRET_OK; - } - /* Query literal table */ - if( SXRET_OK != GenStateFindLiteral(&(*pGen), &pToken->sData, &nIdx) ){ - jx9_value *pObj; - /* Unknown literal, install it in the literal table */ - pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx); - if( pObj == 0 ){ - return GenStateOutOfMem(pGen); - } - jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData); - GenStateInstallLiteral(&(*pGen), pObj, nIdx); - } - /* Emit the load constant instruction */ - jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC,1,nIdx, 0, 0); - /* Node successfully compiled */ - return SXRET_OK; -} -/* - * Compile an array entry whether it is a key or a value. - */ -static sxi32 GenStateCompileJSONEntry( - jx9_gen_state *pGen, /* Code generator state */ - SyToken *pIn, /* Token stream */ - SyToken *pEnd, /* End of the token stream */ - sxi32 iFlags, /* Compilation flags */ - sxi32 (*xValidator)(jx9_gen_state *,jx9_expr_node *) /* Expression tree validator callback */ - ) -{ - SyToken *pTmpIn, *pTmpEnd; - sxi32 rc; - /* Swap token stream */ - SWAP_DELIMITER(pGen, pIn, pEnd); - /* Compile the expression*/ - rc = jx9CompileExpr(&(*pGen), iFlags, xValidator); - /* Restore token stream */ - RE_SWAP_DELIMITER(pGen); - return rc; -} -/* - * Compile a Jx9 JSON Array. - */ -JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag) -{ - sxi32 nPair = 0; - SyToken *pCur; - sxi32 rc; - - pGen->pIn++; /* Jump the open square bracket '['*/ - pGen->pEnd--; - SXUNUSED(iCompileFlag); /* cc warning */ - for(;;){ - /* Jump leading commas */ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){ - pGen->pIn++; - } - pCur = pGen->pIn; - if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){ - /* No more entry to process */ - break; - } - /* Compile entry */ - rc = GenStateCompileJSONEntry(&(*pGen),pCur,pGen->pIn,EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - nPair++; - } - /* Emit the load map instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP,nPair,0,0,0); - /* Node successfully compiled */ - return SXRET_OK; -} -/* - * Node validator for a given JSON key. - */ -static sxi32 GenStateJSONObjectKeyNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot) -{ - sxi32 rc = SXRET_OK; - if( pRoot->xCode != jx9CompileVariable && pRoot->xCode != jx9CompileString - && pRoot->xCode != jx9CompileSimpleString && pRoot->xCode != jx9CompileLiteral ){ - /* Unexpected expression */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pRoot->pStart? pRoot->pStart->nLine : 0, - "JSON Object: Unexpected expression, key must be of type string, literal or simple variable"); - if( rc != SXERR_ABORT ){ - rc = SXERR_INVALID; - } - } - return rc; -} -/* - * Compile a Jx9 JSON Object - */ -JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag) -{ - SyToken *pKey, *pCur; - sxi32 nPair = 0; - sxi32 rc; - - pGen->pIn++; /* Jump the open querly braces '{'*/ - pGen->pEnd--; - SXUNUSED(iCompileFlag); /* cc warning */ - for(;;){ - /* Jump leading commas */ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){ - pGen->pIn++; - } - pCur = pGen->pIn; - if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){ - /* No more entry to process */ - break; - } - /* Compile the key */ - pKey = pCur; - while( pCur < pGen->pIn ){ - if( pCur->nType & JX9_TK_COLON /*':'*/ ){ - break; - } - pCur++; - } - rc = SXERR_EMPTY; - if( pCur < pGen->pIn ){ - if( &pCur[1] >= pGen->pIn ){ - /* Missing value */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "JSON Object: Missing entry value"); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - return SXRET_OK; - } - /* Compile the expression holding the key */ - rc = GenStateCompileJSONEntry(&(*pGen), pKey, pCur, - EXPR_FLAG_RDONLY_LOAD /* Do not create the variable if inexistant */, - GenStateJSONObjectKeyNodeValidator /* Node validator callback */ - ); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - pCur++; /* Jump the double colon ':' */ - }else if( pKey == pCur ){ - /* Key is omitted, emit an error */ - jx9GenCompileError(&(*pGen),E_ERROR, pCur->nLine, "JSON Object: Missing entry key"); - pCur++; /* Jump the double colon ':' */ - }else{ - /* Reset back the cursor and point to the entry value */ - pCur = pKey; - } - /* Compile indice value */ - rc = GenStateCompileJSONEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - nPair++; - } - /* Emit the load map instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP, nPair * 2, 1, 0, 0); - /* Node successfully compiled */ - return SXRET_OK; -} -/* - * Compile a function [i.e: print, exit(), include(), ...] which is a langauge - * construct. - */ -JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen,sxi32 iCompileFlag) -{ - SyString *pName; - sxu32 nKeyID; - sxi32 rc; - /* Name of the language construct [i.e: print, die...]*/ - pName = &pGen->pIn->sData; - nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData); - pGen->pIn++; /* Jump the language construct keyword */ - if( nKeyID == JX9_TKWRD_PRINT ){ - SyToken *pTmp, *pNext = 0; - /* Compile arguments one after one */ - pTmp = pGen->pEnd; - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1 /* Boolean true index */, 0, 0); - while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){ - if( pGen->pIn < pNext ){ - pGen->pEnd = pNext; - rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - if( rc != SXERR_EMPTY ){ - /* Ticket 1433-008: Optimization #1: Consume input directly - * without the overhead of a function call. - * This is a very powerful optimization that improve - * performance greatly. - */ - jx9VmEmitInstr(pGen->pVm,JX9_OP_CONSUME,1,0,0,0); - } - } - /* Jump trailing commas */ - while( pNext < pTmp && (pNext->nType & JX9_TK_COMMA) ){ - pNext++; - } - pGen->pIn = pNext; - } - /* Restore token stream */ - pGen->pEnd = pTmp; - }else{ - sxi32 nArg = 0; - sxu32 nIdx = 0; - rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - }else if(rc != SXERR_EMPTY ){ - nArg = 1; - } - if( SXRET_OK != GenStateFindLiteral(&(*pGen), pName, &nIdx) ){ - jx9_value *pObj; - /* Emit the call instruction */ - pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx); - if( pObj == 0 ){ - SXUNUSED(iCompileFlag); /* cc warning */ - return GenStateOutOfMem(pGen); - } - jx9MemObjInitFromString(pGen->pVm, pObj, pName); - /* Install in the literal table */ - GenStateInstallLiteral(&(*pGen), pObj, nIdx); - } - /* Emit the call instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0); - jx9VmEmitInstr(pGen->pVm, JX9_OP_CALL, nArg, 0, 0, 0); - } - /* Node successfully compiled */ - return SXRET_OK; -} -/* - * Compile a node holding a variable declaration. - * According to the J9X language reference - * Variables in JX9 are represented by a dollar sign followed by the name of the variable. - * The variable name is case-sensitive. - * Variable names follow the same rules as other labels in JX9. A valid variable name - * starts with a letter, underscore or any UTF-8 stream, followed by any number of letters - * numbers, or underscores. - * By default, variables are always assigned by value unless the target value is a JSON - * array or a JSON object which is passed by reference. - */ -JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen,sxi32 iCompileFlag) -{ - sxu32 nLine = pGen->pIn->nLine; - SyHashEntry *pEntry; - SyString *pName; - char *zName = 0; - sxi32 iP1; - void *p3; - sxi32 rc; - - pGen->pIn++; /* Jump the dollar sign '$' */ - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){ - /* Invalid variable name */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Invalid variable name"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - return SXRET_OK; - } - /* Extract variable name */ - pName = &pGen->pIn->sData; - /* Advance the stream cursor */ - pGen->pIn++; - pEntry = SyHashGet(&pGen->hVar, (const void *)pName->zString, pName->nByte); - if( pEntry == 0 ){ - /* Duplicate name */ - zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte); - if( zName == 0 ){ - return GenStateOutOfMem(pGen); - } - /* Install in the hashtable */ - SyHashInsert(&pGen->hVar, zName, pName->nByte, zName); - }else{ - /* Name already available */ - zName = (char *)pEntry->pUserData; - } - p3 = (void *)zName; - iP1 = 0; - if( iCompileFlag & EXPR_FLAG_RDONLY_LOAD ){ - if( (iCompileFlag & EXPR_FLAG_LOAD_IDX_STORE) == 0 ){ - /* Read-only load.In other words do not create the variable if inexistant */ - iP1 = 1; - } - } - /* Emit the load instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD, iP1, 0, p3, 0); - /* Node successfully compiled */ - return SXRET_OK; -} -/* Forward declaration */ -static sxi32 GenStateCompileFunc(jx9_gen_state *pGen,SyString *pName,sxi32 iFlags,jx9_vm_func **ppFunc); -/* - * Compile an annoynmous function or a closure. - * According to the JX9 language reference - * Anonymous functions, also known as closures, allow the creation of functions - * which have no specified name. They are most useful as the value of callback - * parameters, but they have many other uses. Closures can also be used as - * the values of variables; Assigning a closure to a variable uses the same - * syntax as any other assignment, including the trailing semicolon: - * Example Anonymous function variable assignment example - * $greet = function($name) - * { - * printf("Hello %s\r\n", $name); - * }; - * $greet('World'); - * $greet('JX9'); - * Note that the implementation of annoynmous function and closure under - * JX9 is completely different from the one used by the engine. - */ -JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen,sxi32 iCompileFlag) -{ - jx9_vm_func *pAnnonFunc; /* Annonymous function body */ - char zName[512]; /* Unique lambda name */ - static int iCnt = 1; /* There is no worry about thread-safety here, because only - * one thread is allowed to compile the script. - */ - jx9_value *pObj; - SyString sName; - sxu32 nIdx; - sxu32 nLen; - sxi32 rc; - - pGen->pIn++; /* Jump the 'function' keyword */ - if( pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){ - pGen->pIn++; - } - /* Reserve a constant for the lambda */ - pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx); - if( pObj == 0 ){ - GenStateOutOfMem(pGen); - SXUNUSED(iCompileFlag); /* cc warning */ - return SXERR_ABORT; - } - /* Generate a unique name */ - nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++); - /* Make sure the generated name is unique */ - while( SyHashGet(&pGen->pVm->hFunction, zName, nLen) != 0 && nLen < sizeof(zName) - 2 ){ - nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++); - } - SyStringInitFromBuf(&sName, zName, nLen); - jx9MemObjInitFromString(pGen->pVm, pObj, &sName); - /* Compile the lambda body */ - rc = GenStateCompileFunc(&(*pGen),&sName,0,&pAnnonFunc); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - /* Emit the load constant instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0); - /* Node successfully compiled */ - return SXRET_OK; -} -/* - * Compile the 'continue' statement. - * According to the JX9 language reference - * continue is used within looping structures to skip the rest of the current loop iteration - * and continue execution at the condition evaluation and then the beginning of the next - * iteration. - * Note: Note that in JX9 the switch statement is considered a looping structure for - * the purposes of continue. - * continue accepts an optional numeric argument which tells it how many levels - * of enclosing loops it should skip to the end of. - * Note: - * continue 0; and continue 1; is the same as running continue;. - */ -static sxi32 jx9CompileContinue(jx9_gen_state *pGen) -{ - GenBlock *pLoop; /* Target loop */ - sxi32 iLevel; /* How many nesting loop to skip */ - sxu32 nLine; - sxi32 rc; - nLine = pGen->pIn->nLine; - iLevel = 0; - /* Jump the 'continue' keyword */ - pGen->pIn++; - if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){ - /* optional numeric argument which tells us how many levels - * of enclosing loops we should skip to the end of. - */ - iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData); - if( iLevel < 2 ){ - iLevel = 0; - } - pGen->pIn++; /* Jump the optional numeric argument */ - } - /* Point to the target loop */ - pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel); - if( pLoop == 0 ){ - /* Illegal continue */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop or switch"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - }else{ - sxu32 nInstrIdx = 0; - /* Emit the unconditional jump to the beginning of the target loop */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx); - if( pLoop->bPostContinue == TRUE ){ - JumpFixup sJumpFix; - /* Post-continue */ - sJumpFix.nJumpType = JX9_OP_JMP; - sJumpFix.nInstrIdx = nInstrIdx; - SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix); - } - } - if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){ - /* Not so fatal, emit a warning only */ - jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'continue' statement"); - } - /* Statement successfully compiled */ - return SXRET_OK; -} -/* - * Compile the 'break' statement. - * According to the JX9 language reference - * break ends execution of the current for, foreach, while, do-while or switch - * structure. - * break accepts an optional numeric argument which tells it how many nested - * enclosing structures are to be broken out of. - */ -static sxi32 jx9CompileBreak(jx9_gen_state *pGen) -{ - GenBlock *pLoop; /* Target loop */ - sxi32 iLevel; /* How many nesting loop to skip */ - sxu32 nLine; - sxi32 rc; - nLine = pGen->pIn->nLine; - iLevel = 0; - /* Jump the 'break' keyword */ - pGen->pIn++; - if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){ - /* optional numeric argument which tells us how many levels - * of enclosing loops we should skip to the end of. - */ - iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData); - if( iLevel < 2 ){ - iLevel = 0; - } - pGen->pIn++; /* Jump the optional numeric argument */ - } - /* Extract the target loop */ - pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel); - if( pLoop == 0 ){ - /* Illegal break */ - rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - }else{ - sxu32 nInstrIdx; - rc = jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nInstrIdx); - if( rc == SXRET_OK ){ - /* Fix the jump later when the jump destination is resolved */ - GenStateNewJumpFixup(pLoop, JX9_OP_JMP, nInstrIdx); - } - } - if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){ - /* Not so fatal, emit a warning only */ - jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'break' statement"); - } - /* Statement successfully compiled */ - return SXRET_OK; -} -/* Forward declaration */ -static sxi32 GenStateCompileChunk(jx9_gen_state *pGen,sxi32 iFlags); -/* - * Compile a JX9 block. - * A block is simply one or more JX9 statements and expressions to compile - * optionally delimited by braces {}. - * Return SXRET_OK on success. Any other return value indicates failure - * and this function takes care of generating the appropriate error - * message. - */ -static sxi32 jx9CompileBlock( - jx9_gen_state *pGen /* Code generator state */ - ) -{ - sxi32 rc; - if( pGen->pIn->nType & JX9_TK_OCB /* '{' */ ){ - sxu32 nLine = pGen->pIn->nLine; - rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_STD, jx9VmInstrLength(pGen->pVm), 0, 0); - if( rc != SXRET_OK ){ - return SXERR_ABORT; - } - pGen->pIn++; - /* Compile until we hit the closing braces '}' */ - for(;;){ - if( pGen->pIn >= pGen->pEnd ){ - /* No more token to process. Missing closing braces */ - jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing closing braces '}'"); - break; - } - if( pGen->pIn->nType & JX9_TK_CCB/*'}'*/ ){ - /* Closing braces found, break immediately*/ - pGen->pIn++; - break; - } - /* Compile a single statement */ - rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - } - GenStateLeaveBlock(&(*pGen), 0); - }else{ - /* Compile a single statement */ - rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - } - /* Jump trailing semi-colons ';' */ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){ - pGen->pIn++; - } - return SXRET_OK; -} -/* - * Compile the gentle 'while' statement. - * According to the JX9 language reference - * while loops are the simplest type of loop in JX9.They behave just like their C counterparts. - * The basic form of a while statement is: - * while (expr) - * statement - * The meaning of a while statement is simple. It tells JX9 to execute the nested statement(s) - * repeatedly, as long as the while expression evaluates to TRUE. The value of the expression - * is checked each time at the beginning of the loop, so even if this value changes during - * the execution of the nested statement(s), execution will not stop until the end of the iteration - * (each time JX9 runs the statements in the loop is one iteration). Sometimes, if the while - * expression evaluates to FALSE from the very beginning, the nested statement(s) won't even be run once. - * Like with the if statement, you can group multiple statements within the same while loop by surrounding - * a group of statements with curly braces, or by using the alternate syntax: - * while (expr): - * statement - * endwhile; - */ -static sxi32 jx9CompileWhile(jx9_gen_state *pGen) -{ - GenBlock *pWhileBlock = 0; - SyToken *pTmp, *pEnd = 0; - sxu32 nFalseJump; - sxu32 nLine; - sxi32 rc; - nLine = pGen->pIn->nLine; - /* Jump the 'while' keyword */ - pGen->pIn++; - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'while' keyword"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - goto Synchronize; - } - /* Jump the left parenthesis '(' */ - pGen->pIn++; - /* Create the loop block */ - rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pWhileBlock); - if( rc != SXRET_OK ){ - return SXERR_ABORT; - } - /* Delimit the condition */ - jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd); - if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){ - /* Empty expression */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'while' keyword"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - } - /* Swap token streams */ - pTmp = pGen->pEnd; - pGen->pEnd = pEnd; - /* Compile the expression */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - if( rc == SXERR_ABORT ){ - /* Expression handler request an operation abort [i.e: Out-of-memory] */ - return SXERR_ABORT; - } - /* Update token stream */ - while(pGen->pIn < pEnd ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - pGen->pIn++; - } - /* Synchronize pointers */ - pGen->pIn = &pEnd[1]; - pGen->pEnd = pTmp; - /* Emit the false jump */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump); - /* Save the instruction index so we can fix it later when the jump destination is resolved */ - GenStateNewJumpFixup(pWhileBlock, JX9_OP_JZ, nFalseJump); - /* Compile the loop body */ - rc = jx9CompileBlock(&(*pGen)); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - /* Emit the unconditional jump to the start of the loop */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pWhileBlock->nFirstInstr, 0, 0); - /* Fix all jumps now the destination is resolved */ - GenStateFixJumps(pWhileBlock, -1, jx9VmInstrLength(pGen->pVm)); - /* Release the loop block */ - GenStateLeaveBlock(pGen, 0); - /* Statement successfully compiled */ - return SXRET_OK; -Synchronize: - /* Synchronize with the first semi-colon ';' so we can avoid - * compiling this erroneous block. - */ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){ - pGen->pIn++; - } - return SXRET_OK; -} -/* - * Compile the complex and powerful 'for' statement. - * According to the JX9 language reference - * for loops are the most complex loops in JX9. They behave like their C counterparts. - * The syntax of a for loop is: - * for (expr1; expr2; expr3) - * statement - * The first expression (expr1) is evaluated (executed) once unconditionally at - * the beginning of the loop. - * In the beginning of each iteration, expr2 is evaluated. If it evaluates to - * TRUE, the loop continues and the nested statement(s) are executed. If it evaluates - * to FALSE, the execution of the loop ends. - * At the end of each iteration, expr3 is evaluated (executed). - * Each of the expressions can be empty or contain multiple expressions separated by commas. - * In expr2, all expressions separated by a comma are evaluated but the result is taken - * from the last part. expr2 being empty means the loop should be run indefinitely - * (JX9 implicitly considers it as TRUE, like C). This may not be as useless as you might - * think, since often you'd want to end the loop using a conditional break statement instead - * of using the for truth expression. - */ -static sxi32 jx9CompileFor(jx9_gen_state *pGen) -{ - SyToken *pTmp, *pPostStart, *pEnd = 0; - GenBlock *pForBlock = 0; - sxu32 nFalseJump; - sxu32 nLine; - sxi32 rc; - nLine = pGen->pIn->nLine; - /* Jump the 'for' keyword */ - pGen->pIn++; - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'for' keyword"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - return SXRET_OK; - } - /* Jump the left parenthesis '(' */ - pGen->pIn++; - /* Delimit the init-expr;condition;post-expr */ - jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd); - if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){ - /* Empty expression */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "for: Invalid expression"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - /* Synchronize */ - pGen->pIn = pEnd; - if( pGen->pIn < pGen->pEnd ){ - pGen->pIn++; - } - return SXRET_OK; - } - /* Swap token streams */ - pTmp = pGen->pEnd; - pGen->pEnd = pEnd; - /* Compile initialization expressions if available */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - /* Pop operand lvalues */ - if( rc == SXERR_ABORT ){ - /* Expression handler request an operation abort [i.e: Out-of-memory] */ - return SXERR_ABORT; - }else if( rc != SXERR_EMPTY ){ - jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0); - } - if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "for: Expected ';' after initialization expressions"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - return SXRET_OK; - } - /* Jump the trailing ';' */ - pGen->pIn++; - /* Create the loop block */ - rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForBlock); - if( rc != SXRET_OK ){ - return SXERR_ABORT; - } - /* Deffer continue jumps */ - pForBlock->bPostContinue = TRUE; - /* Compile the condition */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - if( rc == SXERR_ABORT ){ - /* Expression handler request an operation abort [i.e: Out-of-memory] */ - return SXERR_ABORT; - }else if( rc != SXERR_EMPTY ){ - /* Emit the false jump */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump); - /* Save the instruction index so we can fix it later when the jump destination is resolved */ - GenStateNewJumpFixup(pForBlock, JX9_OP_JZ, nFalseJump); - } - if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "for: Expected ';' after conditionals expressions"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - return SXRET_OK; - } - /* Jump the trailing ';' */ - pGen->pIn++; - /* Save the post condition stream */ - pPostStart = pGen->pIn; - /* Compile the loop body */ - pGen->pIn = &pEnd[1]; /* Jump the trailing parenthesis ')' */ - pGen->pEnd = pTmp; - rc = jx9CompileBlock(&(*pGen)); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - /* Fix post-continue jumps */ - if( SySetUsed(&pForBlock->aPostContFix) > 0 ){ - JumpFixup *aPost; - VmInstr *pInstr; - sxu32 nJumpDest; - sxu32 n; - aPost = (JumpFixup *)SySetBasePtr(&pForBlock->aPostContFix); - nJumpDest = jx9VmInstrLength(pGen->pVm); - for( n = 0 ; n < SySetUsed(&pForBlock->aPostContFix) ; ++n ){ - pInstr = jx9VmGetInstr(pGen->pVm, aPost[n].nInstrIdx); - if( pInstr ){ - /* Fix jump */ - pInstr->iP2 = nJumpDest; - } - } - } - /* compile the post-expressions if available */ - while( pPostStart < pEnd && (pPostStart->nType & JX9_TK_SEMI) ){ - pPostStart++; - } - if( pPostStart < pEnd ){ - SyToken *pTmpIn, *pTmpEnd; - SWAP_DELIMITER(pGen, pPostStart, pEnd); - rc = jx9CompileExpr(&(*pGen), 0, 0); - if( pGen->pIn < pGen->pEnd ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "for: Expected ')' after post-expressions"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - return SXRET_OK; - } - RE_SWAP_DELIMITER(pGen); - if( rc == SXERR_ABORT ){ - /* Expression handler request an operation abort [i.e: Out-of-memory] */ - return SXERR_ABORT; - }else if( rc != SXERR_EMPTY){ - /* Pop operand lvalue */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0); - } - } - /* Emit the unconditional jump to the start of the loop */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForBlock->nFirstInstr, 0, 0); - /* Fix all jumps now the destination is resolved */ - GenStateFixJumps(pForBlock, -1, jx9VmInstrLength(pGen->pVm)); - /* Release the loop block */ - GenStateLeaveBlock(pGen, 0); - /* Statement successfully compiled */ - return SXRET_OK; -} -/* Expression tree validator callback used by the 'foreach' statement. - * Note that only variable expression [i.e: $x; ${'My'.'Var'}; ${$a['key]};...] - * are allowed. - */ -static sxi32 GenStateForEachNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot) -{ - sxi32 rc = SXRET_OK; /* Assume a valid expression tree */ - if( pRoot->xCode != jx9CompileVariable ){ - /* Unexpected expression */ - rc = jx9GenCompileError(&(*pGen), - E_ERROR, - pRoot->pStart? pRoot->pStart->nLine : 0, - "foreach: Expecting a variable name" - ); - if( rc != SXERR_ABORT ){ - rc = SXERR_INVALID; - } - } - return rc; -} -/* - * Compile the 'foreach' statement. - * According to the JX9 language reference - * The foreach construct simply gives an easy way to iterate over arrays. foreach works - * only on arrays (and objects), and will issue an error when you try to use it on a variable - * with a different data type or an uninitialized variable. There are two syntaxes; the second - * is a minor but useful extension of the first: - * foreach (json_array_json_object as $value) - * statement - * foreach (json_array_json_objec as $key,$value) - * statement - * The first form loops over the array given by array_expression. On each loop, the value - * of the current element is assigned to $value and the internal array pointer is advanced - * by one (so on the next loop, you'll be looking at the next element). - * The second form does the same thing, except that the current element's key will be assigned - * to the variable $key on each loop. - * Note: - * When foreach first starts executing, the internal array pointer is automatically reset to the - * first element of the array. This means that you do not need to call reset() before a foreach loop. - */ -static sxi32 jx9CompileForeach(jx9_gen_state *pGen) -{ - SyToken *pCur, *pTmp, *pEnd = 0; - GenBlock *pForeachBlock = 0; - jx9_foreach_info *pInfo; - sxu32 nFalseJump; - VmInstr *pInstr; - sxu32 nLine; - sxi32 rc; - nLine = pGen->pIn->nLine; - /* Jump the 'foreach' keyword */ - pGen->pIn++; - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Expected '('"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - goto Synchronize; - } - /* Jump the left parenthesis '(' */ - pGen->pIn++; - /* Create the loop block */ - rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForeachBlock); - if( rc != SXRET_OK ){ - return SXERR_ABORT; - } - /* Delimit the expression */ - jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd); - if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){ - /* Empty expression */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Missing expression"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - /* Synchronize */ - pGen->pIn = pEnd; - if( pGen->pIn < pGen->pEnd ){ - pGen->pIn++; - } - return SXRET_OK; - } - /* Compile the array expression */ - pCur = pGen->pIn; - while( pCur < pEnd ){ - if( pCur->nType & JX9_TK_KEYWORD ){ - sxi32 nKeywrd = SX_PTR_TO_INT(pCur->pUserData); - if( nKeywrd == JX9_TKWRD_AS ){ - /* Break with the first 'as' found */ - break; - } - } - /* Advance the stream cursor */ - pCur++; - } - if( pCur <= pGen->pIn ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, - "foreach: Missing array/object expression"); - if( rc == SXERR_ABORT ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return SXERR_ABORT; - } - goto Synchronize; - } - /* Swap token streams */ - pTmp = pGen->pEnd; - pGen->pEnd = pCur; - rc = jx9CompileExpr(&(*pGen), 0, 0); - if( rc == SXERR_ABORT ){ - /* Expression handler request an operation abort [i.e: Out-of-memory] */ - return SXERR_ABORT; - } - /* Update token stream */ - while(pGen->pIn < pCur ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Unexpected token '%z'", &pGen->pIn->sData); - if( rc == SXERR_ABORT ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return SXERR_ABORT; - } - pGen->pIn++; - } - pCur++; /* Jump the 'as' keyword */ - pGen->pIn = pCur; - if( pGen->pIn >= pEnd ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key => $value pair"); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - } - /* Create the foreach context */ - pInfo = (jx9_foreach_info *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_foreach_info)); - if( pInfo == 0 ){ - jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Fatal, JX9 engine is running out-of-memory"); - return SXERR_ABORT; - } - /* Zero the structure */ - SyZero(pInfo, sizeof(jx9_foreach_info)); - /* Initialize structure fields */ - SySetInit(&pInfo->aStep, &pGen->pVm->sAllocator, sizeof(jx9_foreach_step *)); - /* Check if we have a key field */ - while( pCur < pEnd && (pCur->nType & JX9_TK_COMMA) == 0 ){ - pCur++; - } - if( pCur < pEnd ){ - /* Compile the expression holding the key name */ - if( pGen->pIn >= pCur ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key"); - if( rc == SXERR_ABORT ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return SXERR_ABORT; - } - }else{ - pGen->pEnd = pCur; - rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator); - if( rc == SXERR_ABORT ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return SXERR_ABORT; - } - pInstr = jx9VmPopInstr(pGen->pVm); - if( pInstr->p3 ){ - /* Record key name */ - SyStringInitFromBuf(&pInfo->sKey, pInstr->p3, SyStrlen((const char *)pInstr->p3)); - } - pInfo->iFlags |= JX9_4EACH_STEP_KEY; - } - pGen->pIn = &pCur[1]; /* Jump the arrow */ - } - pGen->pEnd = pEnd; - if( pGen->pIn >= pEnd ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $value"); - if( rc == SXERR_ABORT ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return SXERR_ABORT; - } - goto Synchronize; - } - /* Compile the expression holding the value name */ - rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator); - if( rc == SXERR_ABORT ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return SXERR_ABORT; - } - pInstr = jx9VmPopInstr(pGen->pVm); - if( pInstr->p3 ){ - /* Record value name */ - SyStringInitFromBuf(&pInfo->sValue, pInstr->p3, SyStrlen((const char *)pInstr->p3)); - } - /* Emit the 'FOREACH_INIT' instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_INIT, 0, 0, pInfo, &nFalseJump); - /* Save the instruction index so we can fix it later when the jump destination is resolved */ - GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_INIT, nFalseJump); - /* Record the first instruction to execute */ - pForeachBlock->nFirstInstr = jx9VmInstrLength(pGen->pVm); - /* Emit the FOREACH_STEP instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_STEP, 0, 0, pInfo, &nFalseJump); - /* Save the instruction index so we can fix it later when the jump destination is resolved */ - GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_STEP, nFalseJump); - /* Compile the loop body */ - pGen->pIn = &pEnd[1]; - pGen->pEnd = pTmp; - rc = jx9CompileBlock(&(*pGen)); - if( rc == SXERR_ABORT ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return SXERR_ABORT; - } - /* Emit the unconditional jump to the start of the loop */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForeachBlock->nFirstInstr, 0, 0); - /* Fix all jumps now the destination is resolved */ - GenStateFixJumps(pForeachBlock, -1,jx9VmInstrLength(pGen->pVm)); - /* Release the loop block */ - GenStateLeaveBlock(pGen, 0); - /* Statement successfully compiled */ - return SXRET_OK; -Synchronize: - /* Synchronize with the first semi-colon ';' so we can avoid - * compiling this erroneous block. - */ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){ - pGen->pIn++; - } - return SXRET_OK; -} -/* - * Compile the infamous if/elseif/else if/else statements. - * According to the JX9 language reference - * The if construct is one of the most important features of many languages JX9 included. - * It allows for conditional execution of code fragments. JX9 features an if structure - * that is similar to that of C: - * if (expr) - * statement - * else construct: - * Often you'd want to execute a statement if a certain condition is met, and a different - * statement if the condition is not met. This is what else is for. else extends an if statement - * to execute a statement in case the expression in the if statement evaluates to FALSE. - * For example, the following code would display a is greater than b if $a is greater than - * $b, and a is NOT greater than b otherwise. - * The else statement is only executed if the if expression evaluated to FALSE, and if there - * were any elseif expressions - only if they evaluated to FALSE as well - * elseif - * elseif, as its name suggests, is a combination of if and else. Like else, it extends - * an if statement to execute a different statement in case the original if expression evaluates - * to FALSE. However, unlike else, it will execute that alternative expression only if the elseif - * conditional expression evaluates to TRUE. For example, the following code would display a is bigger - * than b, a equal to b or a is smaller than b: - * if ($a > $b) { - * print "a is bigger than b"; - * } elseif ($a == $b) { - * print "a is equal to b"; - * } else { - * print "a is smaller than b"; - * } - */ -static sxi32 jx9CompileIf(jx9_gen_state *pGen) -{ - SyToken *pToken, *pTmp, *pEnd = 0; - GenBlock *pCondBlock = 0; - sxu32 nJumpIdx; - sxu32 nKeyID; - sxi32 rc; - /* Jump the 'if' keyword */ - pGen->pIn++; - pToken = pGen->pIn; - /* Create the conditional block */ - rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_COND, jx9VmInstrLength(pGen->pVm), 0, &pCondBlock); - if( rc != SXRET_OK ){ - return SXERR_ABORT; - } - /* Process as many [if/else if/elseif/else] blocks as we can */ - for(;;){ - if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_LPAREN) == 0 ){ - /* Syntax error */ - if( pToken >= pGen->pEnd ){ - pToken--; - } - rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing '('"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - goto Synchronize; - } - /* Jump the left parenthesis '(' */ - pToken++; - /* Delimit the condition */ - jx9DelimitNestedTokens(pToken, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd); - if( pToken >= pEnd || (pEnd->nType & JX9_TK_RPAREN) == 0 ){ - /* Syntax error */ - if( pToken >= pGen->pEnd ){ - pToken--; - } - rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing ')'"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - goto Synchronize; - } - /* Swap token streams */ - SWAP_TOKEN_STREAM(pGen, pToken, pEnd); - /* Compile the condition */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - /* Update token stream */ - while(pGen->pIn < pEnd ){ - jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData); - pGen->pIn++; - } - pGen->pIn = &pEnd[1]; - pGen->pEnd = pTmp; - if( rc == SXERR_ABORT ){ - /* Expression handler request an operation abort [i.e: Out-of-memory] */ - return SXERR_ABORT; - } - /* Emit the false jump */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJumpIdx); - /* Save the instruction index so we can fix it later when the jump destination is resolved */ - GenStateNewJumpFixup(pCondBlock, JX9_OP_JZ, nJumpIdx); - /* Compile the body */ - rc = jx9CompileBlock(&(*pGen)); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){ - break; - } - /* Ensure that the keyword ID is 'else if' or 'else' */ - nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData); - if( (nKeyID & (JX9_TKWRD_ELSE|JX9_TKWRD_ELIF)) == 0 ){ - break; - } - /* Emit the unconditional jump */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJumpIdx); - /* Save the instruction index so we can fix it later when the jump destination is resolved */ - GenStateNewJumpFixup(pCondBlock, JX9_OP_JMP, nJumpIdx); - if( nKeyID & JX9_TKWRD_ELSE ){ - pToken = &pGen->pIn[1]; - if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_KEYWORD) == 0 || - SX_PTR_TO_INT(pToken->pUserData) != JX9_TKWRD_IF ){ - break; - } - pGen->pIn++; /* Jump the 'else' keyword */ - } - pGen->pIn++; /* Jump the 'elseif/if' keyword */ - /* Synchronize cursors */ - pToken = pGen->pIn; - /* Fix the false jump */ - GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm)); - } /* For(;;) */ - /* Fix the false jump */ - GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm)); - if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_KEYWORD) && - (SX_PTR_TO_INT(pGen->pIn->pUserData) & JX9_TKWRD_ELSE) ){ - /* Compile the else block */ - pGen->pIn++; - rc = jx9CompileBlock(&(*pGen)); - if( rc == SXERR_ABORT ){ - - return SXERR_ABORT; - } - } - nJumpIdx = jx9VmInstrLength(pGen->pVm); - /* Fix all unconditional jumps now the destination is resolved */ - GenStateFixJumps(pCondBlock, JX9_OP_JMP, nJumpIdx); - /* Release the conditional block */ - GenStateLeaveBlock(pGen, 0); - /* Statement successfully compiled */ - return SXRET_OK; -Synchronize: - /* Synchronize with the first semi-colon ';' so we can avoid compiling this erroneous block. - */ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){ - pGen->pIn++; - } - return SXRET_OK; -} -/* - * Compile the return statement. - * According to the JX9 language reference - * If called from within a function, the return() statement immediately ends execution - * of the current function, and returns its argument as the value of the function call. - * return() will also end the execution of an eval() statement or script file. - * If called from the global scope, then execution of the current script file is ended. - * If the current script file was include()ed or require()ed, then control is passed back - * to the calling file. Furthermore, if the current script file was include()ed, then the value - * given to return() will be returned as the value of the include() call. If return() is called - * from within the main script file, then script execution end. - * Note that since return() is a language construct and not a function, the parentheses - * surrounding its arguments are not required. It is common to leave them out, and you actually - * should do so as JX9 has less work to do in this case. - * Note: If no parameter is supplied, then the parentheses must be omitted and JX9 is returning NULL instead.. - */ -static sxi32 jx9CompileReturn(jx9_gen_state *pGen) -{ - sxi32 nRet = 0; /* TRUE if there is a return value */ - sxi32 rc; - /* Jump the 'return' keyword */ - pGen->pIn++; - if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){ - /* Compile the expression */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - }else if(rc != SXERR_EMPTY ){ - nRet = 1; - } - } - /* Emit the done instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, nRet, 0, 0, 0); - return SXRET_OK; -} -/* - * Compile the die/exit language construct. - * The role of these constructs is to terminate execution of the script. - * Shutdown functions will always be executed even if exit() is called. - */ -static sxi32 jx9CompileHalt(jx9_gen_state *pGen) -{ - sxi32 nExpr = 0; - sxi32 rc; - /* Jump the die/exit keyword */ - pGen->pIn++; - if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){ - /* Compile the expression */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - }else if(rc != SXERR_EMPTY ){ - nExpr = 1; - } - } - /* Emit the HALT instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_HALT, nExpr, 0, 0, 0); - return SXRET_OK; -} -/* - * Compile the static statement. - * According to the JX9 language reference - * Another important feature of variable scoping is the static variable. - * A static variable exists only in a local function scope, but it does not lose its value - * when program execution leaves this scope. - * Static variables also provide one way to deal with recursive functions. - */ -static sxi32 jx9CompileStatic(jx9_gen_state *pGen) -{ - jx9_vm_func_static_var sStatic; /* Structure describing the static variable */ - jx9_vm_func *pFunc; /* Enclosing function */ - GenBlock *pBlock; - SyString *pName; - char *zDup; - sxu32 nLine; - sxi32 rc; - /* Jump the static keyword */ - nLine = pGen->pIn->nLine; - pGen->pIn++; - /* Extract the enclosing function if any */ - pBlock = pGen->pCurrent; - while( pBlock ){ - if( pBlock->iFlags & GEN_BLOCK_FUNC){ - break; - } - /* Point to the upper block */ - pBlock = pBlock->pParent; - } - if( pBlock == 0 ){ - /* Static statement, called outside of a function body, treat it as a simple variable. */ - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword"); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - goto Synchronize; - } - /* Compile the expression holding the variable */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - }else if( rc != SXERR_EMPTY ){ - /* Emit the POP instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0); - } - return SXRET_OK; - } - pFunc = (jx9_vm_func *)pBlock->pUserData; - /* Make sure we are dealing with a valid statement */ - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd || - (pGen->pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword"); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - goto Synchronize; - } - pGen->pIn++; - /* Extract variable name */ - pName = &pGen->pIn->sData; - pGen->pIn++; /* Jump the var name */ - if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_EQUAL/*'='*/)) == 0 ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "static: Unexpected token '%z'", &pGen->pIn->sData); - goto Synchronize; - } - /* Initialize the structure describing the static variable */ - SySetInit(&sStatic.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr)); - sStatic.nIdx = SXU32_HIGH; /* Not yet created */ - /* Duplicate variable name */ - zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte); - if( zDup == 0 ){ - jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, JX9 engine is running out of memory"); - return SXERR_ABORT; - } - SyStringInitFromBuf(&sStatic.sName, zDup, pName->nByte); - /* Check if we have an expression to compile */ - if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_EQUAL) ){ - SySet *pInstrContainer; - pGen->pIn++; /* Jump the equal '=' sign */ - /* Swap bytecode container */ - pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm); - jx9VmSetByteCodeContainer(pGen->pVm, &sStatic.aByteCode); - /* Compile the expression */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - /* Emit the done instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0); - /* Restore default bytecode container */ - jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); - } - /* Finally save the compiled static variable in the appropriate container */ - SySetPut(&pFunc->aStatic, (const void *)&sStatic); - return SXRET_OK; -Synchronize: - /* Synchronize with the first semi-colon ';', so we can avoid compiling this erroneous - * statement. - */ - while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){ - pGen->pIn++; - } - return SXRET_OK; -} -/* - * Compile the 'const' statement. - * According to the JX9 language reference - * A constant is an identifier (name) for a simple value. As the name suggests, that value - * cannot change during the execution of the script (except for magic constants, which aren't actually constants). - * A constant is case-sensitive by default. By convention, constant identifiers are always uppercase. - * The name of a constant follows the same rules as any label in JX9. A valid constant name starts - * with a letter or underscore, followed by any number of letters, numbers, or underscores. - * As a regular expression it would be expressed thusly: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]* - * Syntax - * You can define a constant by using the define()-function or by using the const keyword outside - * a object definition. Once a constant is defined, it can never be changed or undefined. - * You can get the value of a constant by simply specifying its name. Unlike with variables - * you should not prepend a constant with a $. You can also use the function constant() to read - * a constant's value if you wish to obtain the constant's name dynamically. Use get_defined_constants() - * to get a list of all defined constants. - */ -static sxi32 jx9CompileConstant(jx9_gen_state *pGen) -{ - SySet *pConsCode, *pInstrContainer; - sxu32 nLine = pGen->pIn->nLine; - SyString *pName; - sxi32 rc; - pGen->pIn++; /* Jump the 'const' keyword */ - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){ - /* Invalid constant name */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Invalid constant name"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - goto Synchronize; - } - /* Peek constant name */ - pName = &pGen->pIn->sData; - /* Make sure the constant name isn't reserved */ - if( GenStateIsReservedID(pName) ){ - /* Reserved constant */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Cannot redeclare a reserved constant '%z'", pName); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - goto Synchronize; - } - pGen->pIn++; - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_EQUAL /* '=' */) == 0 ){ - /* Invalid statement*/ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Expected '=' after constant name"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - goto Synchronize; - } - pGen->pIn++; /*Jump the equal sign */ - /* Allocate a new constant value container */ - pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(SySet)); - if( pConsCode == 0 ){ - return GenStateOutOfMem(pGen); - } - SySetInit(pConsCode, &pGen->pVm->sAllocator, sizeof(VmInstr)); - /* Swap bytecode container */ - pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm); - jx9VmSetByteCodeContainer(pGen->pVm, pConsCode); - /* Compile constant value */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - /* Emit the done instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0); - jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); - if( rc == SXERR_ABORT ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return SXERR_ABORT; - } - SySetSetUserData(pConsCode, pGen->pVm); - /* Register the constant */ - rc = jx9VmRegisterConstant(pGen->pVm, pName, jx9VmExpandConstantValue, pConsCode); - if( rc != SXRET_OK ){ - SySetRelease(pConsCode); - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pConsCode); - } - return SXRET_OK; -Synchronize: - /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */ - while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){ - pGen->pIn++; - } - return SXRET_OK; -} -/* - * Compile the uplink construct. - * According to the JX9 language reference - * In JX9 global variables must be declared uplink inside a function if they are going - * to be used in that function. - * Example #1 Using global - * $a = 1; - * $b = 2; - * function Sum() - * { - * uplink $a, $b; - * $b = $a + $b; - * } - * Sum(); - * print $b; - * ?> - * The above script will output 3. By declaring $a and $b global within the function - * all references to either variable will refer to the global version. There is no limit - * to the number of global variables that can be manipulated by a function. - */ -static sxi32 jx9CompileUplink(jx9_gen_state *pGen) -{ - SyToken *pTmp, *pNext = 0; - sxi32 nExpr; - sxi32 rc; - /* Jump the 'uplink' keyword */ - pGen->pIn++; - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_SEMI) ){ - /* Nothing to process */ - return SXRET_OK; - } - pTmp = pGen->pEnd; - nExpr = 0; - while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){ - if( pGen->pIn < pNext ){ - pGen->pEnd = pNext; - if( (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "uplink: Expected variable name"); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - }else{ - pGen->pIn++; - if( pGen->pIn >= pGen->pEnd ){ - /* Emit a warning */ - jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn[-1].nLine, "uplink: Empty variable name"); - }else{ - rc = jx9CompileExpr(&(*pGen), 0, 0); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - }else if(rc != SXERR_EMPTY ){ - nExpr++; - } - } - } - } - /* Next expression in the stream */ - pGen->pIn = pNext; - /* Jump trailing commas */ - while( pGen->pIn < pTmp && (pGen->pIn->nType & JX9_TK_COMMA) ){ - pGen->pIn++; - } - } - /* Restore token stream */ - pGen->pEnd = pTmp; - if( nExpr > 0 ){ - /* Emit the uplink instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_UPLINK, nExpr, 0, 0, 0); - } - return SXRET_OK; -} -/* - * Compile a switch block. - * (See block-comment below for more information) - */ -static sxi32 GenStateCompileSwitchBlock(jx9_gen_state *pGen,sxu32 *pBlockStart) -{ - sxi32 rc = SXRET_OK; - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*':'*/)) == 0 ){ - /* Unexpected token */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - pGen->pIn++; - } - pGen->pIn++; - /* First instruction to execute in this block. */ - *pBlockStart = jx9VmInstrLength(pGen->pVm); - /* Compile the block until we hit a case/default/endswitch keyword - * or the '}' token */ - for(;;){ - if( pGen->pIn >= pGen->pEnd ){ - /* No more input to process */ - break; - } - rc = SXRET_OK; - if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){ - if( pGen->pIn->nType & JX9_TK_CCB /*'}' */ ){ - rc = SXERR_EOF; - break; - } - }else{ - sxi32 nKwrd; - /* Extract the keyword */ - nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); - if( nKwrd == JX9_TKWRD_CASE || nKwrd == JX9_TKWRD_DEFAULT ){ - break; - } - } - /* Compile block */ - rc = jx9CompileBlock(&(*pGen)); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - } - return rc; -} -/* - * Compile a case eXpression. - * (See block-comment below for more information) - */ -static sxi32 GenStateCompileCaseExpr(jx9_gen_state *pGen, jx9_case_expr *pExpr) -{ - SySet *pInstrContainer; - SyToken *pEnd, *pTmp; - sxi32 iNest = 0; - sxi32 rc; - /* Delimit the expression */ - pEnd = pGen->pIn; - while( pEnd < pGen->pEnd ){ - if( pEnd->nType & JX9_TK_LPAREN /*(*/ ){ - /* Increment nesting level */ - iNest++; - }else if( pEnd->nType & JX9_TK_RPAREN /*)*/ ){ - /* Decrement nesting level */ - iNest--; - }else if( pEnd->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*;'*/) && iNest < 1 ){ - break; - } - pEnd++; - } - if( pGen->pIn >= pEnd ){ - rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Empty case expression"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - } - /* Swap token stream */ - pTmp = pGen->pEnd; - pGen->pEnd = pEnd; - pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm); - jx9VmSetByteCodeContainer(pGen->pVm, &pExpr->aByteCode); - rc = jx9CompileExpr(&(*pGen), 0, 0); - /* Emit the done instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0); - jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); - /* Update token stream */ - pGen->pIn = pEnd; - pGen->pEnd = pTmp; - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - return SXRET_OK; -} -/* - * Compile the smart switch statement. - * According to the JX9 language reference manual - * The switch statement is similar to a series of IF statements on the same expression. - * In many occasions, you may want to compare the same variable (or expression) with many - * different values, and execute a different piece of code depending on which value it equals to. - * This is exactly what the switch statement is for. - * Note: Note that unlike some other languages, the continue statement applies to switch and acts - * similar to break. If you have a switch inside a loop and wish to continue to the next iteration - * of the outer loop, use continue 2. - * Note that switch/case does loose comparision. - * It is important to understand how the switch statement is executed in order to avoid mistakes. - * The switch statement executes line by line (actually, statement by statement). - * In the beginning, no code is executed. Only when a case statement is found with a value that - * matches the value of the switch expression does JX9 begin to execute the statements. - * JX9 continues to execute the statements until the end of the switch block, or the first time - * it sees a break statement. If you don't write a break statement at the end of a case's statement list. - * In a switch statement, the condition is evaluated only once and the result is compared to each - * case statement. In an elseif statement, the condition is evaluated again. If your condition - * is more complicated than a simple compare and/or is in a tight loop, a switch may be faster. - * The statement list for a case can also be empty, which simply passes control into the statement - * list for the next case. - * The case expression may be any expression that evaluates to a simple type, that is, integer - * or floating-point numbers and strings. - */ -static sxi32 jx9CompileSwitch(jx9_gen_state *pGen) -{ - GenBlock *pSwitchBlock; - SyToken *pTmp, *pEnd; - jx9_switch *pSwitch; - sxu32 nLine; - sxi32 rc; - nLine = pGen->pIn->nLine; - /* Jump the 'switch' keyword */ - pGen->pIn++; - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'switch' keyword"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - goto Synchronize; - } - /* Jump the left parenthesis '(' */ - pGen->pIn++; - pEnd = 0; /* cc warning */ - /* Create the loop block */ - rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP|GEN_BLOCK_SWITCH, - jx9VmInstrLength(pGen->pVm), 0, &pSwitchBlock); - if( rc != SXRET_OK ){ - return SXERR_ABORT; - } - /* Delimit the condition */ - jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd); - if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){ - /* Empty expression */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'switch' keyword"); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - } - /* Swap token streams */ - pTmp = pGen->pEnd; - pGen->pEnd = pEnd; - /* Compile the expression */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - if( rc == SXERR_ABORT ){ - /* Expression handler request an operation abort [i.e: Out-of-memory] */ - return SXERR_ABORT; - } - /* Update token stream */ - while(pGen->pIn < pEnd ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, - "Switch: Unexpected token '%z'", &pGen->pIn->sData); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - pGen->pIn++; - } - pGen->pIn = &pEnd[1]; - pGen->pEnd = pTmp; - if( pGen->pIn >= pGen->pEnd || &pGen->pIn[1] >= pGen->pEnd || - (pGen->pIn->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_COLON/*:*/)) == 0 ){ - pTmp = pGen->pIn; - if( pTmp >= pGen->pEnd ){ - pTmp--; - } - /* Unexpected token */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pTmp->nLine, "Switch: Unexpected token '%z'", &pTmp->sData); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - goto Synchronize; - } - pGen->pIn++; /* Jump the leading curly braces/colons */ - /* Create the switch blocks container */ - pSwitch = (jx9_switch *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_switch)); - if( pSwitch == 0 ){ - /* Abort compilation */ - return GenStateOutOfMem(pGen); - } - /* Zero the structure */ - SyZero(pSwitch, sizeof(jx9_switch)); - /* Initialize fields */ - SySetInit(&pSwitch->aCaseExpr, &pGen->pVm->sAllocator, sizeof(jx9_case_expr)); - /* Emit the switch instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_SWITCH, 0, 0, pSwitch, 0); - /* Compile case blocks */ - for(;;){ - sxu32 nKwrd; - if( pGen->pIn >= pGen->pEnd ){ - /* No more input to process */ - break; - } - if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){ - if( (pGen->pIn->nType & JX9_TK_CCB /*}*/) == 0 ){ - /* Unexpected token */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'", - &pGen->pIn->sData); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - /* FALL THROUGH */ - } - /* Block compiled */ - break; - } - /* Extract the keyword */ - nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); - if( nKwrd == JX9_TKWRD_DEFAULT ){ - /* - * Accroding to the JX9 language reference manual - * A special case is the default case. This case matches anything - * that wasn't matched by the other cases. - */ - if( pSwitch->nDefault > 0 ){ - /* Default case already compiled */ - rc = jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Switch: 'default' case already compiled"); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - } - pGen->pIn++; /* Jump the 'default' keyword */ - /* Compile the default block */ - rc = GenStateCompileSwitchBlock(pGen,&pSwitch->nDefault); - if( rc == SXERR_ABORT){ - return SXERR_ABORT; - }else if( rc == SXERR_EOF ){ - break; - } - }else if( nKwrd == JX9_TKWRD_CASE ){ - jx9_case_expr sCase; - /* Standard case block */ - pGen->pIn++; /* Jump the 'case' keyword */ - /* initialize the structure */ - SySetInit(&sCase.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr)); - /* Compile the case expression */ - rc = GenStateCompileCaseExpr(pGen, &sCase); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - /* Compile the case block */ - rc = GenStateCompileSwitchBlock(pGen,&sCase.nStart); - /* Insert in the switch container */ - SySetPut(&pSwitch->aCaseExpr, (const void *)&sCase); - if( rc == SXERR_ABORT){ - return SXERR_ABORT; - }else if( rc == SXERR_EOF ){ - break; - } - }else{ - /* Unexpected token */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'", - &pGen->pIn->sData); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - break; - } - } - /* Fix all jumps now the destination is resolved */ - pSwitch->nOut = jx9VmInstrLength(pGen->pVm); - GenStateFixJumps(pSwitchBlock, -1, jx9VmInstrLength(pGen->pVm)); - /* Release the loop block */ - GenStateLeaveBlock(pGen, 0); - if( pGen->pIn < pGen->pEnd ){ - /* Jump the trailing curly braces */ - pGen->pIn++; - } - /* Statement successfully compiled */ - return SXRET_OK; -Synchronize: - /* Synchronize with the first semi-colon */ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){ - pGen->pIn++; - } - return SXRET_OK; -} -/* - * Process default argument values. That is, a function may define C++-style default value - * as follows: - * function makecoffee($type = "cappuccino") - * { - * return "Making a cup of $type.\n"; - * } - * Some features: - * 1 -) Default arguments value can be any complex expression [i.e: function call, annynoymous - * functions, array member, ..] - * 2 -) Full type hinting: (Arguments are automatically casted to the desired type) - * Example: - * function a(string $a){} function b(int $a, string $c, float $d){} - * 3 -) Function overloading!! - * Example: - * function foo($a) { - * return $a.JX9_EOL; - * } - * function foo($a, $b) { - * return $a + $b; - * } - * print foo(5); // Prints "5" - * print foo(5, 2); // Prints "7" - * // Same arg - * function foo(string $a) - * { - * print "a is a string\n"; - * dump($a); - * } - * function foo(int $a) - * { - * print "a is integer\n"; - * dump($a); - * } - * function foo(array $a) - * { - * print "a is an array\n"; - * dump($a); - * } - * foo('This is a great feature'); // a is a string [first foo] - * foo(52); // a is integer [second foo] - * foo(array(14, __TIME__, __DATE__)); // a is an array [third foo] - * Please refer to the official documentation for more information on the powerful extension - * introduced by the JX9 engine. - */ -static sxi32 GenStateProcessArgValue(jx9_gen_state *pGen, jx9_vm_func_arg *pArg, SyToken *pIn, SyToken *pEnd) -{ - SyToken *pTmpIn, *pTmpEnd; - SySet *pInstrContainer; - sxi32 rc; - /* Swap token stream */ - SWAP_DELIMITER(pGen, pIn, pEnd); - pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm); - jx9VmSetByteCodeContainer(pGen->pVm, &pArg->aByteCode); - /* Compile the expression holding the argument value */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - /* Emit the done instruction */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0); - jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); - RE_SWAP_DELIMITER(pGen); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - return SXRET_OK; -} -/* - * Collect function arguments one after one. - * According to the JX9 language reference manual. - * Information may be passed to functions via the argument list, which is a comma-delimited - * list of expressions. - * JX9 supports passing arguments by value (the default), passing by reference - * and default argument values. Variable-length argument lists are also supported, - * see also the function references for func_num_args(), func_get_arg(), and func_get_args() - * for more information. - * Example #1 Passing arrays to functions - * - * Making arguments be passed by reference - * By default, function arguments are passed by value (so that if the value of the argument - * within the function is changed, it does not get changed outside of the function). - * To allow a function to modify its arguments, they must be passed by reference. - * To have an argument to a function always passed by reference, prepend an ampersand (&) - * to the argument name in the function definition: - * Example #2 Passing function parameters by reference - * - * - * JX9 have introduced powerful extension including full type hinting, function overloading - * complex agrument values.Please refer to the official documentation for more information - * on these extension. - */ -static sxi32 GenStateCollectFuncArgs(jx9_vm_func *pFunc, jx9_gen_state *pGen, SyToken *pEnd) -{ - jx9_vm_func_arg sArg; /* Current processed argument */ - SyToken *pCur, *pIn; /* Token stream */ - SyBlob sSig; /* Function signature */ - char *zDup; /* Copy of argument name */ - sxi32 rc; - - pIn = pGen->pIn; - pCur = 0; - SyBlobInit(&sSig, &pGen->pVm->sAllocator); - /* Process arguments one after one */ - for(;;){ - if( pIn >= pEnd ){ - /* No more arguments to process */ - break; - } - SyZero(&sArg, sizeof(jx9_vm_func_arg)); - SySetInit(&sArg.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr)); - if( pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){ - if( pIn->nType & JX9_TK_KEYWORD ){ - sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pIn->pUserData)); - if( nKey & JX9_TKWRD_BOOL ){ - sArg.nType = MEMOBJ_BOOL; - }else if( nKey & JX9_TKWRD_INT ){ - sArg.nType = MEMOBJ_INT; - }else if( nKey & JX9_TKWRD_STRING ){ - sArg.nType = MEMOBJ_STRING; - }else if( nKey & JX9_TKWRD_FLOAT ){ - sArg.nType = MEMOBJ_REAL; - }else{ - jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, - "Invalid argument type '%z', Automatic cast will not be performed", - &pIn->sData); - } - } - pIn++; - } - if( pIn >= pEnd ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Missing argument name"); - return rc; - } - if( pIn >= pEnd || (pIn->nType & JX9_TK_DOLLAR) == 0 || &pIn[1] >= pEnd || (pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){ - /* Invalid argument */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid argument name"); - return rc; - } - pIn++; /* Jump the dollar sign */ - /* Copy argument name */ - zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, SyStringData(&pIn->sData), SyStringLength(&pIn->sData)); - if( zDup == 0 ){ - return GenStateOutOfMem(pGen); - } - SyStringInitFromBuf(&sArg.sName, zDup, SyStringLength(&pIn->sData)); - pIn++; - if( pIn < pEnd ){ - if( pIn->nType & JX9_TK_EQUAL ){ - SyToken *pDefend; - sxi32 iNest = 0; - pIn++; /* Jump the equal sign */ - pDefend = pIn; - /* Process the default value associated with this argument */ - while( pDefend < pEnd ){ - if( (pDefend->nType & JX9_TK_COMMA) && iNest <= 0 ){ - break; - } - if( pDefend->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*[*/) ){ - /* Increment nesting level */ - iNest++; - }else if( pDefend->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*]*/) ){ - /* Decrement nesting level */ - iNest--; - } - pDefend++; - } - if( pIn >= pDefend ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Missing argument default value"); - return rc; - } - /* Process default value */ - rc = GenStateProcessArgValue(&(*pGen), &sArg, pIn, pDefend); - if( rc != SXRET_OK ){ - return rc; - } - /* Point beyond the default value */ - pIn = pDefend; - } - if( pIn < pEnd && (pIn->nType & JX9_TK_COMMA) == 0 ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Unexpected token '%z'", &pIn->sData); - return rc; - } - pIn++; /* Jump the trailing comma */ - } - /* Append argument signature */ - if( sArg.nType > 0 ){ - int c; - c = 'n'; /* cc warning */ - /* Type leading character */ - switch(sArg.nType){ - case MEMOBJ_HASHMAP: - /* Hashmap aka 'array' */ - c = 'h'; - break; - case MEMOBJ_INT: - /* Integer */ - c = 'i'; - break; - case MEMOBJ_BOOL: - /* Bool */ - c = 'b'; - break; - case MEMOBJ_REAL: - /* Float */ - c = 'f'; - break; - case MEMOBJ_STRING: - /* String */ - c = 's'; - break; - default: - break; - } - SyBlobAppend(&sSig, (const void *)&c, sizeof(char)); - } - /* Save in the argument set */ - SySetPut(&pFunc->aArgs, (const void *)&sArg); - } - if( SyBlobLength(&sSig) > 0 ){ - /* Save function signature */ - SyStringInitFromBuf(&pFunc->sSignature, SyBlobData(&sSig), SyBlobLength(&sSig)); - } - return SXRET_OK; -} -/* - * Compile function [i.e: standard function, annonymous function or closure ] body. - * Return SXRET_OK on success. Any other return value indicates failure - * and this routine takes care of generating the appropriate error message. - */ -static sxi32 GenStateCompileFuncBody( - jx9_gen_state *pGen, /* Code generator state */ - jx9_vm_func *pFunc /* Function state */ - ) -{ - SySet *pInstrContainer; /* Instruction container */ - GenBlock *pBlock; - sxi32 rc; - /* Attach the new function */ - rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC,jx9VmInstrLength(pGen->pVm), pFunc, &pBlock); - if( rc != SXRET_OK ){ - return GenStateOutOfMem(pGen); - } - /* Swap bytecode containers */ - pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm); - jx9VmSetByteCodeContainer(pGen->pVm, &pFunc->aByteCode); - /* Compile the body */ - jx9CompileBlock(&(*pGen)); - /* Emit the final return if not yet done */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, 0, 0, 0, 0); - /* Restore the default container */ - jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); - /* Leave function block */ - GenStateLeaveBlock(&(*pGen), 0); - if( rc == SXERR_ABORT ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return SXERR_ABORT; - } - /* All done, function body compiled */ - return SXRET_OK; -} -/* - * Compile a JX9 function whether is a Standard or Annonymous function. - * According to the JX9 language reference manual. - * Function names follow the same rules as other labels in JX9. A valid function name - * starts with a letter or underscore, followed by any number of letters, numbers, or - * underscores. As a regular expression, it would be expressed thus: - * [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*. - * Functions need not be defined before they are referenced. - * All functions and objectes in JX9 have the global scope - they can be called outside - * a function even if they were defined inside and vice versa. - * It is possible to call recursive functions in JX9. However avoid recursive function/method - * calls with over 32-64 recursion levels. - * - * JX9 have introduced powerful extension including full type hinting, function overloading, - * complex agrument values and more. Please refer to the official documentation for more information - * on these extension. - */ -static sxi32 GenStateCompileFunc( - jx9_gen_state *pGen, /* Code generator state */ - SyString *pName, /* Function name. NULL otherwise */ - sxi32 iFlags, /* Control flags */ - jx9_vm_func **ppFunc /* OUT: function state */ - ) -{ - jx9_vm_func *pFunc; - SyToken *pEnd; - sxu32 nLine; - char *zName; - sxi32 rc; - /* Extract line number */ - nLine = pGen->pIn->nLine; - /* Jump the left parenthesis '(' */ - pGen->pIn++; - /* Delimit the function signature */ - jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd); - if( pEnd >= pGen->pEnd ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Missing ')' after function '%z' signature", pName); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - pGen->pIn = pGen->pEnd; - return SXRET_OK; - } - /* Create the function state */ - pFunc = (jx9_vm_func *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_vm_func)); - if( pFunc == 0 ){ - goto OutOfMem; - } - /* function ID */ - zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte); - if( zName == 0 ){ - /* Don't worry about freeing memory, everything will be released shortly */ - goto OutOfMem; - } - /* Initialize the function state */ - jx9VmInitFuncState(pGen->pVm, pFunc, zName, pName->nByte, iFlags, 0); - if( pGen->pIn < pEnd ){ - /* Collect function arguments */ - rc = GenStateCollectFuncArgs(pFunc, &(*pGen), pEnd); - if( rc == SXERR_ABORT ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return SXERR_ABORT; - } - } - /* Compile function body */ - pGen->pIn = &pEnd[1]; - /* Compile the body */ - rc = GenStateCompileFuncBody(&(*pGen), pFunc); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - if( ppFunc ){ - *ppFunc = pFunc; - } - /* Finally register the function */ - rc = jx9VmInstallUserFunction(pGen->pVm, pFunc, 0); - return rc; - /* Fall through if something goes wrong */ -OutOfMem: - /* If the supplied memory subsystem is so sick that we are unable to allocate - * a tiny chunk of memory, there is no much we can do here. - */ - return GenStateOutOfMem(pGen); -} -/* - * Compile a standard JX9 function. - * Refer to the block-comment above for more information. - */ -static sxi32 jx9CompileFunction(jx9_gen_state *pGen) -{ - SyString *pName; - sxi32 iFlags; - sxu32 nLine; - sxi32 rc; - - nLine = pGen->pIn->nLine; - pGen->pIn++; /* Jump the 'function' keyword */ - iFlags = 0; - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){ - /* Invalid function name */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Invalid function name"); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - /* Sychronize with the next semi-colon or braces*/ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){ - pGen->pIn++; - } - return SXRET_OK; - } - pName = &pGen->pIn->sData; - nLine = pGen->pIn->nLine; - /* Jump the function name */ - pGen->pIn++; - if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after function name '%z'", pName); - if( rc == SXERR_ABORT ){ - /* Error count limit reached, abort immediately */ - return SXERR_ABORT; - } - /* Sychronize with the next semi-colon or '{' */ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){ - pGen->pIn++; - } - return SXRET_OK; - } - /* Compile function body */ - rc = GenStateCompileFunc(&(*pGen),pName,iFlags,0); - return rc; -} -/* - * Generate bytecode for a given expression tree. - * If something goes wrong while generating bytecode - * for the expression tree (A very unlikely scenario) - * this function takes care of generating the appropriate - * error message. - */ -static sxi32 GenStateEmitExprCode( - jx9_gen_state *pGen, /* Code generator state */ - jx9_expr_node *pNode, /* Root of the expression tree */ - sxi32 iFlags /* Control flags */ - ) -{ - VmInstr *pInstr; - sxu32 nJmpIdx; - sxi32 iP1 = 0; - sxu32 iP2 = 0; - void *p3 = 0; - sxi32 iVmOp; - sxi32 rc; - if( pNode->xCode ){ - SyToken *pTmpIn, *pTmpEnd; - /* Compile node */ - SWAP_DELIMITER(pGen, pNode->pStart, pNode->pEnd); - rc = pNode->xCode(&(*pGen), iFlags); - RE_SWAP_DELIMITER(pGen); - return rc; - } - if( pNode->pOp == 0 ){ - jx9GenCompileError(&(*pGen), E_ERROR, pNode->pStart->nLine, - "Invalid expression node, JX9 is aborting compilation"); - return SXERR_ABORT; - } - iVmOp = pNode->pOp->iVmOp; - if( pNode->pOp->iOp == EXPR_OP_QUESTY ){ - sxu32 nJz, nJmp; - /* Ternary operator require special handling */ - /* Phase#1: Compile the condition */ - rc = GenStateEmitExprCode(&(*pGen), pNode->pCond, iFlags); - if( rc != SXRET_OK ){ - return rc; - } - nJz = nJmp = 0; /* cc -O6 warning */ - /* Phase#2: Emit the false jump */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJz); - if( pNode->pLeft ){ - /* Phase#3: Compile the 'then' expression */ - rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags); - if( rc != SXRET_OK ){ - return rc; - } - } - /* Phase#4: Emit the unconditional jump */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJmp); - /* Phase#5: Fix the false jump now the jump destination is resolved. */ - pInstr = jx9VmGetInstr(pGen->pVm, nJz); - if( pInstr ){ - pInstr->iP2 = jx9VmInstrLength(pGen->pVm); - } - /* Phase#6: Compile the 'else' expression */ - if( pNode->pRight ){ - rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags); - if( rc != SXRET_OK ){ - return rc; - } - } - if( nJmp > 0 ){ - /* Phase#7: Fix the unconditional jump */ - pInstr = jx9VmGetInstr(pGen->pVm, nJmp); - if( pInstr ){ - pInstr->iP2 = jx9VmInstrLength(pGen->pVm); - } - } - /* All done */ - return SXRET_OK; - } - /* Generate code for the left tree */ - if( pNode->pLeft ){ - if( iVmOp == JX9_OP_CALL ){ - jx9_expr_node **apNode; - sxi32 n; - /* Recurse and generate bytecodes for function arguments */ - apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs); - /* Read-only load */ - iFlags |= EXPR_FLAG_RDONLY_LOAD; - for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){ - rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE); - if( rc != SXRET_OK ){ - return rc; - } - } - /* Total number of given arguments */ - iP1 = (sxi32)SySetUsed(&pNode->aNodeArgs); - /* Remove stale flags now */ - iFlags &= ~EXPR_FLAG_RDONLY_LOAD; - } - rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags); - if( rc != SXRET_OK ){ - return rc; - } - if( iVmOp == JX9_OP_CALL ){ - pInstr = jx9VmPeekInstr(pGen->pVm); - if( pInstr ){ - if ( pInstr->iOp == JX9_OP_LOADC ){ - /* Prevent constant expansion */ - pInstr->iP1 = 0; - }else if( pInstr->iOp == JX9_OP_MEMBER /* $a.b(1, 2, 3) */ ){ - /* Annonymous function call, flag that */ - pInstr->iP2 = 1; - } - } - }else if( iVmOp == JX9_OP_LOAD_IDX ){ - jx9_expr_node **apNode; - sxi32 n; - /* Recurse and generate bytecodes for array index */ - apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs); - for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){ - rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE); - if( rc != SXRET_OK ){ - return rc; - } - } - if( SySetUsed(&pNode->aNodeArgs) > 0 ){ - iP1 = 1; /* Node have an index associated with it */ - } - if( iFlags & EXPR_FLAG_LOAD_IDX_STORE ){ - /* Create an empty entry when the desired index is not found */ - iP2 = 1; - } - }else if( pNode->pOp->iOp == EXPR_OP_COMMA ){ - /* POP the left node */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0); - } - } - rc = SXRET_OK; - nJmpIdx = 0; - /* Generate code for the right tree */ - if( pNode->pRight ){ - if( iVmOp == JX9_OP_LAND ){ - /* Emit the false jump so we can short-circuit the logical and */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx); - }else if (iVmOp == JX9_OP_LOR ){ - /* Emit the true jump so we can short-circuit the logical or*/ - jx9VmEmitInstr(pGen->pVm, JX9_OP_JNZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx); - }else if( pNode->pOp->iPrec == 18 /* Combined binary operators [i.e: =, '.=', '+=', *=' ...] precedence */ ){ - iFlags |= EXPR_FLAG_LOAD_IDX_STORE; - } - rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags); - if( iVmOp == JX9_OP_STORE ){ - pInstr = jx9VmPeekInstr(pGen->pVm); - if( pInstr ){ - if(pInstr->iOp == JX9_OP_MEMBER ){ - /* Perform a member store operation [i.e: $this.x = 50] */ - iP2 = 1; - }else{ - if( pInstr->iOp == JX9_OP_LOAD_IDX ){ - /* Transform the STORE instruction to STORE_IDX instruction */ - iVmOp = JX9_OP_STORE_IDX; - iP1 = pInstr->iP1; - }else{ - p3 = pInstr->p3; - } - /* POP the last dynamic load instruction */ - (void)jx9VmPopInstr(pGen->pVm); - } - } - } - } - if( iVmOp > 0 ){ - if( iVmOp == JX9_OP_INCR || iVmOp == JX9_OP_DECR ){ - if( pNode->iFlags & EXPR_NODE_PRE_INCR ){ - /* Pre-increment/decrement operator [i.e: ++$i, --$j ] */ - iP1 = 1; - } - } - /* Finally, emit the VM instruction associated with this operator */ - jx9VmEmitInstr(pGen->pVm, iVmOp, iP1, iP2, p3, 0); - if( nJmpIdx > 0 ){ - /* Fix short-circuited jumps now the destination is resolved */ - pInstr = jx9VmGetInstr(pGen->pVm, nJmpIdx); - if( pInstr ){ - pInstr->iP2 = jx9VmInstrLength(pGen->pVm); - } - } - } - return rc; -} -/* - * Compile a JX9 expression. - * According to the JX9 language reference manual: - * Expressions are the most important building stones of JX9. - * In JX9, almost anything you write is an expression. - * The simplest yet most accurate way to define an expression - * is "anything that has a value". - * If something goes wrong while compiling the expression, this - * function takes care of generating the appropriate error - * message. - */ -static sxi32 jx9CompileExpr( - jx9_gen_state *pGen, /* Code generator state */ - sxi32 iFlags, /* Control flags */ - sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */ - ) -{ - jx9_expr_node *pRoot; - SySet sExprNode; - SyToken *pEnd; - sxi32 nExpr; - sxi32 iNest; - sxi32 rc; - /* Initialize worker variables */ - nExpr = 0; - pRoot = 0; - SySetInit(&sExprNode, &pGen->pVm->sAllocator, sizeof(jx9_expr_node *)); - SySetAlloc(&sExprNode, 0x10); - rc = SXRET_OK; - /* Delimit the expression */ - pEnd = pGen->pIn; - iNest = 0; - while( pEnd < pGen->pEnd ){ - if( pEnd->nType & JX9_TK_OCB /* '{' */ ){ - /* Ticket 1433-30: Annonymous/Closure functions body */ - iNest++; - }else if(pEnd->nType & JX9_TK_CCB /* '}' */ ){ - iNest--; - }else if( pEnd->nType & JX9_TK_SEMI /* ';' */ ){ - if( iNest <= 0 ){ - break; - } - } - pEnd++; - } - if( iFlags & EXPR_FLAG_COMMA_STATEMENT ){ - SyToken *pEnd2 = pGen->pIn; - iNest = 0; - /* Stop at the first comma */ - while( pEnd2 < pEnd ){ - if( pEnd2->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_LPAREN/*'('*/) ){ - iNest++; - }else if(pEnd2->nType & (JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*']'*/|JX9_TK_RPAREN/*')'*/)){ - iNest--; - }else if( pEnd2->nType & JX9_TK_COMMA /*','*/ ){ - if( iNest <= 0 ){ - break; - } - } - pEnd2++; - } - if( pEnd2 pGen->pIn ){ - SyToken *pTmp = pGen->pEnd; - /* Swap delimiter */ - pGen->pEnd = pEnd; - /* Try to get an expression tree */ - rc = jx9ExprMakeTree(&(*pGen), &sExprNode, &pRoot); - if( rc == SXRET_OK && pRoot ){ - rc = SXRET_OK; - if( xTreeValidator ){ - /* Call the upper layer validator callback */ - rc = xTreeValidator(&(*pGen), pRoot); - } - if( rc != SXERR_ABORT ){ - /* Generate code for the given tree */ - rc = GenStateEmitExprCode(&(*pGen), pRoot, iFlags); - } - nExpr = 1; - } - /* Release the whole tree */ - jx9ExprFreeTree(&(*pGen), &sExprNode); - /* Synchronize token stream */ - pGen->pEnd = pTmp; - pGen->pIn = pEnd; - if( rc == SXERR_ABORT ){ - SySetRelease(&sExprNode); - return SXERR_ABORT; - } - } - SySetRelease(&sExprNode); - return nExpr > 0 ? SXRET_OK : SXERR_EMPTY; -} -/* - * Return a pointer to the node construct handler associated - * with a given node type [i.e: string, integer, float, ...]. - */ -JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType) -{ - if( nNodeType & JX9_TK_NUM ){ - /* Numeric literal: Either real or integer */ - return jx9CompileNumLiteral; - }else if( nNodeType & JX9_TK_DSTR ){ - /* Double quoted string */ - return jx9CompileString; - }else if( nNodeType & JX9_TK_SSTR ){ - /* Single quoted string */ - return jx9CompileSimpleString; - }else if( nNodeType & JX9_TK_NOWDOC ){ - /* Nowdoc */ - return jx9CompileNowdoc; - } - return 0; -} -/* - * Jx9 Language construct table. - */ -static const LangConstruct aLangConstruct[] = { - { JX9_TKWRD_IF, jx9CompileIf }, - { JX9_TKWRD_FUNCTION, jx9CompileFunction }, - { JX9_TKWRD_FOREACH, jx9CompileForeach }, - { JX9_TKWRD_WHILE, jx9CompileWhile }, - { JX9_TKWRD_FOR, jx9CompileFor }, - { JX9_TKWRD_SWITCH, jx9CompileSwitch }, - { JX9_TKWRD_DIE, jx9CompileHalt }, - { JX9_TKWRD_EXIT, jx9CompileHalt }, - { JX9_TKWRD_RETURN, jx9CompileReturn }, - { JX9_TKWRD_BREAK, jx9CompileBreak }, - { JX9_TKWRD_CONTINUE, jx9CompileContinue }, - { JX9_TKWRD_STATIC, jx9CompileStatic }, - { JX9_TKWRD_UPLINK, jx9CompileUplink }, - { JX9_TKWRD_CONST, jx9CompileConstant }, -}; -/* - * Return a pointer to the statement handler routine associated - * with a given JX9 keyword [i.e: if, for, while, ...]. - */ -static ProcLangConstruct GenStateGetStatementHandler( - sxu32 nKeywordID /* Keyword ID*/ - ) -{ - sxu32 n = 0; - for(;;){ - if( n >= SX_ARRAYSIZE(aLangConstruct) ){ - break; - } - if( aLangConstruct[n].nID == nKeywordID ){ - /* Return a pointer to the handler. - */ - return aLangConstruct[n].xConstruct; - } - n++; - } - /* Not a language construct */ - return 0; -} -/* - * Compile a jx9 program. - * If something goes wrong while compiling the Jx9 chunk, this function - * takes care of generating the appropriate error message. - */ -static sxi32 GenStateCompileChunk( - jx9_gen_state *pGen, /* Code generator state */ - sxi32 iFlags /* Compile flags */ - ) -{ - ProcLangConstruct xCons; - sxi32 rc; - rc = SXRET_OK; /* Prevent compiler warning */ - for(;;){ - if( pGen->pIn >= pGen->pEnd ){ - /* No more input to process */ - break; - } - xCons = 0; - if( pGen->pIn->nType & JX9_TK_KEYWORD ){ - sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData); - /* Try to extract a language construct handler */ - xCons = GenStateGetStatementHandler(nKeyword); - if( xCons == 0 && !jx9IsLangConstruct(nKeyword) ){ - rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Syntax error: Unexpected keyword '%z'", - &pGen->pIn->sData); - if( rc == SXERR_ABORT ){ - break; - } - /* Synchronize with the first semi-colon and avoid compiling - * this erroneous statement. - */ - xCons = jx9ErrorRecover; - } - } - if( xCons == 0 ){ - /* Assume an expression an try to compile it */ - rc = jx9CompileExpr(&(*pGen), 0, 0); - if( rc != SXERR_EMPTY ){ - /* Pop l-value */ - jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0); - } - }else{ - /* Go compile the sucker */ - rc = xCons(&(*pGen)); - } - if( rc == SXERR_ABORT ){ - /* Request to abort compilation */ - break; - } - /* Ignore trailing semi-colons ';' */ - while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){ - pGen->pIn++; - } - if( iFlags & JX9_COMPILE_SINGLE_STMT ){ - /* Compile a single statement and return */ - break; - } - /* LOOP ONE */ - /* LOOP TWO */ - /* LOOP THREE */ - /* LOOP FOUR */ - } - /* Return compilation status */ - return rc; -} -/* - * Compile a raw chunk. The raw chunk can contain JX9 code embedded - * in HTML, XML and so on. This function handle all the stuff. - * This is the only compile interface exported from this file. - */ -JX9_PRIVATE sxi32 jx9CompileScript( - jx9_vm *pVm, /* Generate JX9 bytecodes for this Virtual Machine */ - SyString *pScript, /* Script to compile */ - sxi32 iFlags /* Compile flags */ - ) -{ - jx9_gen_state *pGen; - SySet aToken; - sxi32 rc; - if( pScript->nByte < 1 ){ - /* Nothing to compile */ - return JX9_OK; - } - /* Initialize the tokens containers */ - SySetInit(&aToken, &pVm->sAllocator, sizeof(SyToken)); - SySetAlloc(&aToken, 0xc0); - pGen = &pVm->sCodeGen; - rc = JX9_OK; - /* Tokenize the JX9 chunk first */ - jx9Tokenize(pScript->zString,pScript->nByte,&aToken); - if( SySetUsed(&aToken) < 1 ){ - return SXERR_EMPTY; - } - /* Point to the head and tail of the token stream. */ - pGen->pIn = (SyToken *)SySetBasePtr(&aToken); - pGen->pEnd = &pGen->pIn[SySetUsed(&aToken)]; - /* Compile the chunk */ - rc = GenStateCompileChunk(pGen,iFlags); - /* Cleanup */ - SySetRelease(&aToken); - return rc; -} -/* - * Utility routines.Initialize the code generator. - */ -JX9_PRIVATE sxi32 jx9InitCodeGenerator( - jx9_vm *pVm, /* Target VM */ - ProcConsumer xErr, /* Error log consumer callabck */ - void *pErrData /* Last argument to xErr() */ - ) -{ - jx9_gen_state *pGen = &pVm->sCodeGen; - /* Zero the structure */ - SyZero(pGen, sizeof(jx9_gen_state)); - /* Initial state */ - pGen->pVm = &(*pVm); - pGen->xErr = xErr; - pGen->pErrData = pErrData; - SyHashInit(&pGen->hLiteral, &pVm->sAllocator, 0, 0); - SyHashInit(&pGen->hVar, &pVm->sAllocator, 0, 0); - /* Create the global scope */ - GenStateInitBlock(pGen, &pGen->sGlobal,GEN_BLOCK_GLOBAL,jx9VmInstrLength(&(*pVm)), 0); - /* Point to the global scope */ - pGen->pCurrent = &pGen->sGlobal; - return SXRET_OK; -} -/* - * Utility routines. Reset the code generator to it's initial state. - */ -JX9_PRIVATE sxi32 jx9ResetCodeGenerator( - jx9_vm *pVm, /* Target VM */ - ProcConsumer xErr, /* Error log consumer callabck */ - void *pErrData /* Last argument to xErr() */ - ) -{ - jx9_gen_state *pGen = &pVm->sCodeGen; - GenBlock *pBlock, *pParent; - /* Point to the global scope */ - pBlock = pGen->pCurrent; - while( pBlock->pParent != 0 ){ - pParent = pBlock->pParent; - GenStateFreeBlock(pBlock); - pBlock = pParent; - } - pGen->xErr = xErr; - pGen->pErrData = pErrData; - pGen->pCurrent = &pGen->sGlobal; - pGen->pIn = pGen->pEnd = 0; - pGen->nErr = 0; - return SXRET_OK; -} -/* - * Generate a compile-time error message. - * If the error count limit is reached (usually 15 error message) - * this function return SXERR_ABORT.In that case upper-layers must - * abort compilation immediately. - */ -JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen,sxi32 nErrType,sxu32 nLine,const char *zFormat,...) -{ - SyBlob *pWorker = &pGen->pVm->pEngine->xConf.sErrConsumer; - const char *zErr = "Error"; - va_list ap; - if( nErrType == E_ERROR ){ - /* Increment the error counter */ - pGen->nErr++; - if( pGen->nErr > 15 ){ - /* Error count limit reached */ - SyBlobFormat(pWorker, "%u Error count limit reached, JX9 is aborting compilation\n", nLine); - /* Abort immediately */ - return SXERR_ABORT; - } - } - switch(nErrType){ - case E_WARNING: zErr = "Warning"; break; - case E_PARSE: zErr = "Parse error"; break; - case E_NOTICE: zErr = "Notice"; break; - default: - break; - } - /* Format the error message */ - SyBlobFormat(pWorker, "%u %s: ", nLine, zErr); - va_start(ap, zFormat); - SyBlobFormatAp(pWorker, zFormat, ap); - va_end(ap); - /* Append a new line */ - SyBlobAppend(pWorker, (const void *)"\n", sizeof(char)); - return JX9_OK; -} -/* - * ---------------------------------------------------------- - * File: jx9_const.c - * MD5: f3980b00dd1eda0bb2b749424a8dfffe - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: const.c v1.7 Win7 2012-12-13 00:01 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* This file implement built-in constants for the JX9 engine. */ -/* - * JX9_VERSION - * __JX9__ - * Expand the current version of the JX9 engine. - */ -static void JX9_VER_Const(jx9_value *pVal, void *pUnused) -{ - SXUNUSED(pUnused); - jx9_value_string(pVal, jx9_lib_signature(), -1/*Compute length automatically*/); -} -#ifdef __WINNT__ -#include -#elif defined(__UNIXES__) -#include -#endif -/* - * JX9_OS - * __OS__ - * Expand the name of the host Operating System. - */ -static void JX9_OS_Const(jx9_value *pVal, void *pUnused) -{ -#if defined(__WINNT__) - jx9_value_string(pVal, "WinNT", (int)sizeof("WinNT")-1); -#elif defined(__UNIXES__) - struct utsname sInfo; - if( uname(&sInfo) != 0 ){ - jx9_value_string(pVal, "Unix", (int)sizeof("Unix")-1); - }else{ - jx9_value_string(pVal, sInfo.sysname, -1); - } -#else - jx9_value_string(pVal,"Host OS", (int)sizeof("Host OS")-1); -#endif - SXUNUSED(pUnused); -} -/* - * JX9_EOL - * Expand the correct 'End Of Line' symbol for this platform. - */ -static void JX9_EOL_Const(jx9_value *pVal, void *pUnused) -{ - SXUNUSED(pUnused); -#ifdef __WINNT__ - jx9_value_string(pVal, "\r\n", (int)sizeof("\r\n")-1); -#else - jx9_value_string(pVal, "\n", (int)sizeof(char)); -#endif -} -/* - * JX9_INT_MAX - * Expand the largest integer supported. - * Note that JX9 deals with 64-bit integer for all platforms. - */ -static void JX9_INTMAX_Const(jx9_value *pVal, void *pUnused) -{ - SXUNUSED(pUnused); - jx9_value_int64(pVal, SXI64_HIGH); -} -/* - * JX9_INT_SIZE - * Expand the size in bytes of a 64-bit integer. - */ -static void JX9_INTSIZE_Const(jx9_value *pVal, void *pUnused) -{ - SXUNUSED(pUnused); - jx9_value_int64(pVal, sizeof(sxi64)); -} -/* - * DIRECTORY_SEPARATOR. - * Expand the directory separator character. - */ -static void JX9_DIRSEP_Const(jx9_value *pVal, void *pUnused) -{ - SXUNUSED(pUnused); -#ifdef __WINNT__ - jx9_value_string(pVal, "\\", (int)sizeof(char)); -#else - jx9_value_string(pVal, "/", (int)sizeof(char)); -#endif -} -/* - * PATH_SEPARATOR. - * Expand the path separator character. - */ -static void JX9_PATHSEP_Const(jx9_value *pVal, void *pUnused) -{ - SXUNUSED(pUnused); -#ifdef __WINNT__ - jx9_value_string(pVal, ";", (int)sizeof(char)); -#else - jx9_value_string(pVal, ":", (int)sizeof(char)); -#endif -} -#ifndef __WINNT__ -#include -#endif -/* - * __TIME__ - * Expand the current time (GMT). - */ -static void JX9_TIME_Const(jx9_value *pVal, void *pUnused) -{ - Sytm sTm; -#ifdef __WINNT__ - SYSTEMTIME sOS; - GetSystemTime(&sOS); - SYSTEMTIME_TO_SYTM(&sOS, &sTm); -#else - struct tm *pTm; - time_t t; - time(&t); - pTm = gmtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); -#endif - SXUNUSED(pUnused); /* cc warning */ - /* Expand */ - jx9_value_string_format(pVal, "%02d:%02d:%02d", sTm.tm_hour, sTm.tm_min, sTm.tm_sec); -} -/* - * __DATE__ - * Expand the current date in the ISO-8601 format. - */ -static void JX9_DATE_Const(jx9_value *pVal, void *pUnused) -{ - Sytm sTm; -#ifdef __WINNT__ - SYSTEMTIME sOS; - GetSystemTime(&sOS); - SYSTEMTIME_TO_SYTM(&sOS, &sTm); -#else - struct tm *pTm; - time_t t; - time(&t); - pTm = gmtime(&t); - STRUCT_TM_TO_SYTM(pTm, &sTm); -#endif - SXUNUSED(pUnused); /* cc warning */ - /* Expand */ - jx9_value_string_format(pVal, "%04d-%02d-%02d", sTm.tm_year, sTm.tm_mon+1, sTm.tm_mday); -} -/* - * __FILE__ - * Path of the processed script. - */ -static void JX9_FILE_Const(jx9_value *pVal, void *pUserData) -{ - jx9_vm *pVm = (jx9_vm *)pUserData; - SyString *pFile; - /* Peek the top entry */ - pFile = (SyString *)SySetPeek(&pVm->aFiles); - if( pFile == 0 ){ - /* Expand the magic word: ":MEMORY:" */ - jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1); - }else{ - jx9_value_string(pVal, pFile->zString, pFile->nByte); - } -} -/* - * __DIR__ - * Directory holding the processed script. - */ -static void JX9_DIR_Const(jx9_value *pVal, void *pUserData) -{ - jx9_vm *pVm = (jx9_vm *)pUserData; - SyString *pFile; - /* Peek the top entry */ - pFile = (SyString *)SySetPeek(&pVm->aFiles); - if( pFile == 0 ){ - /* Expand the magic word: ":MEMORY:" */ - jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1); - }else{ - if( pFile->nByte > 0 ){ - const char *zDir; - int nLen; - zDir = jx9ExtractDirName(pFile->zString, (int)pFile->nByte, &nLen); - jx9_value_string(pVal, zDir, nLen); - }else{ - /* Expand '.' as the current directory*/ - jx9_value_string(pVal, ".", (int)sizeof(char)); - } - } -} -/* - * E_ERROR - * Expands 1 - */ -static void JX9_E_ERROR_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 1); - SXUNUSED(pUserData); -} -/* - * E_WARNING - * Expands 2 - */ -static void JX9_E_WARNING_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 2); - SXUNUSED(pUserData); -} -/* - * E_PARSE - * Expands 4 - */ -static void JX9_E_PARSE_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 4); - SXUNUSED(pUserData); -} -/* - * E_NOTICE - * Expands 8 - */ -static void JX9_E_NOTICE_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 8); - SXUNUSED(pUserData); -} -/* - * CASE_LOWER - * Expands 0. - */ -static void JX9_CASE_LOWER_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 0); - SXUNUSED(pUserData); -} -/* - * CASE_UPPER - * Expands 1. - */ -static void JX9_CASE_UPPER_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 1); - SXUNUSED(pUserData); -} -/* - * STR_PAD_LEFT - * Expands 0. - */ -static void JX9_STR_PAD_LEFT_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 0); - SXUNUSED(pUserData); -} -/* - * STR_PAD_RIGHT - * Expands 1. - */ -static void JX9_STR_PAD_RIGHT_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 1); - SXUNUSED(pUserData); -} -/* - * STR_PAD_BOTH - * Expands 2. - */ -static void JX9_STR_PAD_BOTH_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 2); - SXUNUSED(pUserData); -} -/* - * COUNT_NORMAL - * Expands 0 - */ -static void JX9_COUNT_NORMAL_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 0); - SXUNUSED(pUserData); -} -/* - * COUNT_RECURSIVE - * Expands 1. - */ -static void JX9_COUNT_RECURSIVE_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 1); - SXUNUSED(pUserData); -} -/* - * SORT_ASC - * Expands 1. - */ -static void JX9_SORT_ASC_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 1); - SXUNUSED(pUserData); -} -/* - * SORT_DESC - * Expands 2. - */ -static void JX9_SORT_DESC_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 2); - SXUNUSED(pUserData); -} -/* - * SORT_REGULAR - * Expands 3. - */ -static void JX9_SORT_REG_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 3); - SXUNUSED(pUserData); -} -/* - * SORT_NUMERIC - * Expands 4. - */ -static void JX9_SORT_NUMERIC_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 4); - SXUNUSED(pUserData); -} -/* - * SORT_STRING - * Expands 5. - */ -static void JX9_SORT_STRING_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 5); - SXUNUSED(pUserData); -} -/* - * JX9_ROUND_HALF_UP - * Expands 1. - */ -static void JX9_JX9_ROUND_HALF_UP_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 1); - SXUNUSED(pUserData); -} -/* - * SJX9_ROUND_HALF_DOWN - * Expands 2. - */ -static void JX9_JX9_ROUND_HALF_DOWN_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 2); - SXUNUSED(pUserData); -} -/* - * JX9_ROUND_HALF_EVEN - * Expands 3. - */ -static void JX9_JX9_ROUND_HALF_EVEN_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 3); - SXUNUSED(pUserData); -} -/* - * JX9_ROUND_HALF_ODD - * Expands 4. - */ -static void JX9_JX9_ROUND_HALF_ODD_Const(jx9_value *pVal, void *pUserData) -{ - jx9_value_int(pVal, 4); - SXUNUSED(pUserData); -} -#ifdef JX9_ENABLE_MATH_FUNC -/* - * PI - * Expand the value of pi. - */ -static void JX9_M_PI_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, JX9_PI); -} -/* - * M_E - * Expand 2.7182818284590452354 - */ -static void JX9_M_E_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 2.7182818284590452354); -} -/* - * M_LOG2E - * Expand 2.7182818284590452354 - */ -static void JX9_M_LOG2E_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 1.4426950408889634074); -} -/* - * M_LOG10E - * Expand 0.4342944819032518276 - */ -static void JX9_M_LOG10E_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 0.4342944819032518276); -} -/* - * M_LN2 - * Expand 0.69314718055994530942 - */ -static void JX9_M_LN2_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 0.69314718055994530942); -} -/* - * M_LN10 - * Expand 2.30258509299404568402 - */ -static void JX9_M_LN10_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 2.30258509299404568402); -} -/* - * M_PI_2 - * Expand 1.57079632679489661923 - */ -static void JX9_M_PI_2_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 1.57079632679489661923); -} -/* - * M_PI_4 - * Expand 0.78539816339744830962 - */ -static void JX9_M_PI_4_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 0.78539816339744830962); -} -/* - * M_1_PI - * Expand 0.31830988618379067154 - */ -static void JX9_M_1_PI_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 0.31830988618379067154); -} -/* - * M_2_PI - * Expand 0.63661977236758134308 - */ -static void JX9_M_2_PI_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 0.63661977236758134308); -} -/* - * M_SQRTPI - * Expand 1.77245385090551602729 - */ -static void JX9_M_SQRTPI_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 1.77245385090551602729); -} -/* - * M_2_SQRTPI - * Expand 1.12837916709551257390 - */ -static void JX9_M_2_SQRTPI_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 1.12837916709551257390); -} -/* - * M_SQRT2 - * Expand 1.41421356237309504880 - */ -static void JX9_M_SQRT2_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 1.41421356237309504880); -} -/* - * M_SQRT3 - * Expand 1.73205080756887729352 - */ -static void JX9_M_SQRT3_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 1.73205080756887729352); -} -/* - * M_SQRT1_2 - * Expand 0.70710678118654752440 - */ -static void JX9_M_SQRT1_2_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 0.70710678118654752440); -} -/* - * M_LNPI - * Expand 1.14472988584940017414 - */ -static void JX9_M_LNPI_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 1.14472988584940017414); -} -/* - * M_EULER - * Expand 0.57721566490153286061 - */ -static void JX9_M_EULER_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_double(pVal, 0.57721566490153286061); -} -#endif /* JX9_DISABLE_BUILTIN_MATH */ -/* - * DATE_ATOM - * Expand Atom (example: 2005-08-15T15:52:01+00:00) - */ -static void JX9_DATE_ATOM_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/); -} -/* - * DATE_COOKIE - * HTTP Cookies (example: Monday, 15-Aug-05 15:52:01 UTC) - */ -static void JX9_DATE_COOKIE_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/); -} -/* - * DATE_ISO8601 - * ISO-8601 (example: 2005-08-15T15:52:01+0000) - */ -static void JX9_DATE_ISO8601_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_string(pVal, "Y-m-d\\TH:i:sO", -1/*Compute length automatically*/); -} -/* - * DATE_RFC822 - * RFC 822 (example: Mon, 15 Aug 05 15:52:01 +0000) - */ -static void JX9_DATE_RFC822_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/); -} -/* - * DATE_RFC850 - * RFC 850 (example: Monday, 15-Aug-05 15:52:01 UTC) - */ -static void JX9_DATE_RFC850_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/); -} -/* - * DATE_RFC1036 - * RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000) - */ -static void JX9_DATE_RFC1036_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/); -} -/* - * DATE_RFC1123 - * RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000) - */ -static void JX9_DATE_RFC1123_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/); -} -/* - * DATE_RFC2822 - * RFC 2822 (Mon, 15 Aug 2005 15:52:01 +0000) - */ -static void JX9_DATE_RFC2822_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/); -} -/* - * DATE_RSS - * RSS (Mon, 15 Aug 2005 15:52:01 +0000) - */ -static void JX9_DATE_RSS_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/); -} -/* - * DATE_W3C - * World Wide Web Consortium (example: 2005-08-15T15:52:01+00:00) - */ -static void JX9_DATE_W3C_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/); -} -/* - * ENT_COMPAT - * Expand 0x01 (Must be a power of two) - */ -static void JX9_ENT_COMPAT_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x01); -} -/* - * ENT_QUOTES - * Expand 0x02 (Must be a power of two) - */ -static void JX9_ENT_QUOTES_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x02); -} -/* - * ENT_NOQUOTES - * Expand 0x04 (Must be a power of two) - */ -static void JX9_ENT_NOQUOTES_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x04); -} -/* - * ENT_IGNORE - * Expand 0x08 (Must be a power of two) - */ -static void JX9_ENT_IGNORE_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x08); -} -/* - * ENT_SUBSTITUTE - * Expand 0x10 (Must be a power of two) - */ -static void JX9_ENT_SUBSTITUTE_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x10); -} -/* - * ENT_DISALLOWED - * Expand 0x20 (Must be a power of two) - */ -static void JX9_ENT_DISALLOWED_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x20); -} -/* - * ENT_HTML401 - * Expand 0x40 (Must be a power of two) - */ -static void JX9_ENT_HTML401_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x40); -} -/* - * ENT_XML1 - * Expand 0x80 (Must be a power of two) - */ -static void JX9_ENT_XML1_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x80); -} -/* - * ENT_XHTML - * Expand 0x100 (Must be a power of two) - */ -static void JX9_ENT_XHTML_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x100); -} -/* - * ENT_HTML5 - * Expand 0x200 (Must be a power of two) - */ -static void JX9_ENT_HTML5_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x200); -} -/* - * ISO-8859-1 - * ISO_8859_1 - * Expand 1 - */ -static void JX9_ISO88591_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 1); -} -/* - * UTF-8 - * UTF8 - * Expand 2 - */ -static void JX9_UTF8_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 1); -} -/* - * HTML_ENTITIES - * Expand 1 - */ -static void JX9_HTML_ENTITIES_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 1); -} -/* - * HTML_SPECIALCHARS - * Expand 2 - */ -static void JX9_HTML_SPECIALCHARS_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 2); -} -/* - * JX9_URL_SCHEME. - * Expand 1 - */ -static void JX9_JX9_URL_SCHEME_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 1); -} -/* - * JX9_URL_HOST. - * Expand 2 - */ -static void JX9_JX9_URL_HOST_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 2); -} -/* - * JX9_URL_PORT. - * Expand 3 - */ -static void JX9_JX9_URL_PORT_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 3); -} -/* - * JX9_URL_USER. - * Expand 4 - */ -static void JX9_JX9_URL_USER_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 4); -} -/* - * JX9_URL_PASS. - * Expand 5 - */ -static void JX9_JX9_URL_PASS_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 5); -} -/* - * JX9_URL_PATH. - * Expand 6 - */ -static void JX9_JX9_URL_PATH_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 6); -} -/* - * JX9_URL_QUERY. - * Expand 7 - */ -static void JX9_JX9_URL_QUERY_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 7); -} -/* - * JX9_URL_FRAGMENT. - * Expand 8 - */ -static void JX9_JX9_URL_FRAGMENT_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 8); -} -/* - * JX9_QUERY_RFC1738 - * Expand 1 - */ -static void JX9_JX9_QUERY_RFC1738_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 1); -} -/* - * JX9_QUERY_RFC3986 - * Expand 1 - */ -static void JX9_JX9_QUERY_RFC3986_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 2); -} -/* - * FNM_NOESCAPE - * Expand 0x01 (Must be a power of two) - */ -static void JX9_FNM_NOESCAPE_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x01); -} -/* - * FNM_PATHNAME - * Expand 0x02 (Must be a power of two) - */ -static void JX9_FNM_PATHNAME_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x02); -} -/* - * FNM_PERIOD - * Expand 0x04 (Must be a power of two) - */ -static void JX9_FNM_PERIOD_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x04); -} -/* - * FNM_CASEFOLD - * Expand 0x08 (Must be a power of two) - */ -static void JX9_FNM_CASEFOLD_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x08); -} -/* - * PATHINFO_DIRNAME - * Expand 1. - */ -static void JX9_PATHINFO_DIRNAME_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 1); -} -/* - * PATHINFO_BASENAME - * Expand 2. - */ -static void JX9_PATHINFO_BASENAME_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 2); -} -/* - * PATHINFO_EXTENSION - * Expand 3. - */ -static void JX9_PATHINFO_EXTENSION_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 3); -} -/* - * PATHINFO_FILENAME - * Expand 4. - */ -static void JX9_PATHINFO_FILENAME_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 4); -} -/* - * ASSERT_ACTIVE. - * Expand the value of JX9_ASSERT_ACTIVE defined in jx9Int.h - */ -static void JX9_ASSERT_ACTIVE_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, JX9_ASSERT_DISABLE); -} -/* - * ASSERT_WARNING. - * Expand the value of JX9_ASSERT_WARNING defined in jx9Int.h - */ -static void JX9_ASSERT_WARNING_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, JX9_ASSERT_WARNING); -} -/* - * ASSERT_BAIL. - * Expand the value of JX9_ASSERT_BAIL defined in jx9Int.h - */ -static void JX9_ASSERT_BAIL_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, JX9_ASSERT_BAIL); -} -/* - * ASSERT_QUIET_EVAL. - * Expand the value of JX9_ASSERT_QUIET_EVAL defined in jx9Int.h - */ -static void JX9_ASSERT_QUIET_EVAL_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, JX9_ASSERT_QUIET_EVAL); -} -/* - * ASSERT_CALLBACK. - * Expand the value of JX9_ASSERT_CALLBACK defined in jx9Int.h - */ -static void JX9_ASSERT_CALLBACK_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, JX9_ASSERT_CALLBACK); -} -/* - * SEEK_SET. - * Expand 0 - */ -static void JX9_SEEK_SET_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0); -} -/* - * SEEK_CUR. - * Expand 1 - */ -static void JX9_SEEK_CUR_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 1); -} -/* - * SEEK_END. - * Expand 2 - */ -static void JX9_SEEK_END_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 2); -} -/* - * LOCK_SH. - * Expand 2 - */ -static void JX9_LOCK_SH_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 1); -} -/* - * LOCK_NB. - * Expand 5 - */ -static void JX9_LOCK_NB_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 5); -} -/* - * LOCK_EX. - * Expand 0x01 (MUST BE A POWER OF TWO) - */ -static void JX9_LOCK_EX_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x01); -} -/* - * LOCK_UN. - * Expand 0 - */ -static void JX9_LOCK_UN_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0); -} -/* - * FILE_USE_INC_PATH - * Expand 0x01 (Must be a power of two) - */ -static void JX9_FILE_USE_INCLUDE_PATH_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x1); -} -/* - * FILE_IGN_NL - * Expand 0x02 (Must be a power of two) - */ -static void JX9_FILE_IGNORE_NEW_LINES_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x2); -} -/* - * FILE_SKIP_EL - * Expand 0x04 (Must be a power of two) - */ -static void JX9_FILE_SKIP_EMPTY_LINES_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x4); -} -/* - * FILE_APPEND - * Expand 0x08 (Must be a power of two) - */ -static void JX9_FILE_APPEND_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x08); -} -/* - * SCANDIR_SORT_ASCENDING - * Expand 0 - */ -static void JX9_SCANDIR_SORT_ASCENDING_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0); -} -/* - * SCANDIR_SORT_DESCENDING - * Expand 1 - */ -static void JX9_SCANDIR_SORT_DESCENDING_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 1); -} -/* - * SCANDIR_SORT_NONE - * Expand 2 - */ -static void JX9_SCANDIR_SORT_NONE_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 2); -} -/* - * GLOB_MARK - * Expand 0x01 (must be a power of two) - */ -static void JX9_GLOB_MARK_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x01); -} -/* - * GLOB_NOSORT - * Expand 0x02 (must be a power of two) - */ -static void JX9_GLOB_NOSORT_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x02); -} -/* - * GLOB_NOCHECK - * Expand 0x04 (must be a power of two) - */ -static void JX9_GLOB_NOCHECK_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x04); -} -/* - * GLOB_NOESCAPE - * Expand 0x08 (must be a power of two) - */ -static void JX9_GLOB_NOESCAPE_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x08); -} -/* - * GLOB_BRACE - * Expand 0x10 (must be a power of two) - */ -static void JX9_GLOB_BRACE_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x10); -} -/* - * GLOB_ONLYDIR - * Expand 0x20 (must be a power of two) - */ -static void JX9_GLOB_ONLYDIR_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x20); -} -/* - * GLOB_ERR - * Expand 0x40 (must be a power of two) - */ -static void JX9_GLOB_ERR_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x40); -} -/* - * STDIN - * Expand the STDIN handle as a resource. - */ -static void JX9_STDIN_Const(jx9_value *pVal, void *pUserData) -{ - jx9_vm *pVm = (jx9_vm *)pUserData; - void *pResource; - pResource = jx9ExportStdin(pVm); - jx9_value_resource(pVal, pResource); -} -/* - * STDOUT - * Expand the STDOUT handle as a resource. - */ -static void JX9_STDOUT_Const(jx9_value *pVal, void *pUserData) -{ - jx9_vm *pVm = (jx9_vm *)pUserData; - void *pResource; - pResource = jx9ExportStdout(pVm); - jx9_value_resource(pVal, pResource); -} -/* - * STDERR - * Expand the STDERR handle as a resource. - */ -static void JX9_STDERR_Const(jx9_value *pVal, void *pUserData) -{ - jx9_vm *pVm = (jx9_vm *)pUserData; - void *pResource; - pResource = jx9ExportStderr(pVm); - jx9_value_resource(pVal, pResource); -} -/* - * INI_SCANNER_NORMAL - * Expand 1 - */ -static void JX9_INI_SCANNER_NORMAL_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 1); -} -/* - * INI_SCANNER_RAW - * Expand 2 - */ -static void JX9_INI_SCANNER_RAW_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 2); -} -/* - * EXTR_OVERWRITE - * Expand 0x01 (Must be a power of two) - */ -static void JX9_EXTR_OVERWRITE_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x1); -} -/* - * EXTR_SKIP - * Expand 0x02 (Must be a power of two) - */ -static void JX9_EXTR_SKIP_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x2); -} -/* - * EXTR_PREFIX_SAME - * Expand 0x04 (Must be a power of two) - */ -static void JX9_EXTR_PREFIX_SAME_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x4); -} -/* - * EXTR_PREFIX_ALL - * Expand 0x08 (Must be a power of two) - */ -static void JX9_EXTR_PREFIX_ALL_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x8); -} -/* - * EXTR_PREFIX_INVALID - * Expand 0x10 (Must be a power of two) - */ -static void JX9_EXTR_PREFIX_INVALID_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x10); -} -/* - * EXTR_IF_EXISTS - * Expand 0x20 (Must be a power of two) - */ -static void JX9_EXTR_IF_EXISTS_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x20); -} -/* - * EXTR_PREFIX_IF_EXISTS - * Expand 0x40 (Must be a power of two) - */ -static void JX9_EXTR_PREFIX_IF_EXISTS_Const(jx9_value *pVal, void *pUserData) -{ - SXUNUSED(pUserData); /* cc warning */ - jx9_value_int(pVal, 0x40); -} -/* - * Table of built-in constants. - */ -static const jx9_builtin_constant aBuiltIn[] = { - {"JX9_VERSION", JX9_VER_Const }, - {"JX9_ENGINE", JX9_VER_Const }, - {"__JX9__", JX9_VER_Const }, - {"JX9_OS", JX9_OS_Const }, - {"__OS__", JX9_OS_Const }, - {"JX9_EOL", JX9_EOL_Const }, - {"JX9_INT_MAX", JX9_INTMAX_Const }, - {"MAXINT", JX9_INTMAX_Const }, - {"JX9_INT_SIZE", JX9_INTSIZE_Const }, - {"PATH_SEPARATOR", JX9_PATHSEP_Const }, - {"DIRECTORY_SEPARATOR", JX9_DIRSEP_Const }, - {"DIR_SEP", JX9_DIRSEP_Const }, - {"__TIME__", JX9_TIME_Const }, - {"__DATE__", JX9_DATE_Const }, - {"__FILE__", JX9_FILE_Const }, - {"__DIR__", JX9_DIR_Const }, - {"E_ERROR", JX9_E_ERROR_Const }, - {"E_WARNING", JX9_E_WARNING_Const}, - {"E_PARSE", JX9_E_PARSE_Const }, - {"E_NOTICE", JX9_E_NOTICE_Const }, - {"CASE_LOWER", JX9_CASE_LOWER_Const }, - {"CASE_UPPER", JX9_CASE_UPPER_Const }, - {"STR_PAD_LEFT", JX9_STR_PAD_LEFT_Const }, - {"STR_PAD_RIGHT", JX9_STR_PAD_RIGHT_Const}, - {"STR_PAD_BOTH", JX9_STR_PAD_BOTH_Const }, - {"COUNT_NORMAL", JX9_COUNT_NORMAL_Const }, - {"COUNT_RECURSIVE", JX9_COUNT_RECURSIVE_Const }, - {"SORT_ASC", JX9_SORT_ASC_Const }, - {"SORT_DESC", JX9_SORT_DESC_Const }, - {"SORT_REGULAR", JX9_SORT_REG_Const }, - {"SORT_NUMERIC", JX9_SORT_NUMERIC_Const }, - {"SORT_STRING", JX9_SORT_STRING_Const }, - {"JX9_ROUND_HALF_DOWN", JX9_JX9_ROUND_HALF_DOWN_Const }, - {"JX9_ROUND_HALF_EVEN", JX9_JX9_ROUND_HALF_EVEN_Const }, - {"JX9_ROUND_HALF_UP", JX9_JX9_ROUND_HALF_UP_Const }, - {"JX9_ROUND_HALF_ODD", JX9_JX9_ROUND_HALF_ODD_Const }, -#ifdef JX9_ENABLE_MATH_FUNC - {"PI", JX9_M_PI_Const }, - {"M_E", JX9_M_E_Const }, - {"M_LOG2E", JX9_M_LOG2E_Const }, - {"M_LOG10E", JX9_M_LOG10E_Const }, - {"M_LN2", JX9_M_LN2_Const }, - {"M_LN10", JX9_M_LN10_Const }, - {"M_PI_2", JX9_M_PI_2_Const }, - {"M_PI_4", JX9_M_PI_4_Const }, - {"M_1_PI", JX9_M_1_PI_Const }, - {"M_2_PI", JX9_M_2_PI_Const }, - {"M_SQRTPI", JX9_M_SQRTPI_Const }, - {"M_2_SQRTPI", JX9_M_2_SQRTPI_Const }, - {"M_SQRT2", JX9_M_SQRT2_Const }, - {"M_SQRT3", JX9_M_SQRT3_Const }, - {"M_SQRT1_2", JX9_M_SQRT1_2_Const }, - {"M_LNPI", JX9_M_LNPI_Const }, - {"M_EULER", JX9_M_EULER_Const }, -#endif /* JX9_ENABLE_MATH_FUNC */ - {"DATE_ATOM", JX9_DATE_ATOM_Const }, - {"DATE_COOKIE", JX9_DATE_COOKIE_Const }, - {"DATE_ISO8601", JX9_DATE_ISO8601_Const }, - {"DATE_RFC822", JX9_DATE_RFC822_Const }, - {"DATE_RFC850", JX9_DATE_RFC850_Const }, - {"DATE_RFC1036", JX9_DATE_RFC1036_Const }, - {"DATE_RFC1123", JX9_DATE_RFC1123_Const }, - {"DATE_RFC2822", JX9_DATE_RFC2822_Const }, - {"DATE_RFC3339", JX9_DATE_ATOM_Const }, - {"DATE_RSS", JX9_DATE_RSS_Const }, - {"DATE_W3C", JX9_DATE_W3C_Const }, - {"ENT_COMPAT", JX9_ENT_COMPAT_Const }, - {"ENT_QUOTES", JX9_ENT_QUOTES_Const }, - {"ENT_NOQUOTES", JX9_ENT_NOQUOTES_Const }, - {"ENT_IGNORE", JX9_ENT_IGNORE_Const }, - {"ENT_SUBSTITUTE", JX9_ENT_SUBSTITUTE_Const}, - {"ENT_DISALLOWED", JX9_ENT_DISALLOWED_Const}, - {"ENT_HTML401", JX9_ENT_HTML401_Const }, - {"ENT_XML1", JX9_ENT_XML1_Const }, - {"ENT_XHTML", JX9_ENT_XHTML_Const }, - {"ENT_HTML5", JX9_ENT_HTML5_Const }, - {"ISO-8859-1", JX9_ISO88591_Const }, - {"ISO_8859_1", JX9_ISO88591_Const }, - {"UTF-8", JX9_UTF8_Const }, - {"UTF8", JX9_UTF8_Const }, - {"HTML_ENTITIES", JX9_HTML_ENTITIES_Const}, - {"HTML_SPECIALCHARS", JX9_HTML_SPECIALCHARS_Const }, - {"JX9_URL_SCHEME", JX9_JX9_URL_SCHEME_Const}, - {"JX9_URL_HOST", JX9_JX9_URL_HOST_Const}, - {"JX9_URL_PORT", JX9_JX9_URL_PORT_Const}, - {"JX9_URL_USER", JX9_JX9_URL_USER_Const}, - {"JX9_URL_PASS", JX9_JX9_URL_PASS_Const}, - {"JX9_URL_PATH", JX9_JX9_URL_PATH_Const}, - {"JX9_URL_QUERY", JX9_JX9_URL_QUERY_Const}, - {"JX9_URL_FRAGMENT", JX9_JX9_URL_FRAGMENT_Const}, - {"JX9_QUERY_RFC1738", JX9_JX9_QUERY_RFC1738_Const}, - {"JX9_QUERY_RFC3986", JX9_JX9_QUERY_RFC3986_Const}, - {"FNM_NOESCAPE", JX9_FNM_NOESCAPE_Const }, - {"FNM_PATHNAME", JX9_FNM_PATHNAME_Const }, - {"FNM_PERIOD", JX9_FNM_PERIOD_Const }, - {"FNM_CASEFOLD", JX9_FNM_CASEFOLD_Const }, - {"PATHINFO_DIRNAME", JX9_PATHINFO_DIRNAME_Const }, - {"PATHINFO_BASENAME", JX9_PATHINFO_BASENAME_Const }, - {"PATHINFO_EXTENSION", JX9_PATHINFO_EXTENSION_Const}, - {"PATHINFO_FILENAME", JX9_PATHINFO_FILENAME_Const }, - {"ASSERT_ACTIVE", JX9_ASSERT_ACTIVE_Const }, - {"ASSERT_WARNING", JX9_ASSERT_WARNING_Const }, - {"ASSERT_BAIL", JX9_ASSERT_BAIL_Const }, - {"ASSERT_QUIET_EVAL", JX9_ASSERT_QUIET_EVAL_Const }, - {"ASSERT_CALLBACK", JX9_ASSERT_CALLBACK_Const }, - {"SEEK_SET", JX9_SEEK_SET_Const }, - {"SEEK_CUR", JX9_SEEK_CUR_Const }, - {"SEEK_END", JX9_SEEK_END_Const }, - {"LOCK_EX", JX9_LOCK_EX_Const }, - {"LOCK_SH", JX9_LOCK_SH_Const }, - {"LOCK_NB", JX9_LOCK_NB_Const }, - {"LOCK_UN", JX9_LOCK_UN_Const }, - {"FILE_USE_INC_PATH", JX9_FILE_USE_INCLUDE_PATH_Const}, - {"FILE_IGN_NL", JX9_FILE_IGNORE_NEW_LINES_Const}, - {"FILE_SKIP_EL", JX9_FILE_SKIP_EMPTY_LINES_Const}, - {"FILE_APPEND", JX9_FILE_APPEND_Const }, - {"SCANDIR_SORT_ASC", JX9_SCANDIR_SORT_ASCENDING_Const }, - {"SCANDIR_SORT_DESC", JX9_SCANDIR_SORT_DESCENDING_Const }, - {"SCANDIR_SORT_NONE", JX9_SCANDIR_SORT_NONE_Const }, - {"GLOB_MARK", JX9_GLOB_MARK_Const }, - {"GLOB_NOSORT", JX9_GLOB_NOSORT_Const }, - {"GLOB_NOCHECK", JX9_GLOB_NOCHECK_Const }, - {"GLOB_NOESCAPE", JX9_GLOB_NOESCAPE_Const}, - {"GLOB_BRACE", JX9_GLOB_BRACE_Const }, - {"GLOB_ONLYDIR", JX9_GLOB_ONLYDIR_Const }, - {"GLOB_ERR", JX9_GLOB_ERR_Const }, - {"STDIN", JX9_STDIN_Const }, - {"stdin", JX9_STDIN_Const }, - {"STDOUT", JX9_STDOUT_Const }, - {"stdout", JX9_STDOUT_Const }, - {"STDERR", JX9_STDERR_Const }, - {"stderr", JX9_STDERR_Const }, - {"INI_SCANNER_NORMAL", JX9_INI_SCANNER_NORMAL_Const }, - {"INI_SCANNER_RAW", JX9_INI_SCANNER_RAW_Const }, - {"EXTR_OVERWRITE", JX9_EXTR_OVERWRITE_Const }, - {"EXTR_SKIP", JX9_EXTR_SKIP_Const }, - {"EXTR_PREFIX_SAME", JX9_EXTR_PREFIX_SAME_Const }, - {"EXTR_PREFIX_ALL", JX9_EXTR_PREFIX_ALL_Const }, - {"EXTR_PREFIX_INVALID", JX9_EXTR_PREFIX_INVALID_Const }, - {"EXTR_IF_EXISTS", JX9_EXTR_IF_EXISTS_Const }, - {"EXTR_PREFIX_IF_EXISTS", JX9_EXTR_PREFIX_IF_EXISTS_Const} -}; -/* - * Register the built-in constants defined above. - */ -JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm) -{ - sxu32 n; - /* - * Note that all built-in constants have access to the jx9 virtual machine - * that trigger the constant invocation as their private data. - */ - for( n = 0 ; n < SX_ARRAYSIZE(aBuiltIn) ; ++n ){ - jx9_create_constant(&(*pVm), aBuiltIn[n].zName, aBuiltIn[n].xExpand, &(*pVm)); - } -} -/* - * ---------------------------------------------------------- - * File: jx9_hashmap.c - * MD5: 4e93d15cd37e6093e25d8ede3064e210 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: hashmap.c v2.6 Win7 2012-12-11 00:50 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* This file implement generic hashmaps used to represent JSON arrays and objects */ -/* Allowed node types */ -#define HASHMAP_INT_NODE 1 /* Node with an int [i.e: 64-bit integer] key */ -#define HASHMAP_BLOB_NODE 2 /* Node with a string/BLOB key */ -/* - * Default hash function for int [i.e; 64-bit integer] keys. - */ -static sxu32 IntHash(sxi64 iKey) -{ - return (sxu32)(iKey ^ (iKey << 8) ^ (iKey >> 8)); -} -/* - * Default hash function for string/BLOB keys. - */ -static sxu32 BinHash(const void *pSrc, sxu32 nLen) -{ - register unsigned char *zIn = (unsigned char *)pSrc; - unsigned char *zEnd; - sxu32 nH = 5381; - zEnd = &zIn[nLen]; - for(;;){ - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - } - return nH; -} -/* - * Return the total number of entries in a given hashmap. - * If bRecurisve is set to TRUE then recurse on hashmap entries. - * If the nesting limit is reached, this function abort immediately. - */ -static sxi64 HashmapCount(jx9_hashmap *pMap, int bRecursive, int iRecCount) -{ - sxi64 iCount = 0; - if( !bRecursive ){ - iCount = pMap->nEntry; - }else{ - /* Recursive hashmap walk */ - jx9_hashmap_node *pEntry = pMap->pLast; - jx9_value *pElem; - sxu32 n = 0; - for(;;){ - if( n >= pMap->nEntry ){ - break; - } - /* Point to the element value */ - pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pEntry->nValIdx); - if( pElem ){ - if( pElem->iFlags & MEMOBJ_HASHMAP ){ - if( iRecCount > 31 ){ - /* Nesting limit reached */ - return iCount; - } - /* Recurse */ - iRecCount++; - iCount += HashmapCount((jx9_hashmap *)pElem->x.pOther, TRUE, iRecCount); - iRecCount--; - } - } - /* Point to the next entry */ - pEntry = pEntry->pNext; - ++n; - } - /* Update count */ - iCount += pMap->nEntry; - } - return iCount; -} -/* - * Allocate a new hashmap node with a 64-bit integer key. - * If something goes wrong [i.e: out of memory], this function return NULL. - * Otherwise a fresh [jx9_hashmap_node] instance is returned. - */ -static jx9_hashmap_node * HashmapNewIntNode(jx9_hashmap *pMap, sxi64 iKey, sxu32 nHash, sxu32 nValIdx) -{ - jx9_hashmap_node *pNode; - /* Allocate a new node */ - pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node)); - if( pNode == 0 ){ - return 0; - } - /* Zero the stucture */ - SyZero(pNode, sizeof(jx9_hashmap_node)); - /* Fill in the structure */ - pNode->pMap = &(*pMap); - pNode->iType = HASHMAP_INT_NODE; - pNode->nHash = nHash; - pNode->xKey.iKey = iKey; - pNode->nValIdx = nValIdx; - return pNode; -} -/* - * Allocate a new hashmap node with a BLOB key. - * If something goes wrong [i.e: out of memory], this function return NULL. - * Otherwise a fresh [jx9_hashmap_node] instance is returned. - */ -static jx9_hashmap_node * HashmapNewBlobNode(jx9_hashmap *pMap, const void *pKey, sxu32 nKeyLen, sxu32 nHash, sxu32 nValIdx) -{ - jx9_hashmap_node *pNode; - /* Allocate a new node */ - pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node)); - if( pNode == 0 ){ - return 0; - } - /* Zero the stucture */ - SyZero(pNode, sizeof(jx9_hashmap_node)); - /* Fill in the structure */ - pNode->pMap = &(*pMap); - pNode->iType = HASHMAP_BLOB_NODE; - pNode->nHash = nHash; - SyBlobInit(&pNode->xKey.sKey, &pMap->pVm->sAllocator); - SyBlobAppend(&pNode->xKey.sKey, pKey, nKeyLen); - pNode->nValIdx = nValIdx; - return pNode; -} -/* - * link a hashmap node to the given bucket index (last argument to this function). - */ -static void HashmapNodeLink(jx9_hashmap *pMap, jx9_hashmap_node *pNode, sxu32 nBucketIdx) -{ - /* Link */ - if( pMap->apBucket[nBucketIdx] != 0 ){ - pNode->pNextCollide = pMap->apBucket[nBucketIdx]; - pMap->apBucket[nBucketIdx]->pPrevCollide = pNode; - } - pMap->apBucket[nBucketIdx] = pNode; - /* Link to the map list */ - if( pMap->pFirst == 0 ){ - pMap->pFirst = pMap->pLast = pNode; - /* Point to the first inserted node */ - pMap->pCur = pNode; - }else{ - MACRO_LD_PUSH(pMap->pLast, pNode); - } - ++pMap->nEntry; -} -/* - * Unlink a node from the hashmap. - * If the node count reaches zero then release the whole hash-bucket. - */ -static void jx9HashmapUnlinkNode(jx9_hashmap_node *pNode) -{ - jx9_hashmap *pMap = pNode->pMap; - jx9_vm *pVm = pMap->pVm; - /* Unlink from the corresponding bucket */ - if( pNode->pPrevCollide == 0 ){ - pMap->apBucket[pNode->nHash & (pMap->nSize - 1)] = pNode->pNextCollide; - }else{ - pNode->pPrevCollide->pNextCollide = pNode->pNextCollide; - } - if( pNode->pNextCollide ){ - pNode->pNextCollide->pPrevCollide = pNode->pPrevCollide; - } - if( pMap->pFirst == pNode ){ - pMap->pFirst = pNode->pPrev; - } - if( pMap->pCur == pNode ){ - /* Advance the node cursor */ - pMap->pCur = pMap->pCur->pPrev; /* Reverse link */ - } - /* Unlink from the map list */ - MACRO_LD_REMOVE(pMap->pLast, pNode); - /* Restore to the free list */ - jx9VmUnsetMemObj(pVm, pNode->nValIdx); - if( pNode->iType == HASHMAP_BLOB_NODE ){ - SyBlobRelease(&pNode->xKey.sKey); - } - SyMemBackendPoolFree(&pVm->sAllocator, pNode); - pMap->nEntry--; - if( pMap->nEntry < 1 ){ - /* Free the hash-bucket */ - SyMemBackendFree(&pVm->sAllocator, pMap->apBucket); - pMap->apBucket = 0; - pMap->nSize = 0; - pMap->pFirst = pMap->pLast = pMap->pCur = 0; - } -} -#define HASHMAP_FILL_FACTOR 3 -/* - * Grow the hash-table and rehash all entries. - */ -static sxi32 HashmapGrowBucket(jx9_hashmap *pMap) -{ - if( pMap->nEntry >= pMap->nSize * HASHMAP_FILL_FACTOR ){ - jx9_hashmap_node **apOld = pMap->apBucket; - jx9_hashmap_node *pEntry, **apNew; - sxu32 nNew = pMap->nSize << 1; - sxu32 nBucket; - sxu32 n; - if( nNew < 1 ){ - nNew = 16; - } - /* Allocate a new bucket */ - apNew = (jx9_hashmap_node **)SyMemBackendAlloc(&pMap->pVm->sAllocator, nNew * sizeof(jx9_hashmap_node *)); - if( apNew == 0 ){ - if( pMap->nSize < 1 ){ - return SXERR_MEM; /* Fatal */ - } - /* Not so fatal here, simply a performance hit */ - return SXRET_OK; - } - /* Zero the table */ - SyZero((void *)apNew, nNew * sizeof(jx9_hashmap_node *)); - /* Reflect the change */ - pMap->apBucket = apNew; - pMap->nSize = nNew; - if( apOld == 0 ){ - /* First allocated table [i.e: no entry], return immediately */ - return SXRET_OK; - } - /* Rehash old entries */ - pEntry = pMap->pFirst; - n = 0; - for( ;; ){ - if( n >= pMap->nEntry ){ - break; - } - /* Clear the old collision link */ - pEntry->pNextCollide = pEntry->pPrevCollide = 0; - /* Link to the new bucket */ - nBucket = pEntry->nHash & (nNew - 1); - if( pMap->apBucket[nBucket] != 0 ){ - pEntry->pNextCollide = pMap->apBucket[nBucket]; - pMap->apBucket[nBucket]->pPrevCollide = pEntry; - } - pMap->apBucket[nBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - n++; - } - /* Free the old table */ - SyMemBackendFree(&pMap->pVm->sAllocator, (void *)apOld); - } - return SXRET_OK; -} -/* - * Insert a 64-bit integer key and it's associated value (if any) in the given - * hashmap. - */ -static sxi32 HashmapInsertIntKey(jx9_hashmap *pMap,sxi64 iKey,jx9_value *pValue) -{ - jx9_hashmap_node *pNode; - jx9_value *pObj; - sxu32 nIdx; - sxu32 nHash; - sxi32 rc; - /* Reserve a jx9_value for the value */ - pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx); - if( pObj == 0 ){ - return SXERR_MEM; - } - if( pValue ){ - /* Duplicate the value */ - jx9MemObjStore(pValue, pObj); - } - /* Hash the key */ - nHash = pMap->xIntHash(iKey); - /* Allocate a new int node */ - pNode = HashmapNewIntNode(&(*pMap), iKey, nHash, nIdx); - if( pNode == 0 ){ - return SXERR_MEM; - } - /* Make sure the bucket is big enough to hold the new entry */ - rc = HashmapGrowBucket(&(*pMap)); - if( rc != SXRET_OK ){ - SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode); - return rc; - } - /* Perform the insertion */ - HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1)); - /* All done */ - return SXRET_OK; -} -/* - * Insert a BLOB key and it's associated value (if any) in the given - * hashmap. - */ -static sxi32 HashmapInsertBlobKey(jx9_hashmap *pMap,const void *pKey,sxu32 nKeyLen,jx9_value *pValue) -{ - jx9_hashmap_node *pNode; - jx9_value *pObj; - sxu32 nHash; - sxu32 nIdx; - sxi32 rc; - /* Reserve a jx9_value for the value */ - pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx); - if( pObj == 0 ){ - return SXERR_MEM; - } - if( pValue ){ - /* Duplicate the value */ - jx9MemObjStore(pValue, pObj); - } - /* Hash the key */ - nHash = pMap->xBlobHash(pKey, nKeyLen); - /* Allocate a new blob node */ - pNode = HashmapNewBlobNode(&(*pMap), pKey, nKeyLen, nHash, nIdx); - if( pNode == 0 ){ - return SXERR_MEM; - } - /* Make sure the bucket is big enough to hold the new entry */ - rc = HashmapGrowBucket(&(*pMap)); - if( rc != SXRET_OK ){ - SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode); - return rc; - } - /* Perform the insertion */ - HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1)); - /* All done */ - return SXRET_OK; -} -/* - * Check if a given 64-bit integer key exists in the given hashmap. - * Write a pointer to the target node on success. Otherwise - * SXERR_NOTFOUND is returned on failure. - */ -static sxi32 HashmapLookupIntKey( - jx9_hashmap *pMap, /* Target hashmap */ - sxi64 iKey, /* lookup key */ - jx9_hashmap_node **ppNode /* OUT: target node on success */ - ) -{ - jx9_hashmap_node *pNode; - sxu32 nHash; - if( pMap->nEntry < 1 ){ - /* Don't bother hashing, there is no entry anyway */ - return SXERR_NOTFOUND; - } - /* Hash the key first */ - nHash = pMap->xIntHash(iKey); - /* Point to the appropriate bucket */ - pNode = pMap->apBucket[nHash & (pMap->nSize - 1)]; - /* Perform the lookup */ - for(;;){ - if( pNode == 0 ){ - break; - } - if( pNode->iType == HASHMAP_INT_NODE - && pNode->nHash == nHash - && pNode->xKey.iKey == iKey ){ - /* Node found */ - if( ppNode ){ - *ppNode = pNode; - } - return SXRET_OK; - } - /* Follow the collision link */ - pNode = pNode->pNextCollide; - } - /* No such entry */ - return SXERR_NOTFOUND; -} -/* - * Check if a given BLOB key exists in the given hashmap. - * Write a pointer to the target node on success. Otherwise - * SXERR_NOTFOUND is returned on failure. - */ -static sxi32 HashmapLookupBlobKey( - jx9_hashmap *pMap, /* Target hashmap */ - const void *pKey, /* Lookup key */ - sxu32 nKeyLen, /* Key length in bytes */ - jx9_hashmap_node **ppNode /* OUT: target node on success */ - ) -{ - jx9_hashmap_node *pNode; - sxu32 nHash; - if( pMap->nEntry < 1 ){ - /* Don't bother hashing, there is no entry anyway */ - return SXERR_NOTFOUND; - } - /* Hash the key first */ - nHash = pMap->xBlobHash(pKey, nKeyLen); - /* Point to the appropriate bucket */ - pNode = pMap->apBucket[nHash & (pMap->nSize - 1)]; - /* Perform the lookup */ - for(;;){ - if( pNode == 0 ){ - break; - } - if( pNode->iType == HASHMAP_BLOB_NODE - && pNode->nHash == nHash - && SyBlobLength(&pNode->xKey.sKey) == nKeyLen - && SyMemcmp(SyBlobData(&pNode->xKey.sKey), pKey, nKeyLen) == 0 ){ - /* Node found */ - if( ppNode ){ - *ppNode = pNode; - } - return SXRET_OK; - } - /* Follow the collision link */ - pNode = pNode->pNextCollide; - } - /* No such entry */ - return SXERR_NOTFOUND; -} -/* - * Check if the given BLOB key looks like a decimal number. - * Retrurn TRUE on success.FALSE otherwise. - */ -static int HashmapIsIntKey(SyBlob *pKey) -{ - const char *zIn = (const char *)SyBlobData(pKey); - const char *zEnd = &zIn[SyBlobLength(pKey)]; - if( (int)(zEnd-zIn) > 1 && zIn[0] == '0' ){ - /* Octal not decimal number */ - return FALSE; - } - if( (zIn[0] == '-' || zIn[0] == '+') && &zIn[1] < zEnd ){ - zIn++; - } - for(;;){ - if( zIn >= zEnd ){ - return TRUE; - } - if( (unsigned char)zIn[0] >= 0xc0 /* UTF-8 stream */ || !SyisDigit(zIn[0]) ){ - break; - } - zIn++; - } - /* Key does not look like a decimal number */ - return FALSE; -} -/* - * Check if a given key exists in the given hashmap. - * Write a pointer to the target node on success. - * Otherwise SXERR_NOTFOUND is returned on failure. - */ -static sxi32 HashmapLookup( - jx9_hashmap *pMap, /* Target hashmap */ - jx9_value *pKey, /* Lookup key */ - jx9_hashmap_node **ppNode /* OUT: target node on success */ - ) -{ - jx9_hashmap_node *pNode = 0; /* cc -O6 warning */ - sxi32 rc; - if( pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES) ){ - if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(&(*pKey)); - } - if( SyBlobLength(&pKey->sBlob) > 0 ){ - /* Perform a blob lookup */ - rc = HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &pNode); - goto result; - } - } - /* Perform an int lookup */ - if((pKey->iFlags & MEMOBJ_INT) == 0 ){ - /* Force an integer cast */ - jx9MemObjToInteger(pKey); - } - /* Perform an int lookup */ - rc = HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode); -result: - if( rc == SXRET_OK ){ - /* Node found */ - if( ppNode ){ - *ppNode = pNode; - } - return SXRET_OK; - } - /* No such entry */ - return SXERR_NOTFOUND; -} -/* - * Insert a given key and it's associated value (if any) in the given - * hashmap. - * If a node with the given key already exists in the database - * then this function overwrite the old value. - */ -static sxi32 HashmapInsert( - jx9_hashmap *pMap, /* Target hashmap */ - jx9_value *pKey, /* Lookup key */ - jx9_value *pVal /* Node value */ - ) -{ - jx9_hashmap_node *pNode = 0; - sxi32 rc = SXRET_OK; - if( pMap->nEntry < 1 && pKey && (pKey->iFlags & MEMOBJ_STRING) ){ - pMap->iFlags |= HASHMAP_JSON_OBJECT; - } - if( pKey && (pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES)) ){ - if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(&(*pKey)); - } - if( SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob) ){ - if(SyBlobLength(&pKey->sBlob) < 1){ - /* Automatic index assign */ - pKey = 0; - } - goto IntKey; - } - if( SXRET_OK == HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), - SyBlobLength(&pKey->sBlob), &pNode) ){ - /* Overwrite the old value */ - jx9_value *pElem; - pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx); - if( pElem ){ - if( pVal ){ - jx9MemObjStore(pVal, pElem); - }else{ - /* Nullify the entry */ - jx9MemObjToNull(pElem); - } - } - return SXRET_OK; - } - /* Perform a blob-key insertion */ - rc = HashmapInsertBlobKey(&(*pMap),SyBlobData(&pKey->sBlob),SyBlobLength(&pKey->sBlob),&(*pVal)); - return rc; - } -IntKey: - if( pKey ){ - if((pKey->iFlags & MEMOBJ_INT) == 0 ){ - /* Force an integer cast */ - jx9MemObjToInteger(pKey); - } - if( SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode) ){ - /* Overwrite the old value */ - jx9_value *pElem; - pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx); - if( pElem ){ - if( pVal ){ - jx9MemObjStore(pVal, pElem); - }else{ - /* Nullify the entry */ - jx9MemObjToNull(pElem); - } - } - return SXRET_OK; - } - /* Perform a 64-bit-int-key insertion */ - rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal)); - if( rc == SXRET_OK ){ - if( pKey->x.iVal >= pMap->iNextIdx ){ - /* Increment the automatic index */ - pMap->iNextIdx = pKey->x.iVal + 1; - /* Make sure the automatic index is not reserved */ - while( SXRET_OK == HashmapLookupIntKey(&(*pMap), pMap->iNextIdx, 0) ){ - pMap->iNextIdx++; - } - } - } - }else{ - /* Assign an automatic index */ - rc = HashmapInsertIntKey(&(*pMap),pMap->iNextIdx,&(*pVal)); - if( rc == SXRET_OK ){ - ++pMap->iNextIdx; - } - } - /* Insertion result */ - return rc; -} -/* - * Extract node value. - */ -static jx9_value * HashmapExtractNodeValue(jx9_hashmap_node *pNode) -{ - /* Point to the desired object */ - jx9_value *pObj; - pObj = (jx9_value *)SySetAt(&pNode->pMap->pVm->aMemObj, pNode->nValIdx); - return pObj; -} -/* - * Insert a node in the given hashmap. - * If a node with the given key already exists in the database - * then this function overwrite the old value. - */ -static sxi32 HashmapInsertNode(jx9_hashmap *pMap, jx9_hashmap_node *pNode, int bPreserve) -{ - jx9_value *pObj; - sxi32 rc; - /* Extract the node value */ - pObj = HashmapExtractNodeValue(&(*pNode)); - if( pObj == 0 ){ - return SXERR_EMPTY; - } - /* Preserve key */ - if( pNode->iType == HASHMAP_INT_NODE){ - /* Int64 key */ - if( !bPreserve ){ - /* Assign an automatic index */ - rc = HashmapInsert(&(*pMap), 0, pObj); - }else{ - rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj); - } - }else{ - /* Blob key */ - rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey), - SyBlobLength(&pNode->xKey.sKey), pObj); - } - return rc; -} -/* - * Compare two node values. - * Return 0 if the node values are equals, > 0 if pLeft is greater than pRight - * or < 0 if pRight is greater than pLeft. - * For a full description on jx9_values comparison, refer to the implementation - * of the [jx9MemObjCmp()] function defined in memobj.c or the official - * documenation. - */ -static sxi32 HashmapNodeCmp(jx9_hashmap_node *pLeft, jx9_hashmap_node *pRight, int bStrict) -{ - jx9_value sObj1, sObj2; - sxi32 rc; - if( pLeft == pRight ){ - /* - * Same node.Refer to the sort() implementation defined - * below for more information on this sceanario. - */ - return 0; - } - /* Do the comparison */ - jx9MemObjInit(pLeft->pMap->pVm, &sObj1); - jx9MemObjInit(pLeft->pMap->pVm, &sObj2); - jx9HashmapExtractNodeValue(pLeft, &sObj1, FALSE); - jx9HashmapExtractNodeValue(pRight, &sObj2, FALSE); - rc = jx9MemObjCmp(&sObj1, &sObj2, bStrict, 0); - jx9MemObjRelease(&sObj1); - jx9MemObjRelease(&sObj2); - return rc; -} -/* - * Rehash a node with a 64-bit integer key. - * Refer to [merge_sort(), array_shift()] implementations for more information. - */ -static void HashmapRehashIntNode(jx9_hashmap_node *pEntry) -{ - jx9_hashmap *pMap = pEntry->pMap; - sxu32 nBucket; - /* Remove old collision links */ - if( pEntry->pPrevCollide ){ - pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide; - }else{ - pMap->apBucket[pEntry->nHash & (pMap->nSize - 1)] = pEntry->pNextCollide; - } - if( pEntry->pNextCollide ){ - pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide; - } - pEntry->pNextCollide = pEntry->pPrevCollide = 0; - /* Compute the new hash */ - pEntry->nHash = pMap->xIntHash(pMap->iNextIdx); - pEntry->xKey.iKey = pMap->iNextIdx; - nBucket = pEntry->nHash & (pMap->nSize - 1); - /* Link to the new bucket */ - pEntry->pNextCollide = pMap->apBucket[nBucket]; - if( pMap->apBucket[nBucket] ){ - pMap->apBucket[nBucket]->pPrevCollide = pEntry; - } - pEntry->pNextCollide = pMap->apBucket[nBucket]; - pMap->apBucket[nBucket] = pEntry; - /* Increment the automatic index */ - pMap->iNextIdx++; -} -/* - * Perform a linear search on a given hashmap. - * Write a pointer to the target node on success. - * Otherwise SXERR_NOTFOUND is returned on failure. - * Refer to [array_intersect(), array_diff(), in_array(), ...] implementations - * for more information. - */ -static int HashmapFindValue( - jx9_hashmap *pMap, /* Target hashmap */ - jx9_value *pNeedle, /* Lookup key */ - jx9_hashmap_node **ppNode, /* OUT: target node on success */ - int bStrict /* TRUE for strict comparison */ - ) -{ - jx9_hashmap_node *pEntry; - jx9_value sVal, *pVal; - jx9_value sNeedle; - sxi32 rc; - sxu32 n; - /* Perform a linear search since we cannot sort the hashmap based on values */ - pEntry = pMap->pFirst; - n = pMap->nEntry; - jx9MemObjInit(pMap->pVm, &sVal); - jx9MemObjInit(pMap->pVm, &sNeedle); - for(;;){ - if( n < 1 ){ - break; - } - /* Extract node value */ - pVal = HashmapExtractNodeValue(pEntry); - if( pVal ){ - if( (pVal->iFlags|pNeedle->iFlags) & MEMOBJ_NULL ){ - sxi32 iF1 = pVal->iFlags; - sxi32 iF2 = pNeedle->iFlags; - if( iF1 == iF2 ){ - /* NULL values are equals */ - if( ppNode ){ - *ppNode = pEntry; - } - return SXRET_OK; - } - }else{ - /* Duplicate value */ - jx9MemObjLoad(pVal, &sVal); - jx9MemObjLoad(pNeedle, &sNeedle); - rc = jx9MemObjCmp(&sNeedle, &sVal, bStrict, 0); - jx9MemObjRelease(&sVal); - jx9MemObjRelease(&sNeedle); - if( rc == 0 ){ - if( ppNode ){ - *ppNode = pEntry; - } - /* Match found*/ - return SXRET_OK; - } - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - n--; - } - /* No such entry */ - return SXERR_NOTFOUND; -} -/* - * Compare two hashmaps. - * Return 0 if the hashmaps are equals.Any other value indicates inequality. - * Note on array comparison operators. - * According to the JX9 language reference manual. - * Array Operators Example Name Result - * $a + $b Union Union of $a and $b. - * $a == $b Equality TRUE if $a and $b have the same key/value pairs. - * $a === $b Identity TRUE if $a and $b have the same key/value pairs in the same - * order and of the same types. - * $a != $b Inequality TRUE if $a is not equal to $b. - * $a <> $b Inequality TRUE if $a is not equal to $b. - * $a !== $b Non-identity TRUE if $a is not identical to $b. - * The + operator returns the right-hand array appended to the left-hand array; - * For keys that exist in both arrays, the elements from the left-hand array will be used - * and the matching elements from the right-hand array will be ignored. - * "apple", "b" => "banana"); - * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry"); - * $c = $a + $b; // Union of $a and $b - * print "Union of \$a and \$b: \n"; - * dump($c); - * $c = $b + $a; // Union of $b and $a - * print "Union of \$b and \$a: \n"; - * dump($c); - * ?> - * When executed, this script will print the following: - * Union of $a and $b: - * array(3) { - * ["a"]=> - * string(5) "apple" - * ["b"]=> - * string(6) "banana" - * ["c"]=> - * string(6) "cherry" - * } - * Union of $b and $a: - * array(3) { - * ["a"]=> - * string(4) "pear" - * ["b"]=> - * string(10) "strawberry" - * ["c"]=> - * string(6) "cherry" - * } - * Elements of arrays are equal for the comparison if they have the same key and value. - */ -JX9_PRIVATE sxi32 jx9HashmapCmp( - jx9_hashmap *pLeft, /* Left hashmap */ - jx9_hashmap *pRight, /* Right hashmap */ - int bStrict /* TRUE for strict comparison */ - ) -{ - jx9_hashmap_node *pLe, *pRe; - sxi32 rc; - sxu32 n; - if( pLeft == pRight ){ - /* Same hashmap instance. This can easily happen since hashmaps are passed by reference. - * Unlike the engine. - */ - return 0; - } - if( pLeft->nEntry != pRight->nEntry ){ - /* Must have the same number of entries */ - return pLeft->nEntry > pRight->nEntry ? 1 : -1; - } - /* Point to the first inserted entry of the left hashmap */ - pLe = pLeft->pFirst; - pRe = 0; /* cc warning */ - /* Perform the comparison */ - n = pLeft->nEntry; - for(;;){ - if( n < 1 ){ - break; - } - if( pLe->iType == HASHMAP_INT_NODE){ - /* Int key */ - rc = HashmapLookupIntKey(&(*pRight), pLe->xKey.iKey, &pRe); - }else{ - SyBlob *pKey = &pLe->xKey.sKey; - /* Blob key */ - rc = HashmapLookupBlobKey(&(*pRight), SyBlobData(pKey), SyBlobLength(pKey), &pRe); - } - if( rc != SXRET_OK ){ - /* No such entry in the right side */ - return 1; - } - rc = 0; - if( bStrict ){ - /* Make sure, the keys are of the same type */ - if( pLe->iType != pRe->iType ){ - rc = 1; - } - } - if( !rc ){ - /* Compare nodes */ - rc = HashmapNodeCmp(pLe, pRe, bStrict); - } - if( rc != 0 ){ - /* Nodes key/value differ */ - return rc; - } - /* Point to the next entry */ - pLe = pLe->pPrev; /* Reverse link */ - n--; - } - return 0; /* Hashmaps are equals */ -} -/* - * Merge two hashmaps. - * Note on the merge process - * According to the JX9 language reference manual. - * Merges the elements of two arrays together so that the values of one are appended - * to the end of the previous one. It returns the resulting array (pDest). - * If the input arrays have the same string keys, then the later value for that key - * will overwrite the previous one. If, however, the arrays contain numeric keys - * the later value will not overwrite the original value, but will be appended. - * Values in the input array with numeric keys will be renumbered with incrementing - * keys starting from zero in the result array. - */ -static sxi32 HashmapMerge(jx9_hashmap *pSrc, jx9_hashmap *pDest) -{ - jx9_hashmap_node *pEntry; - jx9_value sKey, *pVal; - sxi32 rc; - sxu32 n; - if( pSrc == pDest ){ - /* Same map. This can easily happen since hashmaps are passed by reference. - * Unlike the engine. - */ - return SXRET_OK; - } - /* Point to the first inserted entry in the source */ - pEntry = pSrc->pFirst; - /* Perform the merge */ - for( n = 0 ; n < pSrc->nEntry ; ++n ){ - /* Extract the node value */ - pVal = HashmapExtractNodeValue(pEntry); - if( pEntry->iType == HASHMAP_BLOB_NODE ){ - /* Blob key insertion */ - jx9MemObjInitFromString(pDest->pVm, &sKey, 0); - jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey)); - rc = jx9HashmapInsert(&(*pDest), &sKey, pVal); - jx9MemObjRelease(&sKey); - }else{ - rc = HashmapInsert(&(*pDest), 0/* Automatic index assign */, pVal); - } - if( rc != SXRET_OK ){ - return rc; - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - return SXRET_OK; -} -/* - * Duplicate the contents of a hashmap. Store the copy in pDest. - * Refer to the [array_pad(), array_copy(), ...] implementation for more information. - */ -JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest) -{ - jx9_hashmap_node *pEntry; - jx9_value sKey, *pVal; - sxi32 rc; - sxu32 n; - if( pSrc == pDest ){ - /* Same map. This can easily happen since hashmaps are passed by reference. - * Unlike the engine. - */ - return SXRET_OK; - } - /* Point to the first inserted entry in the source */ - pEntry = pSrc->pFirst; - /* Perform the duplication */ - for( n = 0 ; n < pSrc->nEntry ; ++n ){ - /* Extract the node value */ - pVal = HashmapExtractNodeValue(pEntry); - if( pEntry->iType == HASHMAP_BLOB_NODE ){ - /* Blob key insertion */ - jx9MemObjInitFromString(pDest->pVm, &sKey, 0); - jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey)); - rc = jx9HashmapInsert(&(*pDest), &sKey, pVal); - jx9MemObjRelease(&sKey); - }else{ - /* Int key insertion */ - rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal); - } - if( rc != SXRET_OK ){ - return rc; - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - return SXRET_OK; -} -/* - * Perform the union of two hashmaps. - * This operation is performed only if the user uses the '+' operator - * with a variable holding an array as follows: - * "apple", "b" => "banana"); - * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry"); - * $c = $a + $b; // Union of $a and $b - * print "Union of \$a and \$b: \n"; - * dump($c); - * $c = $b + $a; // Union of $b and $a - * print "Union of \$b and \$a: \n"; - * dump($c); - * ?> - * When executed, this script will print the following: - * Union of $a and $b: - * array(3) { - * ["a"]=> - * string(5) "apple" - * ["b"]=> - * string(6) "banana" - * ["c"]=> - * string(6) "cherry" - * } - * Union of $b and $a: - * array(3) { - * ["a"]=> - * string(4) "pear" - * ["b"]=> - * string(10) "strawberry" - * ["c"]=> - * string(6) "cherry" - * } - * The + operator returns the right-hand array appended to the left-hand array; - * For keys that exist in both arrays, the elements from the left-hand array will be used - * and the matching elements from the right-hand array will be ignored. - */ -JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight) -{ - jx9_hashmap_node *pEntry; - sxi32 rc = SXRET_OK; - jx9_value *pObj; - sxu32 n; - if( pLeft == pRight ){ - /* Same map. This can easily happen since hashmaps are passed by reference. - * Unlike the engine. - */ - return SXRET_OK; - } - /* Perform the union */ - pEntry = pRight->pFirst; - for(n = 0 ; n < pRight->nEntry ; ++n ){ - /* Make sure the given key does not exists in the left array */ - if( pEntry->iType == HASHMAP_BLOB_NODE ){ - /* BLOB key */ - if( SXRET_OK != - HashmapLookupBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), 0) ){ - pObj = HashmapExtractNodeValue(pEntry); - if( pObj ){ - /* Perform the insertion */ - rc = HashmapInsertBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), - SyBlobLength(&pEntry->xKey.sKey),pObj); - if( rc != SXRET_OK ){ - return rc; - } - } - } - }else{ - /* INT key */ - if( SXRET_OK != HashmapLookupIntKey(&(*pLeft), pEntry->xKey.iKey, 0) ){ - pObj = HashmapExtractNodeValue(pEntry); - if( pObj ){ - /* Perform the insertion */ - rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj); - if( rc != SXRET_OK ){ - return rc; - } - } - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - return SXRET_OK; -} -/* - * Allocate a new hashmap. - * Return a pointer to the freshly allocated hashmap on success.NULL otherwise. - */ -JX9_PRIVATE jx9_hashmap * jx9NewHashmap( - jx9_vm *pVm, /* VM that trigger the hashmap creation */ - sxu32 (*xIntHash)(sxi64), /* Hash function for int keys.NULL otherwise*/ - sxu32 (*xBlobHash)(const void *, sxu32) /* Hash function for BLOB keys.NULL otherwise */ - ) -{ - jx9_hashmap *pMap; - /* Allocate a new instance */ - pMap = (jx9_hashmap *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_hashmap)); - if( pMap == 0 ){ - return 0; - } - /* Zero the structure */ - SyZero(pMap, sizeof(jx9_hashmap)); - /* Fill in the structure */ - pMap->pVm = &(*pVm); - pMap->iRef = 1; - /* pMap->iFlags = 0; */ - /* Default hash functions */ - pMap->xIntHash = xIntHash ? xIntHash : IntHash; - pMap->xBlobHash = xBlobHash ? xBlobHash : BinHash; - return pMap; -} -/* - * Install superglobals in the given virtual machine. - * Note on superglobals. - * According to the JX9 language reference manual. - * Superglobals are built-in variables that are always available in all scopes. -* Description -* All predefined variables in JX9 are "superglobals", which means they -* are available in all scopes throughout a script. -* These variables are: -* $_SERVER -* $_GET -* $_POST -* $_FILES -* $_REQUEST -* $_ENV -*/ -JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm) -{ - static const char * azSuper[] = { - "_SERVER", /* $_SERVER */ - "_GET", /* $_GET */ - "_POST", /* $_POST */ - "_FILES", /* $_FILES */ - "_REQUEST", /* $_REQUEST */ - "_COOKIE", /* $_COOKIE */ - "_ENV", /* $_ENV */ - "_HEADER", /* $_HEADER */ - "argv" /* $argv */ - }; - SyString *pFile; - sxi32 rc; - sxu32 n; - /* Install globals variable now */ - for( n = 0 ; n < SX_ARRAYSIZE(azSuper) ; n++ ){ - jx9_value *pSuper; - /* Request an empty array */ - pSuper = jx9_new_array(&(*pVm)); - if( pSuper == 0 ){ - return SXERR_MEM; - } - /* Install */ - rc = jx9_vm_config(&(*pVm),JX9_VM_CONFIG_CREATE_VAR, azSuper[n]/* Super-global name*/, pSuper/* Super-global value */); - if( rc != SXRET_OK ){ - return rc; - } - /* Release the value now it have been installed */ - jx9_release_value(&(*pVm), pSuper); - } - /* Set some $_SERVER entries */ - pFile = (SyString *)SySetPeek(&pVm->aFiles); - /* - * 'SCRIPT_FILENAME' - * The absolute pathname of the currently executing script. - */ - jx9_vm_config(pVm, JX9_VM_CONFIG_SERVER_ATTR, - "SCRIPT_FILENAME", - pFile ? pFile->zString : ":Memory:", - pFile ? pFile->nByte : sizeof(":Memory:") - 1 - ); - /* All done, all global variables are installed now */ - return SXRET_OK; -} -/* - * Release a hashmap. - */ -JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS) -{ - jx9_hashmap_node *pEntry, *pNext; - jx9_vm *pVm = pMap->pVm; - sxu32 n; - /* Start the release process */ - n = 0; - pEntry = pMap->pFirst; - for(;;){ - if( n >= pMap->nEntry ){ - break; - } - pNext = pEntry->pPrev; /* Reverse link */ - /* Restore the jx9_value to the free list */ - jx9VmUnsetMemObj(pVm, pEntry->nValIdx); - /* Release the node */ - if( pEntry->iType == HASHMAP_BLOB_NODE ){ - SyBlobRelease(&pEntry->xKey.sKey); - } - SyMemBackendPoolFree(&pVm->sAllocator, pEntry); - /* Point to the next entry */ - pEntry = pNext; - n++; - } - if( pMap->nEntry > 0 ){ - /* Release the hash bucket */ - SyMemBackendFree(&pVm->sAllocator, pMap->apBucket); - } - if( FreeDS ){ - /* Free the whole instance */ - SyMemBackendPoolFree(&pVm->sAllocator, pMap); - }else{ - /* Keep the instance but reset it's fields */ - pMap->apBucket = 0; - pMap->iNextIdx = 0; - pMap->nEntry = pMap->nSize = 0; - pMap->pFirst = pMap->pLast = pMap->pCur = 0; - } - return SXRET_OK; -} -/* - * Decrement the reference count of a given hashmap. - * If the count reaches zero which mean no more variables - * are pointing to this hashmap, then release the whole instance. - */ -JX9_PRIVATE void jx9HashmapUnref(jx9_hashmap *pMap) -{ - pMap->iRef--; - if( pMap->iRef < 1 ){ - jx9HashmapRelease(pMap, TRUE); - } -} -/* - * Check if a given key exists in the given hashmap. - * Write a pointer to the target node on success. - * Otherwise SXERR_NOTFOUND is returned on failure. - */ -JX9_PRIVATE sxi32 jx9HashmapLookup( - jx9_hashmap *pMap, /* Target hashmap */ - jx9_value *pKey, /* Lookup key */ - jx9_hashmap_node **ppNode /* OUT: Target node on success */ - ) -{ - sxi32 rc; - if( pMap->nEntry < 1 ){ - /* TICKET 1433-25: Don't bother hashing, the hashmap is empty anyway. - */ - return SXERR_NOTFOUND; - } - rc = HashmapLookup(&(*pMap), &(*pKey), ppNode); - return rc; -} -/* - * Insert a given key and it's associated value (if any) in the given - * hashmap. - * If a node with the given key already exists in the database - * then this function overwrite the old value. - */ -JX9_PRIVATE sxi32 jx9HashmapInsert( - jx9_hashmap *pMap, /* Target hashmap */ - jx9_value *pKey, /* Lookup key */ - jx9_value *pVal /* Node value.NULL otherwise */ - ) -{ - sxi32 rc; - rc = HashmapInsert(&(*pMap), &(*pKey), &(*pVal)); - return rc; -} -/* - * Reset the node cursor of a given hashmap. - */ -JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap) -{ - /* Reset the loop cursor */ - pMap->pCur = pMap->pFirst; -} -/* - * Return a pointer to the node currently pointed by the node cursor. - * If the cursor reaches the end of the list, then this function - * return NULL. - * Note that the node cursor is automatically advanced by this function. - */ -JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap) -{ - jx9_hashmap_node *pCur = pMap->pCur; - if( pCur == 0 ){ - /* End of the list, return null */ - return 0; - } - /* Advance the node cursor */ - pMap->pCur = pCur->pPrev; /* Reverse link */ - return pCur; -} -/* - * Extract a node value. - */ -JX9_PRIVATE jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode) -{ - jx9_value *pValue; - pValue = HashmapExtractNodeValue(pNode); - return pValue; -} -/* - * Extract a node value (Second). - */ -JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore) -{ - jx9_value *pEntry = HashmapExtractNodeValue(pNode); - if( pEntry ){ - if( bStore ){ - jx9MemObjStore(pEntry, pValue); - }else{ - jx9MemObjLoad(pEntry, pValue); - } - }else{ - jx9MemObjRelease(pValue); - } -} -/* - * Extract a node key. - */ -JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode,jx9_value *pKey) -{ - /* Fill with the current key */ - if( pNode->iType == HASHMAP_INT_NODE ){ - if( SyBlobLength(&pKey->sBlob) > 0 ){ - SyBlobRelease(&pKey->sBlob); - } - pKey->x.iVal = pNode->xKey.iKey; - MemObjSetType(pKey, MEMOBJ_INT); - }else{ - SyBlobReset(&pKey->sBlob); - SyBlobAppend(&pKey->sBlob, SyBlobData(&pNode->xKey.sKey), SyBlobLength(&pNode->xKey.sKey)); - MemObjSetType(pKey, MEMOBJ_STRING); - } -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -/* - * Store the address of nodes value in the given container. - * Refer to the [vfprintf(), vprintf(), vsprintf()] implementations - * defined in 'builtin.c' for more information. - */ -JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut) -{ - jx9_hashmap_node *pEntry = pMap->pFirst; - jx9_value *pValue; - sxu32 n; - /* Initialize the container */ - SySetInit(pOut, &pMap->pVm->sAllocator, sizeof(jx9_value *)); - for(n = 0 ; n < pMap->nEntry ; n++ ){ - /* Extract node value */ - pValue = HashmapExtractNodeValue(pEntry); - if( pValue ){ - SySetPut(pOut, (const void *)&pValue); - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - /* Total inserted entries */ - return (int)SySetUsed(pOut); -} -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -/* - * Merge sort. - * The merge sort implementation is based on the one found in the SQLite3 source tree. - * Status: Public domain - */ -/* Node comparison callback signature */ -typedef sxi32 (*ProcNodeCmp)(jx9_hashmap_node *, jx9_hashmap_node *, void *); -/* -** Inputs: -** a: A sorted, null-terminated linked list. (May be null). -** b: A sorted, null-terminated linked list. (May be null). -** cmp: A pointer to the comparison function. -** -** Return Value: -** A pointer to the head of a sorted list containing the elements -** of both a and b. -** -** Side effects: -** The "next", "prev" pointers for elements in the lists a and b are -** changed. -*/ -static jx9_hashmap_node * HashmapNodeMerge(jx9_hashmap_node *pA, jx9_hashmap_node *pB, ProcNodeCmp xCmp, void *pCmpData) -{ - jx9_hashmap_node result, *pTail; - /* Prevent compiler warning */ - result.pNext = result.pPrev = 0; - pTail = &result; - while( pA && pB ){ - if( xCmp(pA, pB, pCmpData) < 0 ){ - pTail->pPrev = pA; - pA->pNext = pTail; - pTail = pA; - pA = pA->pPrev; - }else{ - pTail->pPrev = pB; - pB->pNext = pTail; - pTail = pB; - pB = pB->pPrev; - } - } - if( pA ){ - pTail->pPrev = pA; - pA->pNext = pTail; - }else if( pB ){ - pTail->pPrev = pB; - pB->pNext = pTail; - }else{ - pTail->pPrev = pTail->pNext = 0; - } - return result.pPrev; -} -/* -** Inputs: -** Map: Input hashmap -** cmp: A comparison function. -** -** Return Value: -** Sorted hashmap. -** -** Side effects: -** The "next" pointers for elements in list are changed. -*/ -#define N_SORT_BUCKET 32 -static sxi32 HashmapMergeSort(jx9_hashmap *pMap, ProcNodeCmp xCmp, void *pCmpData) -{ - jx9_hashmap_node *a[N_SORT_BUCKET], *p, *pIn; - sxu32 i; - SyZero(a, sizeof(a)); - /* Point to the first inserted entry */ - pIn = pMap->pFirst; - while( pIn ){ - p = pIn; - pIn = p->pPrev; - p->pPrev = 0; - for(i=0; ipNext = 0; - /* Reflect the change */ - pMap->pFirst = p; - /* Reset the loop cursor */ - pMap->pCur = pMap->pFirst; - return SXRET_OK; -} -/* - * Node comparison callback. - * used-by: [sort(), asort(), ...] - */ -static sxi32 HashmapCmpCallback1(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData) -{ - jx9_value sA, sB; - sxi32 iFlags; - int rc; - if( pCmpData == 0 ){ - /* Perform a standard comparison */ - rc = HashmapNodeCmp(pA, pB, FALSE); - return rc; - } - iFlags = SX_PTR_TO_INT(pCmpData); - /* Duplicate node values */ - jx9MemObjInit(pA->pMap->pVm, &sA); - jx9MemObjInit(pA->pMap->pVm, &sB); - jx9HashmapExtractNodeValue(pA, &sA, FALSE); - jx9HashmapExtractNodeValue(pB, &sB, FALSE); - if( iFlags == 5 ){ - /* String cast */ - if( (sA.iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(&sA); - } - if( (sB.iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(&sB); - } - }else{ - /* Numeric cast */ - jx9MemObjToNumeric(&sA); - jx9MemObjToNumeric(&sB); - } - /* Perform the comparison */ - rc = jx9MemObjCmp(&sA, &sB, FALSE, 0); - jx9MemObjRelease(&sA); - jx9MemObjRelease(&sB); - return rc; -} -/* - * Node comparison callback. - * Used by: [rsort(), arsort()]; - */ -static sxi32 HashmapCmpCallback3(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData) -{ - jx9_value sA, sB; - sxi32 iFlags; - int rc; - if( pCmpData == 0 ){ - /* Perform a standard comparison */ - rc = HashmapNodeCmp(pA, pB, FALSE); - return -rc; - } - iFlags = SX_PTR_TO_INT(pCmpData); - /* Duplicate node values */ - jx9MemObjInit(pA->pMap->pVm, &sA); - jx9MemObjInit(pA->pMap->pVm, &sB); - jx9HashmapExtractNodeValue(pA, &sA, FALSE); - jx9HashmapExtractNodeValue(pB, &sB, FALSE); - if( iFlags == 5 ){ - /* String cast */ - if( (sA.iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(&sA); - } - if( (sB.iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(&sB); - } - }else{ - /* Numeric cast */ - jx9MemObjToNumeric(&sA); - jx9MemObjToNumeric(&sB); - } - /* Perform the comparison */ - rc = jx9MemObjCmp(&sA, &sB, FALSE, 0); - jx9MemObjRelease(&sA); - jx9MemObjRelease(&sB); - return -rc; -} -/* - * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison. - * used-by: [usort(), uasort()] - */ -static sxi32 HashmapCmpCallback4(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData) -{ - jx9_value sResult, *pCallback; - jx9_value *pV1, *pV2; - jx9_value *apArg[2]; /* Callback arguments */ - sxi32 rc; - /* Point to the desired callback */ - pCallback = (jx9_value *)pCmpData; - /* initialize the result value */ - jx9MemObjInit(pA->pMap->pVm, &sResult); - /* Extract nodes values */ - pV1 = HashmapExtractNodeValue(pA); - pV2 = HashmapExtractNodeValue(pB); - apArg[0] = pV1; - apArg[1] = pV2; - /* Invoke the callback */ - rc = jx9VmCallUserFunction(pA->pMap->pVm, pCallback, 2, apArg, &sResult); - if( rc != SXRET_OK ){ - /* An error occured while calling user defined function [i.e: not defined] */ - rc = -1; /* Set a dummy result */ - }else{ - /* Extract callback result */ - if((sResult.iFlags & MEMOBJ_INT) == 0 ){ - /* Perform an int cast */ - jx9MemObjToInteger(&sResult); - } - rc = (sxi32)sResult.x.iVal; - } - jx9MemObjRelease(&sResult); - /* Callback result */ - return rc; -} -/* - * Rehash all nodes keys after a merge-sort have been applied. - * Used by [sort(), usort() and rsort()]. - */ -static void HashmapSortRehash(jx9_hashmap *pMap) -{ - jx9_hashmap_node *p, *pLast; - sxu32 i; - /* Rehash all entries */ - pLast = p = pMap->pFirst; - pMap->iNextIdx = 0; /* Reset the automatic index */ - i = 0; - for( ;; ){ - if( i >= pMap->nEntry ){ - pMap->pLast = pLast; /* Fix the last link broken by the merge-sort */ - break; - } - if( p->iType == HASHMAP_BLOB_NODE ){ - /* Do not maintain index association as requested by the JX9 specification */ - SyBlobRelease(&p->xKey.sKey); - /* Change key type */ - p->iType = HASHMAP_INT_NODE; - } - HashmapRehashIntNode(p); - /* Point to the next entry */ - i++; - pLast = p; - p = p->pPrev; /* Reverse link */ - } -} -/* - * Array functions implementation. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * bool sort(array &$array[, int $sort_flags = SORT_REGULAR ] ) - * Sort an array. - * Parameters - * $array - * The input array. - * $sort_flags - * The optional second parameter sort_flags may be used to modify the sorting behavior using these values: - * Sorting type flags: - * SORT_REGULAR - compare items normally (don't change types) - * SORT_NUMERIC - compare items numerically - * SORT_STRING - compare items as strings - * Return - * TRUE on success or FALSE on failure. - * - */ -static int jx9_hashmap_sort(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - /* Make sure we are dealing with a valid hashmap */ - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry > 1 ){ - sxi32 iCmpFlags = 0; - if( nArg > 1 ){ - /* Extract comparison flags */ - iCmpFlags = jx9_value_to_int(apArg[1]); - if( iCmpFlags == 3 /* SORT_REGULAR */ ){ - iCmpFlags = 0; /* Standard comparison */ - } - } - /* Do the merge sort */ - HashmapMergeSort(pMap, HashmapCmpCallback1, SX_INT_TO_PTR(iCmpFlags)); - /* Rehash [Do not maintain index association as requested by the JX9 specification] */ - HashmapSortRehash(pMap); - } - /* All done, return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_OK; -} -/* - * bool rsort(array &$array[, int $sort_flags = SORT_REGULAR ] ) - * Sort an array in reverse order. - * Parameters - * $array - * The input array. - * $sort_flags - * The optional second parameter sort_flags may be used to modify the sorting behavior using these values: - * Sorting type flags: - * SORT_REGULAR - compare items normally (don't change types) - * SORT_NUMERIC - compare items numerically - * SORT_STRING - compare items as strings - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9_hashmap_rsort(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - /* Make sure we are dealing with a valid hashmap */ - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry > 1 ){ - sxi32 iCmpFlags = 0; - if( nArg > 1 ){ - /* Extract comparison flags */ - iCmpFlags = jx9_value_to_int(apArg[1]); - if( iCmpFlags == 3 /* SORT_REGULAR */ ){ - iCmpFlags = 0; /* Standard comparison */ - } - } - /* Do the merge sort */ - HashmapMergeSort(pMap, HashmapCmpCallback3, SX_INT_TO_PTR(iCmpFlags)); - /* Rehash [Do not maintain index association as requested by the JX9 specification] */ - HashmapSortRehash(pMap); - } - /* All done, return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_OK; -} -/* - * bool usort(array &$array, callable $cmp_function) - * Sort an array by values using a user-defined comparison function. - * Parameters - * $array - * The input array. - * $cmp_function - * The comparison function must return an integer less than, equal to, or greater - * than zero if the first argument is considered to be respectively less than, equal - * to, or greater than the second. - * int callback ( mixed $a, mixed $b ) - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9_hashmap_usort(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - /* Make sure we are dealing with a valid hashmap */ - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry > 1 ){ - jx9_value *pCallback = 0; - ProcNodeCmp xCmp; - xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */ - if( nArg > 1 && jx9_value_is_callable(apArg[1]) ){ - /* Point to the desired callback */ - pCallback = apArg[1]; - }else{ - /* Use the default comparison function */ - xCmp = HashmapCmpCallback1; - } - /* Do the merge sort */ - HashmapMergeSort(pMap, xCmp, pCallback); - /* Rehash [Do not maintain index association as requested by the JX9 specification] */ - HashmapSortRehash(pMap); - } - /* All done, return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_OK; -} -/* - * int count(array $var [, int $mode = COUNT_NORMAL ]) - * Count all elements in an array, or something in an object. - * Parameters - * $var - * The array or the object. - * $mode - * If the optional mode parameter is set to COUNT_RECURSIVE (or 1), count() - * will recursively count the array. This is particularly useful for counting - * all the elements of a multidimensional array. count() does not detect infinite - * recursion. - * Return - * Returns the number of elements in the array. - */ -static int jx9_hashmap_count(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int bRecursive = FALSE; - sxi64 iCount; - if( nArg < 1 ){ - /* Missing arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - if( !jx9_value_is_json_array(apArg[0]) ){ - /* TICKET 1433-19: Handle objects */ - int res = !jx9_value_is_null(apArg[0]); - jx9_result_int(pCtx, res); - return JX9_OK; - } - if( nArg > 1 ){ - /* Recursive count? */ - bRecursive = jx9_value_to_int(apArg[1]) == 1 /* COUNT_RECURSIVE */; - } - /* Count */ - iCount = HashmapCount((jx9_hashmap *)apArg[0]->x.pOther, bRecursive, 0); - jx9_result_int64(pCtx, iCount); - return JX9_OK; -} -/* - * bool array_key_exists(value $key, array $search) - * Checks if the given key or index exists in the array. - * Parameters - * $key - * Value to check. - * $search - * An array with keys to check. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9_hashmap_key_exists(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[1]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the lookup */ - rc = jx9HashmapLookup((jx9_hashmap *)apArg[1]->x.pOther, apArg[0], 0); - /* lookup result */ - jx9_result_bool(pCtx, rc == SXRET_OK ? 1 : 0); - return JX9_OK; -} -/* - * value array_pop(array $array) - * POP the last inserted element from the array. - * Parameter - * The array to get the value from. - * Return - * Poped value or NULL on failure. - */ -static int jx9_hashmap_pop(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry < 1 ){ - /* Noting to pop, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_hashmap_node *pLast = pMap->pLast; - jx9_value *pObj; - pObj = HashmapExtractNodeValue(pLast); - if( pObj ){ - /* Node value */ - jx9_result_value(pCtx, pObj); - /* Unlink the node */ - jx9HashmapUnlinkNode(pLast); - }else{ - jx9_result_null(pCtx); - } - /* Reset the cursor */ - pMap->pCur = pMap->pFirst; - } - return JX9_OK; -} -/* - * int array_push($array, $var, ...) - * Push one or more elements onto the end of array. (Stack insertion) - * Parameters - * array - * The input array. - * var - * On or more value to push. - * Return - * New array count (including old items). - */ -static int jx9_hashmap_push(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - sxi32 rc; - int i; - if( nArg < 1 ){ - /* Missing arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - /* Start pushing given values */ - for( i = 1 ; i < nArg ; ++i ){ - rc = jx9HashmapInsert(pMap, 0, apArg[i]); - if( rc != SXRET_OK ){ - break; - } - } - /* Return the new count */ - jx9_result_int64(pCtx, (sxi64)pMap->nEntry); - return JX9_OK; -} -/* - * value array_shift(array $array) - * Shift an element off the beginning of array. - * Parameter - * The array to get the value from. - * Return - * Shifted value or NULL on failure. - */ -static int jx9_hashmap_shift(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry < 1 ){ - /* Empty hashmap, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_hashmap_node *pEntry = pMap->pFirst; - jx9_value *pObj; - sxu32 n; - pObj = HashmapExtractNodeValue(pEntry); - if( pObj ){ - /* Node value */ - jx9_result_value(pCtx, pObj); - /* Unlink the first node */ - jx9HashmapUnlinkNode(pEntry); - }else{ - jx9_result_null(pCtx); - } - /* Rehash all int keys */ - n = pMap->nEntry; - pEntry = pMap->pFirst; - pMap->iNextIdx = 0; /* Reset the automatic index */ - for(;;){ - if( n < 1 ){ - break; - } - if( pEntry->iType == HASHMAP_INT_NODE ){ - HashmapRehashIntNode(pEntry); - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - n--; - } - /* Reset the cursor */ - pMap->pCur = pMap->pFirst; - } - return JX9_OK; -} -/* - * Extract the node cursor value. - */ -static sxi32 HashmapCurrentValue(jx9_context *pCtx, jx9_hashmap *pMap, int iDirection) -{ - jx9_hashmap_node *pCur = pMap->pCur; - jx9_value *pVal; - if( pCur == 0 ){ - /* Cursor does not point to anything, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( iDirection != 0 ){ - if( iDirection > 0 ){ - /* Point to the next entry */ - pMap->pCur = pCur->pPrev; /* Reverse link */ - pCur = pMap->pCur; - }else{ - /* Point to the previous entry */ - pMap->pCur = pCur->pNext; /* Reverse link */ - pCur = pMap->pCur; - } - if( pCur == 0 ){ - /* End of input reached, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - } - /* Point to the desired element */ - pVal = HashmapExtractNodeValue(pCur); - if( pVal ){ - jx9_result_value(pCtx, pVal); - }else{ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * value current(array $array) - * Return the current element in an array. - * Parameter - * $input: The input array. - * Return - * The current() function simply returns the value of the array element that's currently - * being pointed to by the internal pointer. It does not move the pointer in any way. - * If the internal pointer points beyond the end of the elements list or the array - * is empty, current() returns FALSE. - */ -static int jx9_hashmap_current(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 0); - return JX9_OK; -} -/* - * value next(array $input) - * Advance the internal array pointer of an array. - * Parameter - * $input: The input array. - * Return - * next() behaves like current(), with one difference. It advances the internal array - * pointer one place forward before returning the element value. That means it returns - * the next array value and advances the internal array pointer by one. - */ -static int jx9_hashmap_next(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 1); - return JX9_OK; -} -/* - * value prev(array $input) - * Rewind the internal array pointer. - * Parameter - * $input: The input array. - * Return - * Returns the array value in the previous place that's pointed - * to by the internal array pointer, or FALSE if there are no more - * elements. - */ -static int jx9_hashmap_prev(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, -1); - return JX9_OK; -} -/* - * value end(array $input) - * Set the internal pointer of an array to its last element. - * Parameter - * $input: The input array. - * Return - * Returns the value of the last element or FALSE for empty array. - */ -static int jx9_hashmap_end(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - /* Point to the last node */ - pMap->pCur = pMap->pLast; - /* Return the last node value */ - HashmapCurrentValue(&(*pCtx), pMap, 0); - return JX9_OK; -} -/* - * value reset(array $array ) - * Set the internal pointer of an array to its first element. - * Parameter - * $input: The input array. - * Return - * Returns the value of the first array element, or FALSE if the array is empty. - */ -static int jx9_hashmap_reset(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - /* Point to the first node */ - pMap->pCur = pMap->pFirst; - /* Return the last node value if available */ - HashmapCurrentValue(&(*pCtx), pMap, 0); - return JX9_OK; -} -/* - * value key(array $array) - * Fetch a key from an array - * Parameter - * $input - * The input array. - * Return - * The key() function simply returns the key of the array element that's currently - * being pointed to by the internal pointer. It does not move the pointer in any way. - * If the internal pointer points beyond the end of the elements list or the array - * is empty, key() returns NULL. - */ -static int jx9_hashmap_simple_key(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap_node *pCur; - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - pCur = pMap->pCur; - if( pCur == 0 ){ - /* Cursor does not point to anything, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - if( pCur->iType == HASHMAP_INT_NODE){ - /* Key is integer */ - jx9_result_int64(pCtx, pCur->xKey.iKey); - }else{ - /* Key is blob */ - jx9_result_string(pCtx, - (const char *)SyBlobData(&pCur->xKey.sKey), (int)SyBlobLength(&pCur->xKey.sKey)); - } - return JX9_OK; -} -/* - * array each(array $input) - * Return the current key and value pair from an array and advance the array cursor. - * Parameter - * $input - * The input array. - * Return - * Returns the current key and value pair from the array array. This pair is returned - * in a four-element array, with the keys 0, 1, key, and value. Elements 0 and key - * contain the key name of the array element, and 1 and value contain the data. - * If the internal pointer for the array points past the end of the array contents - * each() returns FALSE. - */ -static int jx9_hashmap_each(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap_node *pCur; - jx9_hashmap *pMap; - jx9_value *pArray; - jx9_value *pVal; - jx9_value sKey; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation that describe the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->pCur == 0 ){ - /* Cursor does not point to anything, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pCur = pMap->pCur; - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pVal = HashmapExtractNodeValue(pCur); - /* Insert the current value */ - jx9_array_add_strkey_elem(pArray, "1", pVal); - jx9_array_add_strkey_elem(pArray, "value", pVal); - /* Make the key */ - if( pCur->iType == HASHMAP_INT_NODE ){ - jx9MemObjInitFromInt(pMap->pVm, &sKey, pCur->xKey.iKey); - }else{ - jx9MemObjInitFromString(pMap->pVm, &sKey, 0); - jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pCur->xKey.sKey), SyBlobLength(&pCur->xKey.sKey)); - } - /* Insert the current key */ - jx9_array_add_elem(pArray, 0, &sKey); - jx9_array_add_strkey_elem(pArray, "key", &sKey); - jx9MemObjRelease(&sKey); - /* Advance the cursor */ - pMap->pCur = pCur->pPrev; /* Reverse link */ - /* Return the current entry */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * array array_values(array $input) - * Returns all the values from the input array and indexes numerically the array. - * Parameters - * input: The input array. - * Return - * An indexed array of values or NULL on failure. - */ -static int jx9_hashmap_values(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap_node *pNode; - jx9_hashmap *pMap; - jx9_value *pArray; - jx9_value *pObj; - sxu32 n; - if( nArg < 1 ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation that describe the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Perform the requested operation */ - pNode = pMap->pFirst; - for( n = 0 ; n < pMap->nEntry ; ++n ){ - pObj = HashmapExtractNodeValue(pNode); - if( pObj ){ - /* perform the insertion */ - jx9_array_add_elem(pArray, 0/* Automatic index assign */, pObj); - } - /* Point to the next entry */ - pNode = pNode->pPrev; /* Reverse link */ - } - /* return the new array */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * bool array_same(array $arr1, array $arr2) - * Return TRUE if the given arrays are the same instance. - * This function is useful under JX9 since arrays and objects - * are passed by reference. - * Parameters - * $arr1 - * First array - * $arr2 - * Second array - * Return - * TRUE if the arrays are the same instance. FALSE otherwise. - * Note - * This function is a symisc eXtension. - */ -static int jx9_hashmap_same(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *p1, *p2; - int rc; - if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){ - /* Missing or invalid arguments, return FALSE*/ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the hashmaps */ - p1 = (jx9_hashmap *)apArg[0]->x.pOther; - p2 = (jx9_hashmap *)apArg[1]->x.pOther; - rc = (p1 == p2); - /* Same instance? */ - jx9_result_bool(pCtx, rc); - return JX9_OK; -} -/* - * array array_merge(array $array1, ...) - * Merge one or more arrays. - * Parameters - * $array1 - * Initial array to merge. - * ... - * More array to merge. - * Return - * The resulting array. - */ -static int jx9_hashmap_merge(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap, *pSrc; - jx9_value *pArray; - int i; - if( nArg < 1 ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_hashmap *)pArray->x.pOther; - /* Start merging */ - for( i = 0 ; i < nArg ; i++ ){ - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[i]) ){ - /* Insert scalar value */ - jx9_array_add_elem(pArray, 0, apArg[i]); - }else{ - pSrc = (jx9_hashmap *)apArg[i]->x.pOther; - /* Merge the two hashmaps */ - HashmapMerge(pSrc, pMap); - } - } - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * bool in_array(value $needle, array $haystack[, bool $strict = FALSE ]) - * Checks if a value exists in an array. - * Parameters - * $needle - * The searched value. - * Note: - * If needle is a string, the comparison is done in a case-sensitive manner. - * $haystack - * The target array. - * $strict - * If the third parameter strict is set to TRUE then the in_array() function - * will also check the types of the needle in the haystack. - */ -static int jx9_hashmap_in_array(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pNeedle; - int bStrict; - int rc; - if( nArg < 2 ){ - /* Missing argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pNeedle = apArg[0]; - bStrict = 0; - if( nArg > 2 ){ - bStrict = jx9_value_to_bool(apArg[2]); - } - if( !jx9_value_is_json_array(apArg[1]) ){ - /* haystack must be an array, perform a standard comparison */ - rc = jx9_value_compare(pNeedle, apArg[1], bStrict); - /* Set the comparison result */ - jx9_result_bool(pCtx, rc == 0); - return JX9_OK; - } - /* Perform the lookup */ - rc = HashmapFindValue((jx9_hashmap *)apArg[1]->x.pOther, pNeedle, 0, bStrict); - /* Lookup result */ - jx9_result_bool(pCtx, rc == SXRET_OK); - return JX9_OK; -} -/* - * array array_copy(array $source) - * Make a blind copy of the target array. - * Parameters - * $source - * Target array - * Return - * Copy of the target array on success. NULL otherwise. - * Note - * This function is a symisc eXtension. - */ -static int jx9_hashmap_copy(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - jx9_value *pArray; - if( nArg < 1 ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_hashmap *)pArray->x.pOther; - if( jx9_value_is_json_array(apArg[0])){ - /* Point to the internal representation of the source */ - jx9_hashmap *pSrc = (jx9_hashmap *)apArg[0]->x.pOther; - /* Perform the copy */ - jx9HashmapDup(pSrc, pMap); - }else{ - /* Simple insertion */ - jx9HashmapInsert(pMap, 0/* Automatic index assign*/, apArg[0]); - } - /* Return the duplicated array */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * bool array_erase(array $source) - * Remove all elements from a given array. - * Parameters - * $source - * Target array - * Return - * TRUE on success. FALSE otherwise. - * Note - * This function is a symisc eXtension. - */ -static int jx9_hashmap_erase(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - /* Erase */ - jx9HashmapRelease(pMap, FALSE); - return JX9_OK; -} -/* - * array array_diff(array $array1, array $array2, ...) - * Computes the difference of arrays. - * Parameters - * $array1 - * The array to compare from - * $array2 - * An array to compare against - * $... - * More arrays to compare against - * Return - * Returns an array containing all the entries from array1 that - * are not present in any of the other arrays. - */ -static int jx9_hashmap_diff(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap_node *pEntry; - jx9_hashmap *pSrc, *pMap; - jx9_value *pArray; - jx9_value *pVal; - sxi32 rc; - sxu32 n; - int i; - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - if( nArg == 1 ){ - /* Return the first array since we cannot perform a diff */ - jx9_result_value(pCtx, apArg[0]); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the source hashmap */ - pSrc = (jx9_hashmap *)apArg[0]->x.pOther; - /* Perform the diff */ - pEntry = pSrc->pFirst; - n = pSrc->nEntry; - for(;;){ - if( n < 1 ){ - break; - } - /* Extract the node value */ - pVal = HashmapExtractNodeValue(pEntry); - if( pVal ){ - for( i = 1 ; i < nArg ; i++ ){ - if( !jx9_value_is_json_array(apArg[i])) { - /* ignore */ - continue; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_hashmap *)apArg[i]->x.pOther; - /* Perform the lookup */ - rc = HashmapFindValue(pMap, pVal, 0, TRUE); - if( rc == SXRET_OK ){ - /* Value exist */ - break; - } - } - if( i >= nArg ){ - /* Perform the insertion */ - HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE); - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - n--; - } - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * array array_intersect(array $array1 , array $array2, ...) - * Computes the intersection of arrays. - * Parameters - * $array1 - * The array to compare from - * $array2 - * An array to compare against - * $... - * More arrays to compare against - * Return - * Returns an array containing all of the values in array1 whose values exist - * in all of the parameters. . - * Note that NULL is returned on failure. - */ -static int jx9_hashmap_intersect(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap_node *pEntry; - jx9_hashmap *pSrc, *pMap; - jx9_value *pArray; - jx9_value *pVal; - sxi32 rc; - sxu32 n; - int i; - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - if( nArg == 1 ){ - /* Return the first array since we cannot perform a diff */ - jx9_result_value(pCtx, apArg[0]); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the source hashmap */ - pSrc = (jx9_hashmap *)apArg[0]->x.pOther; - /* Perform the intersection */ - pEntry = pSrc->pFirst; - n = pSrc->nEntry; - for(;;){ - if( n < 1 ){ - break; - } - /* Extract the node value */ - pVal = HashmapExtractNodeValue(pEntry); - if( pVal ){ - for( i = 1 ; i < nArg ; i++ ){ - if( !jx9_value_is_json_array(apArg[i])) { - /* ignore */ - continue; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_hashmap *)apArg[i]->x.pOther; - /* Perform the lookup */ - rc = HashmapFindValue(pMap, pVal, 0, TRUE); - if( rc != SXRET_OK ){ - /* Value does not exist */ - break; - } - } - if( i >= nArg ){ - /* Perform the insertion */ - HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE); - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - n--; - } - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * number array_sum(array $array ) - * Calculate the sum of values in an array. - * Parameters - * $array: The input array. - * Return - * Returns the sum of values as an integer or float. - */ -static void DoubleSum(jx9_context *pCtx, jx9_hashmap *pMap) -{ - jx9_hashmap_node *pEntry; - jx9_value *pObj; - double dSum = 0; - sxu32 n; - pEntry = pMap->pFirst; - for( n = 0 ; n < pMap->nEntry ; n++ ){ - pObj = HashmapExtractNodeValue(pEntry); - if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){ - if( pObj->iFlags & MEMOBJ_REAL ){ - dSum += pObj->x.rVal; - }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ - dSum += (double)pObj->x.iVal; - }else if( pObj->iFlags & MEMOBJ_STRING ){ - if( SyBlobLength(&pObj->sBlob) > 0 ){ - double dv = 0; - SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0); - dSum += dv; - } - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - /* Return sum */ - jx9_result_double(pCtx, dSum); -} -static void Int64Sum(jx9_context *pCtx, jx9_hashmap *pMap) -{ - jx9_hashmap_node *pEntry; - jx9_value *pObj; - sxi64 nSum = 0; - sxu32 n; - pEntry = pMap->pFirst; - for( n = 0 ; n < pMap->nEntry ; n++ ){ - pObj = HashmapExtractNodeValue(pEntry); - if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){ - if( pObj->iFlags & MEMOBJ_REAL ){ - nSum += (sxi64)pObj->x.rVal; - }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ - nSum += pObj->x.iVal; - }else if( pObj->iFlags & MEMOBJ_STRING ){ - if( SyBlobLength(&pObj->sBlob) > 0 ){ - sxi64 nv = 0; - SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0); - nSum += nv; - } - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - /* Return sum */ - jx9_result_int64(pCtx, nSum); -} -/* number array_sum(array $array ) - * (See block-coment above) - */ -static int jx9_hashmap_sum(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - jx9_value *pObj; - if( nArg < 1 ){ - /* Missing arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry < 1 ){ - /* Nothing to compute, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* If the first element is of type float, then perform floating - * point computaion.Otherwise switch to int64 computaion. - */ - pObj = HashmapExtractNodeValue(pMap->pFirst); - if( pObj == 0 ){ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - if( pObj->iFlags & MEMOBJ_REAL ){ - DoubleSum(pCtx, pMap); - }else{ - Int64Sum(pCtx, pMap); - } - return JX9_OK; -} -/* - * number array_product(array $array ) - * Calculate the product of values in an array. - * Parameters - * $array: The input array. - * Return - * Returns the product of values as an integer or float. - */ -static void DoubleProd(jx9_context *pCtx, jx9_hashmap *pMap) -{ - jx9_hashmap_node *pEntry; - jx9_value *pObj; - double dProd; - sxu32 n; - pEntry = pMap->pFirst; - dProd = 1; - for( n = 0 ; n < pMap->nEntry ; n++ ){ - pObj = HashmapExtractNodeValue(pEntry); - if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){ - if( pObj->iFlags & MEMOBJ_REAL ){ - dProd *= pObj->x.rVal; - }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ - dProd *= (double)pObj->x.iVal; - }else if( pObj->iFlags & MEMOBJ_STRING ){ - if( SyBlobLength(&pObj->sBlob) > 0 ){ - double dv = 0; - SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0); - dProd *= dv; - } - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - /* Return product */ - jx9_result_double(pCtx, dProd); -} -static void Int64Prod(jx9_context *pCtx, jx9_hashmap *pMap) -{ - jx9_hashmap_node *pEntry; - jx9_value *pObj; - sxi64 nProd; - sxu32 n; - pEntry = pMap->pFirst; - nProd = 1; - for( n = 0 ; n < pMap->nEntry ; n++ ){ - pObj = HashmapExtractNodeValue(pEntry); - if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){ - if( pObj->iFlags & MEMOBJ_REAL ){ - nProd *= (sxi64)pObj->x.rVal; - }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ - nProd *= pObj->x.iVal; - }else if( pObj->iFlags & MEMOBJ_STRING ){ - if( SyBlobLength(&pObj->sBlob) > 0 ){ - sxi64 nv = 0; - SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0); - nProd *= nv; - } - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - /* Return product */ - jx9_result_int64(pCtx, nProd); -} -/* number array_product(array $array ) - * (See block-block comment above) - */ -static int jx9_hashmap_product(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - jx9_value *pObj; - if( nArg < 1 ){ - /* Missing arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry < 1 ){ - /* Nothing to compute, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* If the first element is of type float, then perform floating - * point computaion.Otherwise switch to int64 computaion. - */ - pObj = HashmapExtractNodeValue(pMap->pFirst); - if( pObj == 0 ){ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - if( pObj->iFlags & MEMOBJ_REAL ){ - DoubleProd(pCtx, pMap); - }else{ - Int64Prod(pCtx, pMap); - } - return JX9_OK; -} -/* - * array array_map(callback $callback, array $arr1) - * Applies the callback to the elements of the given arrays. - * Parameters - * $callback - * Callback function to run for each element in each array. - * $arr1 - * An array to run through the callback function. - * Return - * Returns an array containing all the elements of arr1 after applying - * the callback function to each one. - * NOTE: - * array_map() passes only a single value to the callback. - */ -static int jx9_hashmap_map(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pArray, *pValue, sKey, sResult; - jx9_hashmap_node *pEntry; - jx9_hashmap *pMap; - sxu32 n; - if( nArg < 2 || !jx9_value_is_json_array(apArg[1]) ){ - /* Invalid arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[1]->x.pOther; - jx9MemObjInit(pMap->pVm, &sResult); - jx9MemObjInit(pMap->pVm, &sKey); - /* Perform the requested operation */ - pEntry = pMap->pFirst; - for( n = 0 ; n < pMap->nEntry ; n++ ){ - /* Extrcat the node value */ - pValue = HashmapExtractNodeValue(pEntry); - if( pValue ){ - sxi32 rc; - /* Invoke the supplied callback */ - rc = jx9VmCallUserFunction(pMap->pVm, apArg[0], 1, &pValue, &sResult); - /* Extract the node key */ - jx9HashmapExtractNodeKey(pEntry, &sKey); - if( rc != SXRET_OK ){ - /* An error occured while invoking the supplied callback [i.e: not defined] */ - jx9_array_add_elem(pArray, &sKey, pValue); /* Keep the same value */ - }else{ - /* Insert the callback return value */ - jx9_array_add_elem(pArray, &sKey, &sResult); - } - jx9MemObjRelease(&sKey); - jx9MemObjRelease(&sResult); - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * bool array_walk(array &$array, callback $funcname [, value $userdata ] ) - * Apply a user function to every member of an array. - * Parameters - * $array - * The input array. - * $funcname - * Typically, funcname takes on two parameters.The array parameter's value being - * the first, and the key/index second. - * Note: - * If funcname needs to be working with the actual values of the array, specify the first - * parameter of funcname as a reference. Then, any changes made to those elements will - * be made in the original array itself. - * $userdata - * If the optional userdata parameter is supplied, it will be passed as the third parameter - * to the callback funcname. - * Return - * Returns TRUE on success or FALSE on failure. - */ -static int jx9_hashmap_walk(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pValue, *pUserData, sKey; - jx9_hashmap_node *pEntry; - jx9_hashmap *pMap; - sxi32 rc; - sxu32 n; - if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid/Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pUserData = nArg > 2 ? apArg[2] : 0; - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - jx9MemObjInit(pMap->pVm, &sKey); - /* Perform the desired operation */ - pEntry = pMap->pFirst; - for( n = 0 ; n < pMap->nEntry ; n++ ){ - /* Extract the node value */ - pValue = HashmapExtractNodeValue(pEntry); - if( pValue ){ - /* Extract the entry key */ - jx9HashmapExtractNodeKey(pEntry, &sKey); - /* Invoke the supplied callback */ - rc = jx9VmCallUserFunctionAp(pMap->pVm, apArg[1], 0, pValue, &sKey, pUserData, 0); - jx9MemObjRelease(&sKey); - if( rc != SXRET_OK ){ - /* An error occured while invoking the supplied callback [i.e: not defined] */ - jx9_result_bool(pCtx, 0); /* return FALSE */ - return JX9_OK; - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - /* All done, return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_OK; -} -/* - * Table of built-in hashmap functions. - */ -static const jx9_builtin_func aHashmapFunc[] = { - {"count", jx9_hashmap_count }, - {"sizeof", jx9_hashmap_count }, - {"array_key_exists", jx9_hashmap_key_exists }, - {"array_pop", jx9_hashmap_pop }, - {"array_push", jx9_hashmap_push }, - {"array_shift", jx9_hashmap_shift }, - {"array_product", jx9_hashmap_product }, - {"array_sum", jx9_hashmap_sum }, - {"array_values", jx9_hashmap_values }, - {"array_same", jx9_hashmap_same }, - {"array_merge", jx9_hashmap_merge }, - {"array_diff", jx9_hashmap_diff }, - {"array_intersect", jx9_hashmap_intersect}, - {"in_array", jx9_hashmap_in_array }, - {"array_copy", jx9_hashmap_copy }, - {"array_erase", jx9_hashmap_erase }, - {"array_map", jx9_hashmap_map }, - {"array_walk", jx9_hashmap_walk }, - {"sort", jx9_hashmap_sort }, - {"rsort", jx9_hashmap_rsort }, - {"usort", jx9_hashmap_usort }, - {"current", jx9_hashmap_current }, - {"each", jx9_hashmap_each }, - {"pos", jx9_hashmap_current }, - {"next", jx9_hashmap_next }, - {"prev", jx9_hashmap_prev }, - {"end", jx9_hashmap_end }, - {"reset", jx9_hashmap_reset }, - {"key", jx9_hashmap_simple_key } -}; -/* - * Register the built-in hashmap functions defined above. - */ -JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm) -{ - sxu32 n; - for( n = 0 ; n < SX_ARRAYSIZE(aHashmapFunc) ; n++ ){ - jx9_create_function(&(*pVm), aHashmapFunc[n].zName, aHashmapFunc[n].xFunc, 0); - } -} -/* - * Iterate throw hashmap entries and invoke the given callback [i.e: xWalk()] for each - * retrieved entry. - * Note that argument are passed to the callback by copy. That is, any modification to - * the entry value in the callback body will not alter the real value. - * If the callback wishes to abort processing [i.e: it's invocation] it must return - * a value different from JX9_OK. - * Refer to [jx9_array_walk()] for more information. - */ -JX9_PRIVATE sxi32 jx9HashmapWalk( - jx9_hashmap *pMap, /* Target hashmap */ - int (*xWalk)(jx9_value *, jx9_value *, void *), /* Walker callback */ - void *pUserData /* Last argument to xWalk() */ - ) -{ - jx9_hashmap_node *pEntry; - jx9_value sKey, sValue; - sxi32 rc; - sxu32 n; - /* Initialize walker parameter */ - rc = SXRET_OK; - jx9MemObjInit(pMap->pVm, &sKey); - jx9MemObjInit(pMap->pVm, &sValue); - n = pMap->nEntry; - pEntry = pMap->pFirst; - /* Start the iteration process */ - for(;;){ - if( n < 1 ){ - break; - } - /* Extract a copy of the key and a copy the current value */ - jx9HashmapExtractNodeKey(pEntry, &sKey); - jx9HashmapExtractNodeValue(pEntry, &sValue, FALSE); - /* Invoke the user callback */ - rc = xWalk(&sKey, &sValue, pUserData); - /* Release the copy of the key and the value */ - jx9MemObjRelease(&sKey); - jx9MemObjRelease(&sValue); - if( rc != JX9_OK ){ - /* Callback request an operation abort */ - return SXERR_ABORT; - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - n--; - } - /* All done */ - return SXRET_OK; -} -/* - * ---------------------------------------------------------- - * File: jx9_json.c - * MD5: 31a27f8797418de511c669feed763341 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: json.c v1.0 FreeBSD 2012-12-16 00:28 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* This file deals with JSON serialization, decoding and stuff like that. */ -/* - * Section: - * JSON encoding/decoding routines. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Devel. - */ -/* Forward reference */ -static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData); -static int VmJsonObjectEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData); -/* - * JSON encoder state is stored in an instance - * of the following structure. - */ -typedef struct json_private_data json_private_data; -struct json_private_data -{ - SyBlob *pOut; /* Output consumer buffer */ - int isFirst; /* True if first encoded entry */ - int iFlags; /* JSON encoding flags */ - int nRecCount; /* Recursion count */ -}; -/* - * Returns the JSON representation of a value.In other word perform a JSON encoding operation. - * According to wikipedia - * JSON's basic types are: - * Number (double precision floating-point format in JavaScript, generally depends on implementation) - * String (double-quoted Unicode, with backslash escaping) - * Boolean (true or false) - * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values - * do not need to be of the same type) - * Object (an unordered collection of key:value pairs with the ':' character separating the key - * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should - * be distinct from each other) - * null (empty) - * Non-significant white space may be added freely around the "structural characters" - * (i.e. the brackets "[{]}", colon ":" and comma ","). - */ -static sxi32 VmJsonEncode( - jx9_value *pIn, /* Encode this value */ - json_private_data *pData /* Context data */ - ){ - SyBlob *pOut = pData->pOut; - int nByte; - if( jx9_value_is_null(pIn) || jx9_value_is_resource(pIn)){ - /* null */ - SyBlobAppend(pOut, "null", sizeof("null")-1); - }else if( jx9_value_is_bool(pIn) ){ - int iBool = jx9_value_to_bool(pIn); - sxu32 iLen; - /* true/false */ - iLen = iBool ? sizeof("true") : sizeof("false"); - SyBlobAppend(pOut, iBool ? "true" : "false", iLen-1); - }else if( jx9_value_is_numeric(pIn) && !jx9_value_is_string(pIn) ){ - const char *zNum; - /* Get a string representation of the number */ - zNum = jx9_value_to_string(pIn, &nByte); - SyBlobAppend(pOut,zNum,nByte); - }else if( jx9_value_is_string(pIn) ){ - const char *zIn, *zEnd; - int c; - /* Encode the string */ - zIn = jx9_value_to_string(pIn, &nByte); - zEnd = &zIn[nByte]; - /* Append the double quote */ - SyBlobAppend(pOut,"\"", sizeof(char)); - for(;;){ - if( zIn >= zEnd ){ - /* No more input to process */ - break; - } - c = zIn[0]; - /* Advance the stream cursor */ - zIn++; - if( c == '"' || c == '\\' ){ - /* Unescape the character */ - SyBlobAppend(pOut,"\\", sizeof(char)); - } - /* Append character verbatim */ - SyBlobAppend(pOut,(const char *)&c,sizeof(char)); - } - /* Append the double quote */ - SyBlobAppend(pOut,"\"",sizeof(char)); - }else if( jx9_value_is_json_array(pIn) ){ - /* Encode the array/object */ - pData->isFirst = 1; - if( jx9_value_is_json_object(pIn) ){ - /* Encode the object instance */ - pData->isFirst = 1; - /* Append the curly braces */ - SyBlobAppend(pOut, "{",sizeof(char)); - /* Iterate throw object attribute */ - jx9_array_walk(pIn,VmJsonObjectEncode, pData); - /* Append the closing curly braces */ - SyBlobAppend(pOut, "}",sizeof(char)); - }else{ - /* Append the square bracket or curly braces */ - SyBlobAppend(pOut,"[",sizeof(char)); - /* Iterate throw array entries */ - jx9_array_walk(pIn, VmJsonArrayEncode, pData); - /* Append the closing square bracket or curly braces */ - SyBlobAppend(pOut,"]",sizeof(char)); - } - }else{ - /* Can't happen */ - SyBlobAppend(pOut,"null",sizeof("null")-1); - } - /* All done */ - return JX9_OK; -} -/* - * The following walker callback is invoked each time we need - * to encode an array to JSON. - */ -static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData) -{ - json_private_data *pJson = (json_private_data *)pUserData; - if( pJson->nRecCount > 31 ){ - /* Recursion limit reached, return immediately */ - SXUNUSED(pKey); /* cc warning */ - return JX9_OK; - } - if( !pJson->isFirst ){ - /* Append the colon first */ - SyBlobAppend(pJson->pOut,",",(int)sizeof(char)); - } - /* Encode the value */ - pJson->nRecCount++; - VmJsonEncode(pValue, pJson); - pJson->nRecCount--; - pJson->isFirst = 0; - return JX9_OK; -} -/* - * The following walker callback is invoked each time we need to encode - * a object instance [i.e: Object in the JX9 jargon] to JSON. - */ -static int VmJsonObjectEncode(jx9_value *pKey,jx9_value *pValue,void *pUserData) -{ - json_private_data *pJson = (json_private_data *)pUserData; - const char *zKey; - int nByte; - if( pJson->nRecCount > 31 ){ - /* Recursion limit reached, return immediately */ - return JX9_OK; - } - if( !pJson->isFirst ){ - /* Append the colon first */ - SyBlobAppend(pJson->pOut,",",sizeof(char)); - } - /* Extract a string representation of the key */ - zKey = jx9_value_to_string(pKey, &nByte); - /* Append the key and the double colon */ - if( nByte > 0 ){ - SyBlobAppend(pJson->pOut,"\"",sizeof(char)); - SyBlobAppend(pJson->pOut,zKey,(sxu32)nByte); - SyBlobAppend(pJson->pOut,"\"",sizeof(char)); - }else{ - /* Can't happen */ - SyBlobAppend(pJson->pOut,"null",sizeof("null")-1); - } - SyBlobAppend(pJson->pOut,":",sizeof(char)); - /* Encode the value */ - pJson->nRecCount++; - VmJsonEncode(pValue, pJson); - pJson->nRecCount--; - pJson->isFirst = 0; - return JX9_OK; -} -/* - * Returns a string containing the JSON representation of value. - * In other words, perform the serialization of the given JSON object. - */ -JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut) -{ - json_private_data sJson; - /* Prepare the JSON data */ - sJson.nRecCount = 0; - sJson.pOut = pOut; - sJson.isFirst = 1; - sJson.iFlags = 0; - /* Perform the encoding operation */ - VmJsonEncode(pValue, &sJson); - /* All done */ - return JX9_OK; -} -/* Possible tokens from the JSON tokenization process */ -#define JSON_TK_TRUE 0x001 /* Boolean true */ -#define JSON_TK_FALSE 0x002 /* Boolean false */ -#define JSON_TK_STR 0x004 /* String enclosed in double quotes */ -#define JSON_TK_NULL 0x008 /* null */ -#define JSON_TK_NUM 0x010 /* Numeric */ -#define JSON_TK_OCB 0x020 /* Open curly braces '{' */ -#define JSON_TK_CCB 0x040 /* Closing curly braces '}' */ -#define JSON_TK_OSB 0x080 /* Open square bracke '[' */ -#define JSON_TK_CSB 0x100 /* Closing square bracket ']' */ -#define JSON_TK_COLON 0x200 /* Single colon ':' */ -#define JSON_TK_COMMA 0x400 /* Single comma ',' */ -#define JSON_TK_ID 0x800 /* ID */ -#define JSON_TK_INVALID 0x1000 /* Unexpected token */ -/* - * Tokenize an entire JSON input. - * Get a single low-level token from the input file. - * Update the stream pointer so that it points to the first - * character beyond the extracted token. - */ -static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData) -{ - int *pJsonErr = (int *)pUserData; - SyString *pStr; - int c; - /* Ignore leading white spaces */ - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){ - /* Advance the stream cursor */ - if( pStream->zText[0] == '\n' ){ - /* Update line counter */ - pStream->nLine++; - } - pStream->zText++; - } - if( pStream->zText >= pStream->zEnd ){ - /* End of input reached */ - SXUNUSED(pCtxData); /* cc warning */ - return SXERR_EOF; - } - /* Record token starting position and line */ - pToken->nLine = pStream->nLine; - pToken->pUserData = 0; - pStr = &pToken->sData; - SyStringInitFromBuf(pStr, pStream->zText, 0); - if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){ - /* The following code fragment is taken verbatim from the xPP source tree. - * xPP is a modern embeddable macro processor with advanced features useful for - * application seeking for a production quality, ready to use macro processor. - * xPP is a widely used library developed and maintened by Symisc Systems. - * You can reach the xPP home page by following this link: - * http://xpp.symisc.net/ - */ - const unsigned char *zIn; - /* Isolate UTF-8 or alphanumeric stream */ - if( pStream->zText[0] < 0xc0 ){ - pStream->zText++; - } - for(;;){ - zIn = pStream->zText; - if( zIn[0] >= 0xc0 ){ - zIn++; - /* UTF-8 stream */ - while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){ - zIn++; - } - } - /* Skip alphanumeric stream */ - while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){ - zIn++; - } - if( zIn == pStream->zText ){ - /* Not an UTF-8 or alphanumeric stream */ - break; - } - /* Synchronize pointers */ - pStream->zText = zIn; - } - /* Record token length */ - pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); - /* A simple identifier */ - pToken->nType = JSON_TK_ID; - if( pStr->nByte == sizeof("true") -1 && SyStrnicmp(pStr->zString, "true", sizeof("true")-1) == 0 ){ - /* boolean true */ - pToken->nType = JSON_TK_TRUE; - }else if( pStr->nByte == sizeof("false") -1 && SyStrnicmp(pStr->zString,"false", sizeof("false")-1) == 0 ){ - /* boolean false */ - pToken->nType = JSON_TK_FALSE; - }else if( pStr->nByte == sizeof("null") -1 && SyStrnicmp(pStr->zString,"null", sizeof("null")-1) == 0 ){ - /* NULL */ - pToken->nType = JSON_TK_NULL; - } - return SXRET_OK; - } - if( pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']' - || pStream->zText[0] == ':' || pStream->zText[0] == ',' ){ - /* Single character */ - c = pStream->zText[0]; - /* Set token type */ - switch(c){ - case '[': pToken->nType = JSON_TK_OSB; break; - case '{': pToken->nType = JSON_TK_OCB; break; - case '}': pToken->nType = JSON_TK_CCB; break; - case ']': pToken->nType = JSON_TK_CSB; break; - case ':': pToken->nType = JSON_TK_COLON; break; - case ',': pToken->nType = JSON_TK_COMMA; break; - default: - break; - } - /* Advance the stream cursor */ - pStream->zText++; - }else if( pStream->zText[0] == '"') { - /* JSON string */ - pStream->zText++; - pStr->zString++; - /* Delimit the string */ - while( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '"' && pStream->zText[-1] != '\\' ){ - break; - } - if( pStream->zText[0] == '\n' ){ - /* Update line counter */ - pStream->nLine++; - } - pStream->zText++; - } - if( pStream->zText >= pStream->zEnd ){ - /* Missing closing '"' */ - pToken->nType = JSON_TK_INVALID; - *pJsonErr = SXERR_SYNTAX; - }else{ - pToken->nType = JSON_TK_STR; - pStream->zText++; /* Jump the closing double quotes */ - } - }else if( pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ - /* Number */ - pStream->zText++; - pToken->nType = JSON_TK_NUM; - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ - pStream->zText++; - } - if( pStream->zText < pStream->zEnd ){ - c = pStream->zText[0]; - if( c == '.' ){ - /* Real number */ - pStream->zText++; - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ - pStream->zText++; - } - if( pStream->zText < pStream->zEnd ){ - c = pStream->zText[0]; - if( c=='e' || c=='E' ){ - pStream->zText++; - if( pStream->zText < pStream->zEnd ){ - c = pStream->zText[0]; - if( c =='+' || c=='-' ){ - pStream->zText++; - } - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ - pStream->zText++; - } - } - } - } - }else if( c=='e' || c=='E' ){ - /* Real number */ - pStream->zText++; - if( pStream->zText < pStream->zEnd ){ - c = pStream->zText[0]; - if( c =='+' || c=='-' ){ - pStream->zText++; - } - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ - pStream->zText++; - } - } - } - } - }else{ - /* Unexpected token */ - pToken->nType = JSON_TK_INVALID; - /* Advance the stream cursor */ - pStream->zText++; - *pJsonErr = SXERR_SYNTAX; - /* Abort processing immediatley */ - return SXERR_ABORT; - } - /* record token length */ - pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); - if( pToken->nType == JSON_TK_STR ){ - pStr->nByte--; - } - /* Return to the lexer */ - return SXRET_OK; -} -/* - * JSON decoded input consumer callback signature. - */ -typedef int (*ProcJSONConsumer)(jx9_context *, jx9_value *, jx9_value *, void *); -/* - * JSON decoder state is kept in the following structure. - */ -typedef struct json_decoder json_decoder; -struct json_decoder -{ - jx9_context *pCtx; /* Call context */ - ProcJSONConsumer xConsumer; /* Consumer callback */ - void *pUserData; /* Last argument to xConsumer() */ - int iFlags; /* Configuration flags */ - SyToken *pIn; /* Token stream */ - SyToken *pEnd; /* End of the token stream */ - int rec_count; /* Current nesting level */ - int *pErr; /* JSON decoding error if any */ -}; -/* Forward declaration */ -static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData); -/* - * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store - * the result in the given jx9_value. - */ -static void VmJsonDequoteString(const SyString *pStr, jx9_value *pWorker) -{ - const char *zIn = pStr->zString; - const char *zEnd = &pStr->zString[pStr->nByte]; - const char *zCur; - int c; - /* Mark the value as a string */ - jx9_value_string(pWorker, "", 0); /* Empty string */ - for(;;){ - zCur = zIn; - while( zIn < zEnd && zIn[0] != '\\' ){ - zIn++; - } - if( zIn > zCur ){ - /* Append chunk verbatim */ - jx9_value_string(pWorker, zCur, (int)(zIn-zCur)); - } - zIn++; - if( zIn >= zEnd ){ - /* End of the input reached */ - break; - } - c = zIn[0]; - /* Unescape the character */ - switch(c){ - case '"': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break; - case '\\': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break; - case 'n': jx9_value_string(pWorker, "\n", (int)sizeof(char)); break; - case 'r': jx9_value_string(pWorker, "\r", (int)sizeof(char)); break; - case 't': jx9_value_string(pWorker, "\t", (int)sizeof(char)); break; - case 'f': jx9_value_string(pWorker, "\f", (int)sizeof(char)); break; - default: - jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); - break; - } - /* Advance the stream cursor */ - zIn++; - } -} -/* - * Returns a jx9_value holding the image of a JSON string. In other word perform a JSON decoding operation. - * According to wikipedia - * JSON's basic types are: - * Number (double precision floating-point format in JavaScript, generally depends on implementation) - * String (double-quoted Unicode, with backslash escaping) - * Boolean (true or false) - * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values - * do not need to be of the same type) - * Object (an unordered collection of key:value pairs with the ':' character separating the key - * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should - * be distinct from each other) - * null (empty) - * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ", "). - */ -static sxi32 VmJsonDecode( - json_decoder *pDecoder, /* JSON decoder */ - jx9_value *pArrayKey /* Key for the decoded array */ - ){ - jx9_value *pWorker; /* Worker variable */ - sxi32 rc; - /* Check if we do not nest to much */ - if( pDecoder->rec_count > 31 ){ - /* Nesting limit reached, abort decoding immediately */ - return SXERR_ABORT; - } - if( pDecoder->pIn->nType & (JSON_TK_STR|JSON_TK_ID|JSON_TK_TRUE|JSON_TK_FALSE|JSON_TK_NULL|JSON_TK_NUM) ){ - /* Scalar value */ - pWorker = jx9_context_new_scalar(pDecoder->pCtx); - if( pWorker == 0 ){ - jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - /* Abort the decoding operation immediately */ - return SXERR_ABORT; - } - /* Reflect the JSON image */ - if( pDecoder->pIn->nType & JSON_TK_NULL ){ - /* Nullify the value.*/ - jx9_value_null(pWorker); - }else if( pDecoder->pIn->nType & (JSON_TK_TRUE|JSON_TK_FALSE) ){ - /* Boolean value */ - jx9_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0 ); - }else if( pDecoder->pIn->nType & JSON_TK_NUM ){ - SyString *pStr = &pDecoder->pIn->sData; - /* - * Numeric value. - * Get a string representation first then try to get a numeric - * value. - */ - jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte); - /* Obtain a numeric representation */ - jx9MemObjToNumeric(pWorker); - }else if( pDecoder->pIn->nType & JSON_TK_ID ){ - SyString *pStr = &pDecoder->pIn->sData; - jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte); - }else{ - /* Dequote the string */ - VmJsonDequoteString(&pDecoder->pIn->sData, pWorker); - } - /* Invoke the consumer callback */ - rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - /* All done, advance the stream cursor */ - pDecoder->pIn++; - }else if( pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) { - ProcJSONConsumer xOld; - void *pOld; - /* Array representation*/ - pDecoder->pIn++; - /* Create a working array */ - pWorker = jx9_context_new_array(pDecoder->pCtx); - if( pWorker == 0 ){ - jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - /* Abort the decoding operation immediately */ - return SXERR_ABORT; - } - /* Save the old consumer */ - xOld = pDecoder->xConsumer; - pOld = pDecoder->pUserData; - /* Set the new consumer */ - pDecoder->xConsumer = VmJsonArrayDecoder; - pDecoder->pUserData = pWorker; - /* Decode the array */ - for(;;){ - /* Jump trailing comma. Note that the standard JX9 engine will not let you - * do this. - */ - while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){ - pDecoder->pIn++; - } - if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/ ){ - if( pDecoder->pIn < pDecoder->pEnd ){ - pDecoder->pIn++; /* Jump the trailing ']' */ - } - break; - } - /* Recurse and decode the entry */ - pDecoder->rec_count++; - rc = VmJsonDecode(pDecoder, 0); - pDecoder->rec_count--; - if( rc == SXERR_ABORT ){ - /* Abort processing immediately */ - return SXERR_ABORT; - } - /*The cursor is automatically advanced by the VmJsonDecode() function */ - if( (pDecoder->pIn < pDecoder->pEnd) && - ((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/|JSON_TK_COMMA/*','*/))==0) ){ - /* Unexpected token, abort immediatley */ - *pDecoder->pErr = SXERR_SYNTAX; - return SXERR_ABORT; - } - } - /* Restore the old consumer */ - pDecoder->xConsumer = xOld; - pDecoder->pUserData = pOld; - /* Invoke the old consumer on the decoded array */ - xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld); - }else if( pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) { - ProcJSONConsumer xOld; - jx9_value *pKey; - void *pOld; - /* Object representation*/ - pDecoder->pIn++; - /* Create a working array */ - pWorker = jx9_context_new_array(pDecoder->pCtx); - pKey = jx9_context_new_scalar(pDecoder->pCtx); - if( pWorker == 0 || pKey == 0){ - jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - /* Abort the decoding operation immediately */ - return SXERR_ABORT; - } - /* Save the old consumer */ - xOld = pDecoder->xConsumer; - pOld = pDecoder->pUserData; - /* Set the new consumer */ - pDecoder->xConsumer = VmJsonArrayDecoder; - pDecoder->pUserData = pWorker; - /* Decode the object */ - for(;;){ - /* Jump trailing comma. Note that the standard JX9 engine will not let you - * do this. - */ - while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){ - pDecoder->pIn++; - } - if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/ ){ - if( pDecoder->pIn < pDecoder->pEnd ){ - pDecoder->pIn++; /* Jump the trailing ']' */ - } - break; - } - if( (pDecoder->pIn->nType & (JSON_TK_ID|JSON_TK_STR)) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd - || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0){ - /* Syntax error, return immediately */ - *pDecoder->pErr = SXERR_SYNTAX; - return SXERR_ABORT; - } - if( pDecoder->pIn->nType & JSON_TK_ID ){ - SyString *pStr = &pDecoder->pIn->sData; - jx9_value_string(pKey, pStr->zString, (int)pStr->nByte); - }else{ - /* Dequote the key */ - VmJsonDequoteString(&pDecoder->pIn->sData, pKey); - } - /* Jump the key and the colon */ - pDecoder->pIn += 2; - /* Recurse and decode the value */ - pDecoder->rec_count++; - rc = VmJsonDecode(pDecoder, pKey); - pDecoder->rec_count--; - if( rc == SXERR_ABORT ){ - /* Abort processing immediately */ - return SXERR_ABORT; - } - /* Reset the internal buffer of the key */ - jx9_value_reset_string_cursor(pKey); - /*The cursor is automatically advanced by the VmJsonDecode() function */ - } - /* Restore the old consumer */ - pDecoder->xConsumer = xOld; - pDecoder->pUserData = pOld; - /* Invoke the old consumer on the decoded object*/ - xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld); - /* Release the key */ - jx9_context_release_value(pDecoder->pCtx, pKey); - }else{ - /* Unexpected token */ - return SXERR_ABORT; /* Abort immediately */ - } - /* Release the worker variable */ - jx9_context_release_value(pDecoder->pCtx, pWorker); - return SXRET_OK; -} -/* - * The following JSON decoder callback is invoked each time - * a JSON array representation [i.e: [15, "hello", FALSE] ] - * is being decoded. - */ -static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData) -{ - jx9_value *pArray = (jx9_value *)pUserData; - /* Insert the entry */ - jx9_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */ - SXUNUSED(pCtx); /* cc warning */ - /* All done */ - return SXRET_OK; -} -/* - * Standard JSON decoder callback. - */ -static int VmJsonDefaultDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData) -{ - /* Return the value directly */ - jx9_result_value(pCtx, pWorker); /* Will make it's own copy */ - SXUNUSED(pKey); /* cc warning */ - SXUNUSED(pUserData); - /* All done */ - return SXRET_OK; -} -/* - * Exported JSON decoding interface - */ -JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte) -{ - jx9_vm *pVm = pCtx->pVm; - json_decoder sDecoder; - SySet sToken; - SyLex sLex; - sxi32 rc; - /* Tokenize the input */ - SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken)); - rc = SXRET_OK; - SyLexInit(&sLex, &sToken, VmJsonTokenize, &rc); - SyLexTokenizeInput(&sLex,zJSON,(sxu32)nByte, 0, 0, 0); - if( rc != SXRET_OK ){ - /* Something goes wrong while tokenizing input. [i.e: Unexpected token] */ - SyLexRelease(&sLex); - SySetRelease(&sToken); - /* return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Fill the decoder */ - sDecoder.pCtx = pCtx; - sDecoder.pErr = &rc; - sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken); - sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)]; - sDecoder.iFlags = 0; - sDecoder.rec_count = 0; - /* Set a default consumer */ - sDecoder.xConsumer = VmJsonDefaultDecoder; - sDecoder.pUserData = 0; - /* Decode the raw JSON input */ - rc = VmJsonDecode(&sDecoder, 0); - if( rc == SXERR_ABORT ){ - /* - * Something goes wrong while decoding JSON input.Return NULL. - */ - jx9_result_null(pCtx); - } - /* Clean-up the mess left behind */ - SyLexRelease(&sLex); - SySetRelease(&sToken); - /* All done */ - return JX9_OK; -} -/* - * ---------------------------------------------------------- - * File: jx9_lex.c - * MD5: a79518c0635dbaf5dcfaca62efa2faf8 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: lex.c v1.0 FreeBSD 2012-12-09 00:19 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* This file implements a thread-safe and full reentrant lexical analyzer for the Jx9 programming language */ -/* Forward declarations */ -static sxu32 keywordCode(const char *z,int n); -static sxi32 LexExtractNowdoc(SyStream *pStream,SyToken *pToken); -/* - * Tokenize a raw jx9 input. - * Get a single low-level token from the input file. Update the stream pointer so that - * it points to the first character beyond the extracted token. - */ -static sxi32 jx9TokenizeInput(SyStream *pStream,SyToken *pToken,void *pUserData,void *pCtxData) -{ - SyString *pStr; - sxi32 rc; - /* Ignore leading white spaces */ - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){ - /* Advance the stream cursor */ - if( pStream->zText[0] == '\n' ){ - /* Update line counter */ - pStream->nLine++; - } - pStream->zText++; - } - if( pStream->zText >= pStream->zEnd ){ - /* End of input reached */ - return SXERR_EOF; - } - /* Record token starting position and line */ - pToken->nLine = pStream->nLine; - pToken->pUserData = 0; - pStr = &pToken->sData; - SyStringInitFromBuf(pStr, pStream->zText, 0); - if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){ - /* The following code fragment is taken verbatim from the xPP source tree. - * xPP is a modern embeddable macro processor with advanced features useful for - * application seeking for a production quality, ready to use macro processor. - * xPP is a widely used library developed and maintened by Symisc Systems. - * You can reach the xPP home page by following this link: - * http://xpp.symisc.net/ - */ - const unsigned char *zIn; - sxu32 nKeyword; - /* Isolate UTF-8 or alphanumeric stream */ - if( pStream->zText[0] < 0xc0 ){ - pStream->zText++; - } - for(;;){ - zIn = pStream->zText; - if( zIn[0] >= 0xc0 ){ - zIn++; - /* UTF-8 stream */ - while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){ - zIn++; - } - } - /* Skip alphanumeric stream */ - while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){ - zIn++; - } - if( zIn == pStream->zText ){ - /* Not an UTF-8 or alphanumeric stream */ - break; - } - /* Synchronize pointers */ - pStream->zText = zIn; - } - /* Record token length */ - pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); - nKeyword = keywordCode(pStr->zString, (int)pStr->nByte); - if( nKeyword != JX9_TK_ID ){ - /* We are dealing with a keyword [i.e: if, function, CREATE, ...], save the keyword ID */ - pToken->nType = JX9_TK_KEYWORD; - pToken->pUserData = SX_INT_TO_PTR(nKeyword); - }else{ - /* A simple identifier */ - pToken->nType = JX9_TK_ID; - } - }else{ - sxi32 c; - /* Non-alpha stream */ - if( pStream->zText[0] == '#' || - ( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '/') ){ - pStream->zText++; - /* Inline comments */ - while( pStream->zText < pStream->zEnd && pStream->zText[0] != '\n' ){ - pStream->zText++; - } - /* Tell the upper-layer to ignore this token */ - return SXERR_CONTINUE; - }else if( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '*' ){ - pStream->zText += 2; - /* Block comment */ - while( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '*' ){ - if( &pStream->zText[1] >= pStream->zEnd || pStream->zText[1] == '/' ){ - break; - } - } - if( pStream->zText[0] == '\n' ){ - pStream->nLine++; - } - pStream->zText++; - } - pStream->zText += 2; - /* Tell the upper-layer to ignore this token */ - return SXERR_CONTINUE; - }else if( SyisDigit(pStream->zText[0]) ){ - pStream->zText++; - /* Decimal digit stream */ - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ - pStream->zText++; - } - /* Mark the token as integer until we encounter a real number */ - pToken->nType = JX9_TK_INTEGER; - if( pStream->zText < pStream->zEnd ){ - c = pStream->zText[0]; - if( c == '.' ){ - /* Real number */ - pStream->zText++; - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ - pStream->zText++; - } - if( pStream->zText < pStream->zEnd ){ - c = pStream->zText[0]; - if( c=='e' || c=='E' ){ - pStream->zText++; - if( pStream->zText < pStream->zEnd ){ - c = pStream->zText[0]; - if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd && - pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){ - pStream->zText++; - } - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ - pStream->zText++; - } - } - } - } - pToken->nType = JX9_TK_REAL; - }else if( c=='e' || c=='E' ){ - SXUNUSED(pUserData); /* Prevent compiler warning */ - SXUNUSED(pCtxData); - pStream->zText++; - if( pStream->zText < pStream->zEnd ){ - c = pStream->zText[0]; - if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd && - pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){ - pStream->zText++; - } - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ - pStream->zText++; - } - } - pToken->nType = JX9_TK_REAL; - }else if( c == 'x' || c == 'X' ){ - /* Hex digit stream */ - pStream->zText++; - while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisHex(pStream->zText[0]) ){ - pStream->zText++; - } - }else if(c == 'b' || c == 'B' ){ - /* Binary digit stream */ - pStream->zText++; - while( pStream->zText < pStream->zEnd && (pStream->zText[0] == '0' || pStream->zText[0] == '1') ){ - pStream->zText++; - } - } - } - /* Record token length */ - pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); - return SXRET_OK; - } - c = pStream->zText[0]; - pStream->zText++; /* Advance the stream cursor */ - /* Assume we are dealing with an operator*/ - pToken->nType = JX9_TK_OP; - switch(c){ - case '$': pToken->nType = JX9_TK_DOLLAR; break; - case '{': pToken->nType = JX9_TK_OCB; break; - case '}': pToken->nType = JX9_TK_CCB; break; - case '(': pToken->nType = JX9_TK_LPAREN; break; - case '[': pToken->nType |= JX9_TK_OSB; break; /* Bitwise operation here, since the square bracket token '[' - * is a potential operator [i.e: subscripting] */ - case ']': pToken->nType = JX9_TK_CSB; break; - case ')': { - SySet *pTokSet = pStream->pSet; - /* Assemble type cast operators [i.e: (int), (float), (bool)...] */ - if( pTokSet->nUsed >= 2 ){ - SyToken *pTmp; - /* Peek the last recongnized token */ - pTmp = (SyToken *)SySetPeek(pTokSet); - if( pTmp->nType & JX9_TK_KEYWORD ){ - sxi32 nID = SX_PTR_TO_INT(pTmp->pUserData); - if( (sxu32)nID & (JX9_TKWRD_INT|JX9_TKWRD_FLOAT|JX9_TKWRD_STRING|JX9_TKWRD_BOOL) ){ - pTmp = (SyToken *)SySetAt(pTokSet, pTokSet->nUsed - 2); - if( pTmp->nType & JX9_TK_LPAREN ){ - /* Merge the three tokens '(' 'TYPE' ')' into a single one */ - const char * zTypeCast = "(int)"; - if( nID & JX9_TKWRD_FLOAT ){ - zTypeCast = "(float)"; - }else if( nID & JX9_TKWRD_BOOL ){ - zTypeCast = "(bool)"; - }else if( nID & JX9_TKWRD_STRING ){ - zTypeCast = "(string)"; - } - /* Reflect the change */ - pToken->nType = JX9_TK_OP; - SyStringInitFromBuf(&pToken->sData, zTypeCast, SyStrlen(zTypeCast)); - /* Save the instance associated with the type cast operator */ - pToken->pUserData = (void *)jx9ExprExtractOperator(&pToken->sData, 0); - /* Remove the two previous tokens */ - pTokSet->nUsed -= 2; - return SXRET_OK; - } - } - } - } - pToken->nType = JX9_TK_RPAREN; - break; - } - case '\'':{ - /* Single quoted string */ - pStr->zString++; - while( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '\'' ){ - if( pStream->zText[-1] != '\\' ){ - break; - }else{ - const unsigned char *zPtr = &pStream->zText[-2]; - sxi32 i = 1; - while( zPtr > pStream->zInput && zPtr[0] == '\\' ){ - zPtr--; - i++; - } - if((i&1)==0){ - break; - } - } - } - if( pStream->zText[0] == '\n' ){ - pStream->nLine++; - } - pStream->zText++; - } - /* Record token length and type */ - pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); - pToken->nType = JX9_TK_SSTR; - /* Jump the trailing single quote */ - pStream->zText++; - return SXRET_OK; - } - case '"':{ - sxi32 iNest; - /* Double quoted string */ - pStr->zString++; - while( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '{' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '$'){ - iNest = 1; - pStream->zText++; - /* TICKET 1433-40: Hnadle braces'{}' in double quoted string where everything is allowed */ - while(pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '{' ){ - iNest++; - }else if (pStream->zText[0] == '}' ){ - iNest--; - if( iNest <= 0 ){ - pStream->zText++; - break; - } - }else if( pStream->zText[0] == '\n' ){ - pStream->nLine++; - } - pStream->zText++; - } - if( pStream->zText >= pStream->zEnd ){ - break; - } - } - if( pStream->zText[0] == '"' ){ - if( pStream->zText[-1] != '\\' ){ - break; - }else{ - const unsigned char *zPtr = &pStream->zText[-2]; - sxi32 i = 1; - while( zPtr > pStream->zInput && zPtr[0] == '\\' ){ - zPtr--; - i++; - } - if((i&1)==0){ - break; - } - } - } - if( pStream->zText[0] == '\n' ){ - pStream->nLine++; - } - pStream->zText++; - } - /* Record token length and type */ - pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); - pToken->nType = JX9_TK_DSTR; - /* Jump the trailing quote */ - pStream->zText++; - return SXRET_OK; - } - case ':': - pToken->nType = JX9_TK_COLON; /* Single colon */ - break; - case ',': pToken->nType |= JX9_TK_COMMA; break; /* Comma is also an operator */ - case ';': pToken->nType = JX9_TK_SEMI; break; - /* Handle combined operators [i.e: +=, ===, !=== ...] */ - case '=': - pToken->nType |= JX9_TK_EQUAL; - if( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '=' ){ - pToken->nType &= ~JX9_TK_EQUAL; - /* Current operator: == */ - pStream->zText++; - if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ - /* Current operator: === */ - pStream->zText++; - } - } - } - break; - case '!': - if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ - /* Current operator: != */ - pStream->zText++; - if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ - /* Current operator: !== */ - pStream->zText++; - } - } - break; - case '&': - pToken->nType |= JX9_TK_AMPER; - if( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '&' ){ - pToken->nType &= ~JX9_TK_AMPER; - /* Current operator: && */ - pStream->zText++; - }else if( pStream->zText[0] == '=' ){ - pToken->nType &= ~JX9_TK_AMPER; - /* Current operator: &= */ - pStream->zText++; - } - } - case '.': - if( pStream->zText < pStream->zEnd && (pStream->zText[0] == '.' || pStream->zText[0] == '=') ){ - /* Concatenation operator: '..' or '.=' */ - pStream->zText++; - } - break; - case '|': - if( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '|' ){ - /* Current operator: || */ - pStream->zText++; - }else if( pStream->zText[0] == '=' ){ - /* Current operator: |= */ - pStream->zText++; - } - } - break; - case '+': - if( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '+' ){ - /* Current operator: ++ */ - pStream->zText++; - }else if( pStream->zText[0] == '=' ){ - /* Current operator: += */ - pStream->zText++; - } - } - break; - case '-': - if( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '-' ){ - /* Current operator: -- */ - pStream->zText++; - }else if( pStream->zText[0] == '=' ){ - /* Current operator: -= */ - pStream->zText++; - }else if( pStream->zText[0] == '>' ){ - /* Current operator: -> */ - pStream->zText++; - } - } - break; - case '*': - if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ - /* Current operator: *= */ - pStream->zText++; - } - break; - case '/': - if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ - /* Current operator: /= */ - pStream->zText++; - } - break; - case '%': - if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ - /* Current operator: %= */ - pStream->zText++; - } - break; - case '^': - if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ - /* Current operator: ^= */ - pStream->zText++; - } - break; - case '<': - if( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '<' ){ - /* Current operator: << */ - pStream->zText++; - if( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '=' ){ - /* Current operator: <<= */ - pStream->zText++; - }else if( pStream->zText[0] == '<' ){ - /* Current Token: <<< */ - pStream->zText++; - /* This may be the beginning of a Heredoc/Nowdoc string, try to delimit it */ - rc = LexExtractNowdoc(&(*pStream), &(*pToken)); - if( rc == SXRET_OK ){ - /* Here/Now doc successfuly extracted */ - return SXRET_OK; - } - } - } - }else if( pStream->zText[0] == '>' ){ - /* Current operator: <> */ - pStream->zText++; - }else if( pStream->zText[0] == '=' ){ - /* Current operator: <= */ - pStream->zText++; - } - } - break; - case '>': - if( pStream->zText < pStream->zEnd ){ - if( pStream->zText[0] == '>' ){ - /* Current operator: >> */ - pStream->zText++; - if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ - /* Current operator: >>= */ - pStream->zText++; - } - }else if( pStream->zText[0] == '=' ){ - /* Current operator: >= */ - pStream->zText++; - } - } - break; - default: - break; - } - if( pStr->nByte <= 0 ){ - /* Record token length */ - pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); - } - if( pToken->nType & JX9_TK_OP ){ - const jx9_expr_op *pOp; - /* Check if the extracted token is an operator */ - pOp = jx9ExprExtractOperator(pStr, (SyToken *)SySetPeek(pStream->pSet)); - if( pOp == 0 ){ - /* Not an operator */ - pToken->nType &= ~JX9_TK_OP; - if( pToken->nType <= 0 ){ - pToken->nType = JX9_TK_OTHER; - } - }else{ - /* Save the instance associated with this operator for later processing */ - pToken->pUserData = (void *)pOp; - } - } - } - /* Tell the upper-layer to save the extracted token for later processing */ - return SXRET_OK; -} -/***** This file contains automatically generated code ****** -** -** The code in this file has been automatically generated by -** -** $Header: /sqlite/sqlite/tool/mkkeywordhash.c,v 1.38 2011/12/21 01:00:46 $ -** -** The code in this file implements a function that determines whether -** or not a given identifier is really a JX9 keyword. The same thing -** might be implemented more directly using a hand-written hash table. -** But by using this automatically generated code, the size of the code -** is substantially reduced. This is important for embedded applications -** on platforms with limited memory. -*/ -/* Hash score: 35 */ -static sxu32 keywordCode(const char *z, int n) -{ - /* zText[] encodes 188 bytes of keywords in 128 bytes */ - /* printegereturnconstaticaselseifloatincludefaultDIEXITcontinue */ - /* diewhileASPRINTbooleanbreakforeachfunctionimportstringswitch */ - /* uplink */ - static const char zText[127] = { - 'p','r','i','n','t','e','g','e','r','e','t','u','r','n','c','o','n','s', - 't','a','t','i','c','a','s','e','l','s','e','i','f','l','o','a','t','i', - 'n','c','l','u','d','e','f','a','u','l','t','D','I','E','X','I','T','c', - 'o','n','t','i','n','u','e','d','i','e','w','h','i','l','e','A','S','P', - 'R','I','N','T','b','o','o','l','e','a','n','b','r','e','a','k','f','o', - 'r','e','a','c','h','f','u','n','c','t','i','o','n','i','m','p','o','r', - 't','s','t','r','i','n','g','s','w','i','t','c','h','u','p','l','i','n', - 'k', - }; - static const unsigned char aHash[59] = { - 0, 0, 0, 0, 15, 0, 30, 0, 0, 2, 19, 18, 0, - 0, 10, 3, 12, 0, 28, 29, 23, 0, 13, 22, 0, 0, - 14, 24, 25, 31, 11, 0, 0, 0, 0, 1, 5, 0, 0, - 20, 0, 27, 9, 0, 0, 0, 8, 0, 0, 26, 6, 0, - 0, 17, 0, 0, 0, 0, 0, - }; - static const unsigned char aNext[31] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 21, 7, - 0, 0, 0, 0, 0, - }; - static const unsigned char aLen[31] = { - 5, 7, 3, 6, 5, 6, 4, 2, 6, 4, 2, 5, 7, - 7, 3, 4, 8, 3, 5, 2, 5, 4, 7, 5, 3, 7, - 8, 6, 6, 6, 6, - }; - static const sxu16 aOffset[31] = { - 0, 2, 2, 8, 14, 17, 22, 23, 25, 25, 29, 30, 35, - 40, 47, 49, 53, 61, 64, 69, 71, 76, 76, 83, 88, 88, - 95, 103, 109, 115, 121, - }; - static const sxu32 aCode[31] = { - JX9_TKWRD_PRINT, JX9_TKWRD_INT, JX9_TKWRD_INT, JX9_TKWRD_RETURN, JX9_TKWRD_CONST, - JX9_TKWRD_STATIC, JX9_TKWRD_CASE, JX9_TKWRD_AS, JX9_TKWRD_ELIF, JX9_TKWRD_ELSE, - JX9_TKWRD_IF, JX9_TKWRD_FLOAT, JX9_TKWRD_INCLUDE, JX9_TKWRD_DEFAULT, JX9_TKWRD_DIE, - JX9_TKWRD_EXIT, JX9_TKWRD_CONTINUE, JX9_TKWRD_DIE, JX9_TKWRD_WHILE, JX9_TKWRD_AS, - JX9_TKWRD_PRINT, JX9_TKWRD_BOOL, JX9_TKWRD_BOOL, JX9_TKWRD_BREAK, JX9_TKWRD_FOR, - JX9_TKWRD_FOREACH, JX9_TKWRD_FUNCTION, JX9_TKWRD_IMPORT, JX9_TKWRD_STRING, JX9_TKWRD_SWITCH, - JX9_TKWRD_UPLINK, - }; - int h, i; - if( n<2 ) return JX9_TK_ID; - h = (((int)z[0]*4) ^ ((int)z[n-1]*3) ^ n) % 59; - for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){ - if( (int)aLen[i]==n && SyMemcmp(&zText[aOffset[i]],z,n)==0 ){ - /* JX9_TKWRD_PRINT */ - /* JX9_TKWRD_INT */ - /* JX9_TKWRD_INT */ - /* JX9_TKWRD_RETURN */ - /* JX9_TKWRD_CONST */ - /* JX9_TKWRD_STATIC */ - /* JX9_TKWRD_CASE */ - /* JX9_TKWRD_AS */ - /* JX9_TKWRD_ELIF */ - /* JX9_TKWRD_ELSE */ - /* JX9_TKWRD_IF */ - /* JX9_TKWRD_FLOAT */ - /* JX9_TKWRD_INCLUDE */ - /* JX9_TKWRD_DEFAULT */ - /* JX9_TKWRD_DIE */ - /* JX9_TKWRD_EXIT */ - /* JX9_TKWRD_CONTINUE */ - /* JX9_TKWRD_DIE */ - /* JX9_TKWRD_WHILE */ - /* JX9_TKWRD_AS */ - /* JX9_TKWRD_PRINT */ - /* JX9_TKWRD_BOOL */ - /* JX9_TKWRD_BOOL */ - /* JX9_TKWRD_BREAK */ - /* JX9_TKWRD_FOR */ - /* JX9_TKWRD_FOREACH */ - /* JX9_TKWRD_FUNCTION */ - /* JX9_TKWRD_IMPORT */ - /* JX9_TKWRD_STRING */ - /* JX9_TKWRD_SWITCH */ - /* JX9_TKWRD_UPLINK */ - return aCode[i]; - } - } - return JX9_TK_ID; -} -/* - * Extract a heredoc/nowdoc text from a raw JX9 input. - * According to the JX9 language reference manual: - * A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier - * is provided, then a newline. The string itself follows, and then the same identifier again - * to close the quotation. - * The closing identifier must begin in the first column of the line. Also, the identifier must - * follow the same naming rules as any other label in JX9: it must contain only alphanumeric - * characters and underscores, and must start with a non-digit character or underscore. - * Heredoc text behaves just like a double-quoted string, without the double quotes. - * This means that quotes in a heredoc do not need to be escaped, but the escape codes listed - * above can still be used. Variables are expanded, but the same care must be taken when expressing - * complex variables inside a heredoc as with strings. - * Nowdocs are to single-quoted strings what heredocs are to double-quoted strings. - * A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc. - * The construct is ideal for embedding JX9 code or other large blocks of text without the need - * for escaping. It shares some features in common with the SGML construct, in that - * it declares a block of text which is not for parsing. - * A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier which follows - * is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc identifiers also apply to nowdoc - * identifiers, especially those regarding the appearance of the closing identifier. - */ -static sxi32 LexExtractNowdoc(SyStream *pStream, SyToken *pToken) -{ - const unsigned char *zIn = pStream->zText; - const unsigned char *zEnd = pStream->zEnd; - const unsigned char *zPtr; - SyString sDelim; - SyString sStr; - /* Jump leading white spaces */ - while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){ - zIn++; - } - if( zIn >= zEnd ){ - /* A simple symbol, return immediately */ - return SXERR_CONTINUE; - } - if( zIn[0] == '\'' || zIn[0] == '"' ){ - zIn++; - } - if( zIn[0] < 0xc0 && !SyisAlphaNum(zIn[0]) && zIn[0] != '_' ){ - /* Invalid delimiter, return immediately */ - return SXERR_CONTINUE; - } - /* Isolate the identifier */ - sDelim.zString = (const char *)zIn; - for(;;){ - zPtr = zIn; - /* Skip alphanumeric stream */ - while( zPtr < zEnd && zPtr[0] < 0xc0 && (SyisAlphaNum(zPtr[0]) || zPtr[0] == '_') ){ - zPtr++; - } - if( zPtr < zEnd && zPtr[0] >= 0xc0 ){ - zPtr++; - /* UTF-8 stream */ - while( zPtr < zEnd && ((zPtr[0] & 0xc0) == 0x80) ){ - zPtr++; - } - } - if( zPtr == zIn ){ - /* Not an UTF-8 or alphanumeric stream */ - break; - } - /* Synchronize pointers */ - zIn = zPtr; - } - /* Get the identifier length */ - sDelim.nByte = (sxu32)((const char *)zIn-sDelim.zString); - if( zIn[0] == '"' || zIn[0] == '\'' ){ - /* Jump the trailing single quote */ - zIn++; - } - /* Jump trailing white spaces */ - while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){ - zIn++; - } - if( sDelim.nByte <= 0 || zIn >= zEnd || zIn[0] != '\n' ){ - /* Invalid syntax */ - return SXERR_CONTINUE; - } - pStream->nLine++; /* Increment line counter */ - zIn++; - /* Isolate the delimited string */ - sStr.zString = (const char *)zIn; - /* Go and found the closing delimiter */ - for(;;){ - /* Synchronize with the next line */ - while( zIn < zEnd && zIn[0] != '\n' ){ - zIn++; - } - if( zIn >= zEnd ){ - /* End of the input reached, break immediately */ - pStream->zText = pStream->zEnd; - break; - } - pStream->nLine++; /* Increment line counter */ - zIn++; - if( (sxu32)(zEnd - zIn) >= sDelim.nByte && SyMemcmp((const void *)sDelim.zString, (const void *)zIn, sDelim.nByte) == 0 ){ - zPtr = &zIn[sDelim.nByte]; - while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){ - zPtr++; - } - if( zPtr >= zEnd ){ - /* End of input */ - pStream->zText = zPtr; - break; - } - if( zPtr[0] == ';' ){ - const unsigned char *zCur = zPtr; - zPtr++; - while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){ - zPtr++; - } - if( zPtr >= zEnd || zPtr[0] == '\n' ){ - /* Closing delimiter found, break immediately */ - pStream->zText = zCur; /* Keep the semi-colon */ - break; - } - }else if( zPtr[0] == '\n' ){ - /* Closing delimiter found, break immediately */ - pStream->zText = zPtr; /* Synchronize with the stream cursor */ - break; - } - /* Synchronize pointers and continue searching */ - zIn = zPtr; - } - } /* For(;;) */ - /* Get the delimited string length */ - sStr.nByte = (sxu32)((const char *)zIn-sStr.zString); - /* Record token type and length */ - pToken->nType = JX9_TK_NOWDOC; - SyStringDupPtr(&pToken->sData, &sStr); - /* Remove trailing white spaces */ - SyStringRightTrim(&pToken->sData); - /* All done */ - return SXRET_OK; -} -/* - * Tokenize a raw jx9 input. - * This is the public tokenizer called by most code generator routines. - */ -JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput,sxu32 nLen,SySet *pOut) -{ - SyLex sLexer; - sxi32 rc; - /* Initialize the lexer */ - rc = SyLexInit(&sLexer, &(*pOut),jx9TokenizeInput,0); - if( rc != SXRET_OK ){ - return rc; - } - /* Tokenize input */ - rc = SyLexTokenizeInput(&sLexer, zInput, nLen, 0, 0, 0); - /* Release the lexer */ - SyLexRelease(&sLexer); - /* Tokenization result */ - return rc; -} - -/* - * ---------------------------------------------------------- - * File: jx9_lib.c - * MD5: a684fb6677b1ab0110d03536f1280c50 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: lib.c v5.1 Win7 2012-08-08 04:19 stable $ */ -/* - * Symisc Run-Time API: A modern thread safe replacement of the standard libc - * Copyright (C) Symisc Systems 2007-2012, http://www.symisc.net/ - * - * The Symisc Run-Time API is an independent project developed by symisc systems - * internally as a secure replacement of the standard libc. - * The library is re-entrant, thread-safe and platform independent. - */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -#if defined(__WINNT__) -#include -#else -#include -#endif -#if defined(JX9_ENABLE_THREADS) -/* SyRunTimeApi: sxmutex.c */ -#if defined(__WINNT__) -struct SyMutex -{ - CRITICAL_SECTION sMutex; - sxu32 nType; /* Mutex type, one of SXMUTEX_TYPE_* */ -}; -/* Preallocated static mutex */ -static SyMutex aStaticMutexes[] = { - {{0}, SXMUTEX_TYPE_STATIC_1}, - {{0}, SXMUTEX_TYPE_STATIC_2}, - {{0}, SXMUTEX_TYPE_STATIC_3}, - {{0}, SXMUTEX_TYPE_STATIC_4}, - {{0}, SXMUTEX_TYPE_STATIC_5}, - {{0}, SXMUTEX_TYPE_STATIC_6} -}; -static BOOL winMutexInit = FALSE; -static LONG winMutexLock = 0; - -static sxi32 WinMutexGlobaInit(void) -{ - LONG rc; - rc = InterlockedCompareExchange(&winMutexLock, 1, 0); - if ( rc == 0 ){ - sxu32 n; - for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){ - InitializeCriticalSection(&aStaticMutexes[n].sMutex); - } - winMutexInit = TRUE; - }else{ - /* Someone else is doing this for us */ - while( winMutexInit == FALSE ){ - Sleep(1); - } - } - return SXRET_OK; -} -static void WinMutexGlobalRelease(void) -{ - LONG rc; - rc = InterlockedCompareExchange(&winMutexLock, 0, 1); - if( rc == 1 ){ - /* The first to decrement to zero does the actual global release */ - if( winMutexInit == TRUE ){ - sxu32 n; - for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){ - DeleteCriticalSection(&aStaticMutexes[n].sMutex); - } - winMutexInit = FALSE; - } - } -} -static SyMutex * WinMutexNew(int nType) -{ - SyMutex *pMutex = 0; - if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){ - /* Allocate a new mutex */ - pMutex = (SyMutex *)HeapAlloc(GetProcessHeap(), 0, sizeof(SyMutex)); - if( pMutex == 0 ){ - return 0; - } - InitializeCriticalSection(&pMutex->sMutex); - }else{ - /* Use a pre-allocated static mutex */ - if( nType > SXMUTEX_TYPE_STATIC_6 ){ - nType = SXMUTEX_TYPE_STATIC_6; - } - pMutex = &aStaticMutexes[nType - 3]; - } - pMutex->nType = nType; - return pMutex; -} -static void WinMutexRelease(SyMutex *pMutex) -{ - if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){ - DeleteCriticalSection(&pMutex->sMutex); - HeapFree(GetProcessHeap(), 0, pMutex); - } -} -static void WinMutexEnter(SyMutex *pMutex) -{ - EnterCriticalSection(&pMutex->sMutex); -} -static sxi32 WinMutexTryEnter(SyMutex *pMutex) -{ -#ifdef _WIN32_WINNT - BOOL rc; - /* Only WindowsNT platforms */ - rc = TryEnterCriticalSection(&pMutex->sMutex); - if( rc ){ - return SXRET_OK; - }else{ - return SXERR_BUSY; - } -#else - return SXERR_NOTIMPLEMENTED; -#endif -} -static void WinMutexLeave(SyMutex *pMutex) -{ - LeaveCriticalSection(&pMutex->sMutex); -} -/* Export Windows mutex interfaces */ -static const SyMutexMethods sWinMutexMethods = { - WinMutexGlobaInit, /* xGlobalInit() */ - WinMutexGlobalRelease, /* xGlobalRelease() */ - WinMutexNew, /* xNew() */ - WinMutexRelease, /* xRelease() */ - WinMutexEnter, /* xEnter() */ - WinMutexTryEnter, /* xTryEnter() */ - WinMutexLeave /* xLeave() */ -}; -JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void) -{ - return &sWinMutexMethods; -} -#elif defined(__UNIXES__) -#include -struct SyMutex -{ - pthread_mutex_t sMutex; - sxu32 nType; -}; -static SyMutex * UnixMutexNew(int nType) -{ - static SyMutex aStaticMutexes[] = { - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_1}, - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_2}, - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_3}, - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_4}, - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_5}, - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_6} - }; - SyMutex *pMutex; - - if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){ - pthread_mutexattr_t sRecursiveAttr; - /* Allocate a new mutex */ - pMutex = (SyMutex *)malloc(sizeof(SyMutex)); - if( pMutex == 0 ){ - return 0; - } - if( nType == SXMUTEX_TYPE_RECURSIVE ){ - pthread_mutexattr_init(&sRecursiveAttr); - pthread_mutexattr_settype(&sRecursiveAttr, PTHREAD_MUTEX_RECURSIVE); - } - pthread_mutex_init(&pMutex->sMutex, nType == SXMUTEX_TYPE_RECURSIVE ? &sRecursiveAttr : 0 ); - if( nType == SXMUTEX_TYPE_RECURSIVE ){ - pthread_mutexattr_destroy(&sRecursiveAttr); - } - }else{ - /* Use a pre-allocated static mutex */ - if( nType > SXMUTEX_TYPE_STATIC_6 ){ - nType = SXMUTEX_TYPE_STATIC_6; - } - pMutex = &aStaticMutexes[nType - 3]; - } - pMutex->nType = nType; - - return pMutex; -} -static void UnixMutexRelease(SyMutex *pMutex) -{ - if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){ - pthread_mutex_destroy(&pMutex->sMutex); - free(pMutex); - } -} -static void UnixMutexEnter(SyMutex *pMutex) -{ - pthread_mutex_lock(&pMutex->sMutex); -} -static void UnixMutexLeave(SyMutex *pMutex) -{ - pthread_mutex_unlock(&pMutex->sMutex); -} -/* Export pthread mutex interfaces */ -static const SyMutexMethods sPthreadMutexMethods = { - 0, /* xGlobalInit() */ - 0, /* xGlobalRelease() */ - UnixMutexNew, /* xNew() */ - UnixMutexRelease, /* xRelease() */ - UnixMutexEnter, /* xEnter() */ - 0, /* xTryEnter() */ - UnixMutexLeave /* xLeave() */ -}; -JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void) -{ - return &sPthreadMutexMethods; -} -#else -/* Host application must register their own mutex subsystem if the target - * platform is not an UNIX-like or windows systems. - */ -struct SyMutex -{ - sxu32 nType; -}; -static SyMutex * DummyMutexNew(int nType) -{ - static SyMutex sMutex; - SXUNUSED(nType); - return &sMutex; -} -static void DummyMutexRelease(SyMutex *pMutex) -{ - SXUNUSED(pMutex); -} -static void DummyMutexEnter(SyMutex *pMutex) -{ - SXUNUSED(pMutex); -} -static void DummyMutexLeave(SyMutex *pMutex) -{ - SXUNUSED(pMutex); -} -/* Export the dummy mutex interfaces */ -static const SyMutexMethods sDummyMutexMethods = { - 0, /* xGlobalInit() */ - 0, /* xGlobalRelease() */ - DummyMutexNew, /* xNew() */ - DummyMutexRelease, /* xRelease() */ - DummyMutexEnter, /* xEnter() */ - 0, /* xTryEnter() */ - DummyMutexLeave /* xLeave() */ -}; -JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void) -{ - return &sDummyMutexMethods; -} -#endif /* __WINNT__ */ -#endif /* JX9_ENABLE_THREADS */ -static void * SyOSHeapAlloc(sxu32 nByte) -{ - void *pNew; -#if defined(__WINNT__) - pNew = HeapAlloc(GetProcessHeap(), 0, nByte); -#else - pNew = malloc((size_t)nByte); -#endif - return pNew; -} -static void * SyOSHeapRealloc(void *pOld, sxu32 nByte) -{ - void *pNew; -#if defined(__WINNT__) - pNew = HeapReAlloc(GetProcessHeap(), 0, pOld, nByte); -#else - pNew = realloc(pOld, (size_t)nByte); -#endif - return pNew; -} -static void SyOSHeapFree(void *pPtr) -{ -#if defined(__WINNT__) - HeapFree(GetProcessHeap(), 0, pPtr); -#else - free(pPtr); -#endif -} -/* SyRunTimeApi:sxstr.c */ -JX9_PRIVATE sxu32 SyStrlen(const char *zSrc) -{ - register const char *zIn = zSrc; -#if defined(UNTRUST) - if( zIn == 0 ){ - return 0; - } -#endif - for(;;){ - if( !zIn[0] ){ break; } zIn++; - if( !zIn[0] ){ break; } zIn++; - if( !zIn[0] ){ break; } zIn++; - if( !zIn[0] ){ break; } zIn++; - } - return (sxu32)(zIn - zSrc); -} -JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos) -{ - const char *zIn = zStr; - const char *zEnd; - - zEnd = &zIn[nLen]; - for(;;){ - if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++; - if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++; - if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++; - if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++; - } - return SXERR_NOTFOUND; -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos) -{ - const char *zIn = zStr; - const char *zEnd; - - zEnd = &zIn[nLen - 1]; - for( ;; ){ - if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--; - if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--; - if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--; - if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--; - } - return SXERR_NOTFOUND; -} -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos) -{ - const char *zIn = zSrc; - const char *zPtr; - const char *zEnd; - sxi32 c; - zEnd = &zSrc[nLen]; - for(;;){ - if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++; - if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++; - if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++; - if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++; - } - return SXERR_NOTFOUND; -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen) -{ - const unsigned char *zP = (const unsigned char *)zLeft; - const unsigned char *zQ = (const unsigned char *)zRight; - - if( SX_EMPTY_STR(zP) || SX_EMPTY_STR(zQ) ){ - return SX_EMPTY_STR(zP) ? (SX_EMPTY_STR(zQ) ? 0 : -1) :1; - } - if( nLen <= 0 ){ - return 0; - } - for(;;){ - if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--; - if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--; - if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--; - if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--; - } - return (sxi32)(zP[0] - zQ[0]); -} -#endif -JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen) -{ - register unsigned char *p = (unsigned char *)zLeft; - register unsigned char *q = (unsigned char *)zRight; - - if( SX_EMPTY_STR(p) || SX_EMPTY_STR(q) ){ - return SX_EMPTY_STR(p)? SX_EMPTY_STR(q) ? 0 : -1 :1; - } - for(;;){ - if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; - if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; - if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; - if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; - - } - return (sxi32)(SyCharToLower(p[0]) - SyCharToLower(q[0])); -} -JX9_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen) -{ - unsigned char *zBuf = (unsigned char *)zDest; - unsigned char *zIn = (unsigned char *)zSrc; - unsigned char *zEnd; -#if defined(UNTRUST) - if( zSrc == (const char *)zDest ){ - return 0; - } -#endif - if( nLen <= 0 ){ - nLen = SyStrlen(zSrc); - } - zEnd = &zBuf[nDestLen - 1]; /* reserve a room for the null terminator */ - for(;;){ - if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--; - if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--; - if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--; - if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--; - } - zBuf[0] = 0; - return (sxu32)(zBuf-(unsigned char *)zDest); -} -/* SyRunTimeApi:sxmem.c */ -JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize) -{ - register unsigned char *zSrc = (unsigned char *)pSrc; - unsigned char *zEnd; -#if defined(UNTRUST) - if( zSrc == 0 || nSize <= 0 ){ - return ; - } -#endif - zEnd = &zSrc[nSize]; - for(;;){ - if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++; - if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++; - if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++; - if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++; - } -} -JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize) -{ - sxi32 rc; - if( nSize <= 0 ){ - return 0; - } - if( pB1 == 0 || pB2 == 0 ){ - return pB1 != 0 ? 1 : (pB2 == 0 ? 0 : -1); - } - SX_MACRO_FAST_CMP(pB1, pB2, nSize, rc); - return rc; -} -JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen) -{ - if( pSrc == 0 || pDest == 0 ){ - return 0; - } - if( pSrc == (const void *)pDest ){ - return nLen; - } - SX_MACRO_FAST_MEMCPY(pSrc, pDest, nLen); - return nLen; -} -static void * MemOSAlloc(sxu32 nBytes) -{ - sxu32 *pChunk; - pChunk = (sxu32 *)SyOSHeapAlloc(nBytes + sizeof(sxu32)); - if( pChunk == 0 ){ - return 0; - } - pChunk[0] = nBytes; - return (void *)&pChunk[1]; -} -static void * MemOSRealloc(void *pOld, sxu32 nBytes) -{ - sxu32 *pOldChunk; - sxu32 *pChunk; - pOldChunk = (sxu32 *)(((char *)pOld)-sizeof(sxu32)); - if( pOldChunk[0] >= nBytes ){ - return pOld; - } - pChunk = (sxu32 *)SyOSHeapRealloc(pOldChunk, nBytes + sizeof(sxu32)); - if( pChunk == 0 ){ - return 0; - } - pChunk[0] = nBytes; - return (void *)&pChunk[1]; -} -static void MemOSFree(void *pBlock) -{ - void *pChunk; - pChunk = (void *)(((char *)pBlock)-sizeof(sxu32)); - SyOSHeapFree(pChunk); -} -static sxu32 MemOSChunkSize(void *pBlock) -{ - sxu32 *pChunk; - pChunk = (sxu32 *)(((char *)pBlock)-sizeof(sxu32)); - return pChunk[0]; -} -/* Export OS allocation methods */ -static const SyMemMethods sOSAllocMethods = { - MemOSAlloc, - MemOSRealloc, - MemOSFree, - MemOSChunkSize, - 0, - 0, - 0 -}; -static void * MemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte) -{ - SyMemBlock *pBlock; - sxi32 nRetry = 0; - - /* Append an extra block so we can tracks allocated chunks and avoid memory - * leaks. - */ - nByte += sizeof(SyMemBlock); - for(;;){ - pBlock = (SyMemBlock *)pBackend->pMethods->xAlloc(nByte); - if( pBlock != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY - || SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){ - break; - } - nRetry++; - } - if( pBlock == 0 ){ - return 0; - } - pBlock->pNext = pBlock->pPrev = 0; - /* Link to the list of already tracked blocks */ - MACRO_LD_PUSH(pBackend->pBlocks, pBlock); -#if defined(UNTRUST) - pBlock->nGuard = SXMEM_BACKEND_MAGIC; -#endif - pBackend->nBlock++; - return (void *)&pBlock[1]; -} -JX9_PRIVATE void * SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte) -{ - void *pChunk; -#if defined(UNTRUST) - if( SXMEM_BACKEND_CORRUPT(pBackend) ){ - return 0; - } -#endif - if( pBackend->pMutexMethods ){ - SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex); - } - pChunk = MemBackendAlloc(&(*pBackend), nByte); - if( pBackend->pMutexMethods ){ - SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex); - } - return pChunk; -} -static void * MemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte) -{ - SyMemBlock *pBlock, *pNew, *pPrev, *pNext; - sxu32 nRetry = 0; - - if( pOld == 0 ){ - return MemBackendAlloc(&(*pBackend), nByte); - } - pBlock = (SyMemBlock *)(((char *)pOld) - sizeof(SyMemBlock)); -#if defined(UNTRUST) - if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){ - return 0; - } -#endif - nByte += sizeof(SyMemBlock); - pPrev = pBlock->pPrev; - pNext = pBlock->pNext; - for(;;){ - pNew = (SyMemBlock *)pBackend->pMethods->xRealloc(pBlock, nByte); - if( pNew != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY || - SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){ - break; - } - nRetry++; - } - if( pNew == 0 ){ - return 0; - } - if( pNew != pBlock ){ - if( pPrev == 0 ){ - pBackend->pBlocks = pNew; - }else{ - pPrev->pNext = pNew; - } - if( pNext ){ - pNext->pPrev = pNew; - } -#if defined(UNTRUST) - pNew->nGuard = SXMEM_BACKEND_MAGIC; -#endif - } - return (void *)&pNew[1]; -} -JX9_PRIVATE void * SyMemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte) -{ - void *pChunk; -#if defined(UNTRUST) - if( SXMEM_BACKEND_CORRUPT(pBackend) ){ - return 0; - } -#endif - if( pBackend->pMutexMethods ){ - SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex); - } - pChunk = MemBackendRealloc(&(*pBackend), pOld, nByte); - if( pBackend->pMutexMethods ){ - SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex); - } - return pChunk; -} -static sxi32 MemBackendFree(SyMemBackend *pBackend, void * pChunk) -{ - SyMemBlock *pBlock; - pBlock = (SyMemBlock *)(((char *)pChunk) - sizeof(SyMemBlock)); -#if defined(UNTRUST) - if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){ - return SXERR_CORRUPT; - } -#endif - /* Unlink from the list of active blocks */ - if( pBackend->nBlock > 0 ){ - /* Release the block */ -#if defined(UNTRUST) - /* Mark as stale block */ - pBlock->nGuard = 0x635B; -#endif - MACRO_LD_REMOVE(pBackend->pBlocks, pBlock); - pBackend->nBlock--; - pBackend->pMethods->xFree(pBlock); - } - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void * pChunk) -{ - sxi32 rc; -#if defined(UNTRUST) - if( SXMEM_BACKEND_CORRUPT(pBackend) ){ - return SXERR_CORRUPT; - } -#endif - if( pChunk == 0 ){ - return SXRET_OK; - } - if( pBackend->pMutexMethods ){ - SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex); - } - rc = MemBackendFree(&(*pBackend), pChunk); - if( pBackend->pMutexMethods ){ - SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex); - } - return rc; -} -#if defined(JX9_ENABLE_THREADS) -JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods) -{ - SyMutex *pMutex; -#if defined(UNTRUST) - if( SXMEM_BACKEND_CORRUPT(pBackend) || pMethods == 0 || pMethods->xNew == 0){ - return SXERR_CORRUPT; - } -#endif - pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST); - if( pMutex == 0 ){ - return SXERR_OS; - } - /* Attach the mutex to the memory backend */ - pBackend->pMutex = pMutex; - pBackend->pMutexMethods = pMethods; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend) -{ -#if defined(UNTRUST) - if( SXMEM_BACKEND_CORRUPT(pBackend) ){ - return SXERR_CORRUPT; - } -#endif - if( pBackend->pMutex == 0 ){ - /* There is no mutex subsystem at all */ - return SXRET_OK; - } - SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex); - pBackend->pMutexMethods = 0; - pBackend->pMutex = 0; - return SXRET_OK; -} -#endif -/* - * Memory pool allocator - */ -#define SXMEM_POOL_MAGIC 0xDEAD -#define SXMEM_POOL_MAXALLOC (1<<(SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR)) -#define SXMEM_POOL_MINALLOC (1<<(SXMEM_POOL_INCR)) -static sxi32 MemPoolBucketAlloc(SyMemBackend *pBackend, sxu32 nBucket) -{ - char *zBucket, *zBucketEnd; - SyMemHeader *pHeader; - sxu32 nBucketSize; - - /* Allocate one big block first */ - zBucket = (char *)MemBackendAlloc(&(*pBackend), SXMEM_POOL_MAXALLOC); - if( zBucket == 0 ){ - return SXERR_MEM; - } - zBucketEnd = &zBucket[SXMEM_POOL_MAXALLOC]; - /* Divide the big block into mini bucket pool */ - nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR); - pBackend->apPool[nBucket] = pHeader = (SyMemHeader *)zBucket; - for(;;){ - if( &zBucket[nBucketSize] >= zBucketEnd ){ - break; - } - pHeader->pNext = (SyMemHeader *)&zBucket[nBucketSize]; - /* Advance the cursor to the next available chunk */ - pHeader = pHeader->pNext; - zBucket += nBucketSize; - } - pHeader->pNext = 0; - - return SXRET_OK; -} -static void * MemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte) -{ - SyMemHeader *pBucket, *pNext; - sxu32 nBucketSize; - sxu32 nBucket; - - if( nByte + sizeof(SyMemHeader) >= SXMEM_POOL_MAXALLOC ){ - /* Allocate a big chunk directly */ - pBucket = (SyMemHeader *)MemBackendAlloc(&(*pBackend), nByte+sizeof(SyMemHeader)); - if( pBucket == 0 ){ - return 0; - } - /* Record as big block */ - pBucket->nBucket = (sxu32)(SXMEM_POOL_MAGIC << 16) | SXU16_HIGH; - return (void *)(pBucket+1); - } - /* Locate the appropriate bucket */ - nBucket = 0; - nBucketSize = SXMEM_POOL_MINALLOC; - while( nByte + sizeof(SyMemHeader) > nBucketSize ){ - nBucketSize <<= 1; - nBucket++; - } - pBucket = pBackend->apPool[nBucket]; - if( pBucket == 0 ){ - sxi32 rc; - rc = MemPoolBucketAlloc(&(*pBackend), nBucket); - if( rc != SXRET_OK ){ - return 0; - } - pBucket = pBackend->apPool[nBucket]; - } - /* Remove from the free list */ - pNext = pBucket->pNext; - pBackend->apPool[nBucket] = pNext; - /* Record bucket&magic number */ - pBucket->nBucket = (SXMEM_POOL_MAGIC << 16) | nBucket; - return (void *)&pBucket[1]; -} -JX9_PRIVATE void * SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte) -{ - void *pChunk; -#if defined(UNTRUST) - if( SXMEM_BACKEND_CORRUPT(pBackend) ){ - return 0; - } -#endif - if( pBackend->pMutexMethods ){ - SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex); - } - pChunk = MemBackendPoolAlloc(&(*pBackend), nByte); - if( pBackend->pMutexMethods ){ - SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex); - } - return pChunk; -} -static sxi32 MemBackendPoolFree(SyMemBackend *pBackend, void * pChunk) -{ - SyMemHeader *pHeader; - sxu32 nBucket; - /* Get the corresponding bucket */ - pHeader = (SyMemHeader *)(((char *)pChunk) - sizeof(SyMemHeader)); - /* Sanity check to avoid misuse */ - if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){ - return SXERR_CORRUPT; - } - nBucket = pHeader->nBucket & 0xFFFF; - if( nBucket == SXU16_HIGH ){ - /* Free the big block */ - MemBackendFree(&(*pBackend), pHeader); - }else{ - /* Return to the free list */ - pHeader->pNext = pBackend->apPool[nBucket & 0x0f]; - pBackend->apPool[nBucket & 0x0f] = pHeader; - } - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void * pChunk) -{ - sxi32 rc; -#if defined(UNTRUST) - if( SXMEM_BACKEND_CORRUPT(pBackend) || pChunk == 0 ){ - return SXERR_CORRUPT; - } -#endif - if( pBackend->pMutexMethods ){ - SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex); - } - rc = MemBackendPoolFree(&(*pBackend), pChunk); - if( pBackend->pMutexMethods ){ - SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex); - } - return rc; -} -#if 0 -static void * MemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte) -{ - sxu32 nBucket, nBucketSize; - SyMemHeader *pHeader; - void * pNew; - - if( pOld == 0 ){ - /* Allocate a new pool */ - pNew = MemBackendPoolAlloc(&(*pBackend), nByte); - return pNew; - } - /* Get the corresponding bucket */ - pHeader = (SyMemHeader *)(((char *)pOld) - sizeof(SyMemHeader)); - /* Sanity check to avoid misuse */ - if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){ - return 0; - } - nBucket = pHeader->nBucket & 0xFFFF; - if( nBucket == SXU16_HIGH ){ - /* Big block */ - return MemBackendRealloc(&(*pBackend), pHeader, nByte); - } - nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR); - if( nBucketSize >= nByte + sizeof(SyMemHeader) ){ - /* The old bucket can honor the requested size */ - return pOld; - } - /* Allocate a new pool */ - pNew = MemBackendPoolAlloc(&(*pBackend), nByte); - if( pNew == 0 ){ - return 0; - } - /* Copy the old data into the new block */ - SyMemcpy(pOld, pNew, nBucketSize); - /* Free the stale block */ - MemBackendPoolFree(&(*pBackend), pOld); - return pNew; -} -JX9_PRIVATE void * SyMemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte) -{ - void *pChunk; -#if defined(UNTRUST) - if( SXMEM_BACKEND_CORRUPT(pBackend) ){ - return 0; - } -#endif - if( pBackend->pMutexMethods ){ - SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex); - } - pChunk = MemBackendPoolRealloc(&(*pBackend), pOld, nByte); - if( pBackend->pMutexMethods ){ - SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex); - } - return pChunk; -} -#endif -JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void * pUserData) -{ -#if defined(UNTRUST) - if( pBackend == 0 ){ - return SXERR_EMPTY; - } -#endif - /* Zero the allocator first */ - SyZero(&(*pBackend), sizeof(SyMemBackend)); - pBackend->xMemError = xMemErr; - pBackend->pUserData = pUserData; - /* Switch to the OS memory allocator */ - pBackend->pMethods = &sOSAllocMethods; - if( pBackend->pMethods->xInit ){ - /* Initialize the backend */ - if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){ - return SXERR_ABORT; - } - } -#if defined(UNTRUST) - pBackend->nMagic = SXMEM_BACKEND_MAGIC; -#endif - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void * pUserData) -{ -#if defined(UNTRUST) - if( pBackend == 0 || pMethods == 0){ - return SXERR_EMPTY; - } -#endif - if( pMethods->xAlloc == 0 || pMethods->xRealloc == 0 || pMethods->xFree == 0 || pMethods->xChunkSize == 0 ){ - /* mandatory methods are missing */ - return SXERR_INVALID; - } - /* Zero the allocator first */ - SyZero(&(*pBackend), sizeof(SyMemBackend)); - pBackend->xMemError = xMemErr; - pBackend->pUserData = pUserData; - /* Switch to the host application memory allocator */ - pBackend->pMethods = pMethods; - if( pBackend->pMethods->xInit ){ - /* Initialize the backend */ - if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){ - return SXERR_ABORT; - } - } -#if defined(UNTRUST) - pBackend->nMagic = SXMEM_BACKEND_MAGIC; -#endif - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,const SyMemBackend *pParent) -{ - sxu8 bInheritMutex; -#if defined(UNTRUST) - if( pBackend == 0 || SXMEM_BACKEND_CORRUPT(pParent) ){ - return SXERR_CORRUPT; - } -#endif - /* Zero the allocator first */ - SyZero(&(*pBackend), sizeof(SyMemBackend)); - pBackend->pMethods = pParent->pMethods; - pBackend->xMemError = pParent->xMemError; - pBackend->pUserData = pParent->pUserData; - bInheritMutex = pParent->pMutexMethods ? TRUE : FALSE; - if( bInheritMutex ){ - pBackend->pMutexMethods = pParent->pMutexMethods; - /* Create a private mutex */ - pBackend->pMutex = pBackend->pMutexMethods->xNew(SXMUTEX_TYPE_FAST); - if( pBackend->pMutex == 0){ - return SXERR_OS; - } - } -#if defined(UNTRUST) - pBackend->nMagic = SXMEM_BACKEND_MAGIC; -#endif - return SXRET_OK; -} -static sxi32 MemBackendRelease(SyMemBackend *pBackend) -{ - SyMemBlock *pBlock, *pNext; - - pBlock = pBackend->pBlocks; - for(;;){ - if( pBackend->nBlock == 0 ){ - break; - } - pNext = pBlock->pNext; - pBackend->pMethods->xFree(pBlock); - pBlock = pNext; - pBackend->nBlock--; - /* LOOP ONE */ - if( pBackend->nBlock == 0 ){ - break; - } - pNext = pBlock->pNext; - pBackend->pMethods->xFree(pBlock); - pBlock = pNext; - pBackend->nBlock--; - /* LOOP TWO */ - if( pBackend->nBlock == 0 ){ - break; - } - pNext = pBlock->pNext; - pBackend->pMethods->xFree(pBlock); - pBlock = pNext; - pBackend->nBlock--; - /* LOOP THREE */ - if( pBackend->nBlock == 0 ){ - break; - } - pNext = pBlock->pNext; - pBackend->pMethods->xFree(pBlock); - pBlock = pNext; - pBackend->nBlock--; - /* LOOP FOUR */ - } - if( pBackend->pMethods->xRelease ){ - pBackend->pMethods->xRelease(pBackend->pMethods->pUserData); - } - pBackend->pMethods = 0; - pBackend->pBlocks = 0; -#if defined(UNTRUST) - pBackend->nMagic = 0x2626; -#endif - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend) -{ - sxi32 rc; -#if defined(UNTRUST) - if( SXMEM_BACKEND_CORRUPT(pBackend) ){ - return SXERR_INVALID; - } -#endif - if( pBackend->pMutexMethods ){ - SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex); - } - rc = MemBackendRelease(&(*pBackend)); - if( pBackend->pMutexMethods ){ - SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex); - SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex); - } - return rc; -} -JX9_PRIVATE void * SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize) -{ - void *pNew; -#if defined(UNTRUST) - if( pSrc == 0 || nSize <= 0 ){ - return 0; - } -#endif - pNew = SyMemBackendAlloc(&(*pBackend), nSize); - if( pNew ){ - SyMemcpy(pSrc, pNew, nSize); - } - return pNew; -} -JX9_PRIVATE char * SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize) -{ - char *zDest; - zDest = (char *)SyMemBackendAlloc(&(*pBackend), nSize + 1); - if( zDest ){ - Systrcpy(zDest, nSize+1, zSrc, nSize); - } - return zDest; -} -JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize) -{ -#if defined(UNTRUST) - if( pBlob == 0 || pBuffer == 0 || nSize < 1 ){ - return SXERR_EMPTY; - } -#endif - pBlob->pBlob = pBuffer; - pBlob->mByte = nSize; - pBlob->nByte = 0; - pBlob->pAllocator = 0; - pBlob->nFlags = SXBLOB_LOCKED|SXBLOB_STATIC; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator) -{ -#if defined(UNTRUST) - if( pBlob == 0 ){ - return SXERR_EMPTY; - } -#endif - pBlob->pBlob = 0; - pBlob->mByte = pBlob->nByte = 0; - pBlob->pAllocator = &(*pAllocator); - pBlob->nFlags = 0; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte) -{ -#if defined(UNTRUST) - if( pBlob == 0 ){ - return SXERR_EMPTY; - } -#endif - pBlob->pBlob = (void *)pData; - pBlob->nByte = nByte; - pBlob->mByte = 0; - pBlob->nFlags |= SXBLOB_RDONLY; - return SXRET_OK; -} -#ifndef SXBLOB_MIN_GROWTH -#define SXBLOB_MIN_GROWTH 16 -#endif -static sxi32 BlobPrepareGrow(SyBlob *pBlob, sxu32 *pByte) -{ - sxu32 nByte; - void *pNew; - nByte = *pByte; - if( pBlob->nFlags & (SXBLOB_LOCKED|SXBLOB_STATIC) ){ - if ( SyBlobFreeSpace(pBlob) < nByte ){ - *pByte = SyBlobFreeSpace(pBlob); - if( (*pByte) == 0 ){ - return SXERR_SHORT; - } - } - return SXRET_OK; - } - if( pBlob->nFlags & SXBLOB_RDONLY ){ - /* Make a copy of the read-only item */ - if( pBlob->nByte > 0 ){ - pNew = SyMemBackendDup(pBlob->pAllocator, pBlob->pBlob, pBlob->nByte); - if( pNew == 0 ){ - return SXERR_MEM; - } - pBlob->pBlob = pNew; - pBlob->mByte = pBlob->nByte; - }else{ - pBlob->pBlob = 0; - pBlob->mByte = 0; - } - /* Remove the read-only flag */ - pBlob->nFlags &= ~SXBLOB_RDONLY; - } - if( SyBlobFreeSpace(pBlob) >= nByte ){ - return SXRET_OK; - } - if( pBlob->mByte > 0 ){ - nByte = nByte + pBlob->mByte * 2 + SXBLOB_MIN_GROWTH; - }else if ( nByte < SXBLOB_MIN_GROWTH ){ - nByte = SXBLOB_MIN_GROWTH; - } - pNew = SyMemBackendRealloc(pBlob->pAllocator, pBlob->pBlob, nByte); - if( pNew == 0 ){ - return SXERR_MEM; - } - pBlob->pBlob = pNew; - pBlob->mByte = nByte; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize) -{ - sxu8 *zBlob; - sxi32 rc; - if( nSize < 1 ){ - return SXRET_OK; - } - rc = BlobPrepareGrow(&(*pBlob), &nSize); - if( SXRET_OK != rc ){ - return rc; - } - if( pData ){ - zBlob = (sxu8 *)pBlob->pBlob ; - zBlob = &zBlob[pBlob->nByte]; - pBlob->nByte += nSize; - SX_MACRO_FAST_MEMCPY(pData, zBlob, nSize); - } - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob) -{ - sxi32 rc; - sxu32 n; - n = pBlob->nByte; - rc = SyBlobAppend(&(*pBlob), (const void *)"\0", sizeof(char)); - if (rc == SXRET_OK ){ - pBlob->nByte = n; - } - return rc; -} -JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest) -{ - sxi32 rc = SXRET_OK; - if( pSrc->nByte > 0 ){ - rc = SyBlobAppend(&(*pDest), pSrc->pBlob, pSrc->nByte); - } - return rc; -} -JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob) -{ - pBlob->nByte = 0; - if( pBlob->nFlags & SXBLOB_RDONLY ){ - /* Read-only (Not malloced chunk) */ - pBlob->pBlob = 0; - pBlob->mByte = 0; - pBlob->nFlags &= ~SXBLOB_RDONLY; - } - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyBlobTruncate(SyBlob *pBlob,sxu32 nNewLen) -{ - if( nNewLen < pBlob->nByte ){ - pBlob->nByte = nNewLen; - } - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob) -{ - if( (pBlob->nFlags & (SXBLOB_STATIC|SXBLOB_RDONLY)) == 0 && pBlob->mByte > 0 ){ - SyMemBackendFree(pBlob->pAllocator, pBlob->pBlob); - } - pBlob->pBlob = 0; - pBlob->nByte = pBlob->mByte = 0; - pBlob->nFlags = 0; - return SXRET_OK; -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft) -{ - const char *zIn = (const char *)pBlob; - const char *zEnd; - sxi32 rc; - if( pLen > nLen ){ - return SXERR_NOTFOUND; - } - zEnd = &zIn[nLen-pLen]; - for(;;){ - if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++; - if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++; - if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++; - if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++; - } - return SXERR_NOTFOUND; -} -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -/* SyRunTimeApi:sxds.c */ -JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize) -{ - pSet->nSize = 0 ; - pSet->nUsed = 0; - pSet->nCursor = 0; - pSet->eSize = ElemSize; - pSet->pAllocator = pAllocator; - pSet->pBase = 0; - pSet->pUserData = 0; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem) -{ - unsigned char *zbase; - if( pSet->nUsed >= pSet->nSize ){ - void *pNew; - if( pSet->pAllocator == 0 ){ - return SXERR_LOCKED; - } - if( pSet->nSize <= 0 ){ - pSet->nSize = 4; - } - pNew = SyMemBackendRealloc(pSet->pAllocator, pSet->pBase, pSet->eSize * pSet->nSize * 2); - if( pNew == 0 ){ - return SXERR_MEM; - } - pSet->pBase = pNew; - pSet->nSize <<= 1; - } - zbase = (unsigned char *)pSet->pBase; - SX_MACRO_FAST_MEMCPY(pItem, &zbase[pSet->nUsed * pSet->eSize], pSet->eSize); - pSet->nUsed++; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem) -{ - if( pSet->nSize > 0 ){ - return SXERR_LOCKED; - } - if( nItem < 8 ){ - nItem = 8; - } - pSet->pBase = SyMemBackendAlloc(pSet->pAllocator, pSet->eSize * nItem); - if( pSet->pBase == 0 ){ - return SXERR_MEM; - } - pSet->nSize = nItem; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SySetReset(SySet *pSet) -{ - pSet->nUsed = 0; - pSet->nCursor = 0; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet) -{ - pSet->nCursor = 0; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry) -{ - register unsigned char *zSrc; - if( pSet->nCursor >= pSet->nUsed ){ - /* Reset cursor */ - pSet->nCursor = 0; - return SXERR_EOF; - } - zSrc = (unsigned char *)SySetBasePtr(pSet); - if( ppEntry ){ - *ppEntry = (void *)&zSrc[pSet->nCursor * pSet->eSize]; - } - pSet->nCursor++; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SySetRelease(SySet *pSet) -{ - sxi32 rc = SXRET_OK; - if( pSet->pAllocator && pSet->pBase ){ - rc = SyMemBackendFree(pSet->pAllocator, pSet->pBase); - } - pSet->pBase = 0; - pSet->nUsed = 0; - pSet->nCursor = 0; - return rc; -} -JX9_PRIVATE void * SySetPeek(SySet *pSet) -{ - const char *zBase; - if( pSet->nUsed <= 0 ){ - return 0; - } - zBase = (const char *)pSet->pBase; - return (void *)&zBase[(pSet->nUsed - 1) * pSet->eSize]; -} -JX9_PRIVATE void * SySetPop(SySet *pSet) -{ - const char *zBase; - void *pData; - if( pSet->nUsed <= 0 ){ - return 0; - } - zBase = (const char *)pSet->pBase; - pSet->nUsed--; - pData = (void *)&zBase[pSet->nUsed * pSet->eSize]; - return pData; -} -JX9_PRIVATE void * SySetAt(SySet *pSet, sxu32 nIdx) -{ - const char *zBase; - if( nIdx >= pSet->nUsed ){ - /* Out of range */ - return 0; - } - zBase = (const char *)pSet->pBase; - return (void *)&zBase[nIdx * pSet->eSize]; -} -/* Private hash entry */ -struct SyHashEntry_Pr -{ - const void *pKey; /* Hash key */ - sxu32 nKeyLen; /* Key length */ - void *pUserData; /* User private data */ - /* Private fields */ - sxu32 nHash; - SyHash *pHash; - SyHashEntry_Pr *pNext, *pPrev; /* Next and previous entry in the list */ - SyHashEntry_Pr *pNextCollide, *pPrevCollide; /* Collision list */ -}; -#define INVALID_HASH(H) ((H)->apBucket == 0) -JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp) -{ - SyHashEntry_Pr **apNew; -#if defined(UNTRUST) - if( pHash == 0 ){ - return SXERR_EMPTY; - } -#endif - /* Allocate a new table */ - apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(&(*pAllocator), sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE); - if( apNew == 0 ){ - return SXERR_MEM; - } - SyZero((void *)apNew, sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE); - pHash->pAllocator = &(*pAllocator); - pHash->xHash = xHash ? xHash : SyBinHash; - pHash->xCmp = xCmp ? xCmp : SyMemcmp; - pHash->pCurrent = pHash->pList = 0; - pHash->nEntry = 0; - pHash->apBucket = apNew; - pHash->nBucketSize = SXHASH_BUCKET_SIZE; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash) -{ - SyHashEntry_Pr *pEntry, *pNext; -#if defined(UNTRUST) - if( INVALID_HASH(pHash) ){ - return SXERR_EMPTY; - } -#endif - pEntry = pHash->pList; - for(;;){ - if( pHash->nEntry == 0 ){ - break; - } - pNext = pEntry->pNext; - SyMemBackendPoolFree(pHash->pAllocator, pEntry); - pEntry = pNext; - pHash->nEntry--; - } - if( pHash->apBucket ){ - SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket); - } - pHash->apBucket = 0; - pHash->nBucketSize = 0; - pHash->pAllocator = 0; - return SXRET_OK; -} -static SyHashEntry_Pr * HashGetEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen) -{ - SyHashEntry_Pr *pEntry; - sxu32 nHash; - - nHash = pHash->xHash(pKey, nKeyLen); - pEntry = pHash->apBucket[nHash & (pHash->nBucketSize - 1)]; - for(;;){ - if( pEntry == 0 ){ - break; - } - if( pEntry->nHash == nHash && pEntry->nKeyLen == nKeyLen && - pHash->xCmp(pEntry->pKey, pKey, nKeyLen) == 0 ){ - return pEntry; - } - pEntry = pEntry->pNextCollide; - } - /* Entry not found */ - return 0; -} -JX9_PRIVATE SyHashEntry * SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen) -{ - SyHashEntry_Pr *pEntry; -#if defined(UNTRUST) - if( INVALID_HASH(pHash) ){ - return 0; - } -#endif - if( pHash->nEntry < 1 || nKeyLen < 1 ){ - /* Don't bother hashing, return immediately */ - return 0; - } - pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen); - if( pEntry == 0 ){ - return 0; - } - return (SyHashEntry *)pEntry; -} -static sxi32 HashDeleteEntry(SyHash *pHash, SyHashEntry_Pr *pEntry, void **ppUserData) -{ - sxi32 rc; - if( pEntry->pPrevCollide == 0 ){ - pHash->apBucket[pEntry->nHash & (pHash->nBucketSize - 1)] = pEntry->pNextCollide; - }else{ - pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide; - } - if( pEntry->pNextCollide ){ - pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide; - } - MACRO_LD_REMOVE(pHash->pList, pEntry); - pHash->nEntry--; - if( ppUserData ){ - /* Write a pointer to the user data */ - *ppUserData = pEntry->pUserData; - } - /* Release the entry */ - rc = SyMemBackendPoolFree(pHash->pAllocator, pEntry); - return rc; -} -JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData) -{ - SyHashEntry_Pr *pEntry; - sxi32 rc; -#if defined(UNTRUST) - if( INVALID_HASH(pHash) ){ - return SXERR_CORRUPT; - } -#endif - pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen); - if( pEntry == 0 ){ - return SXERR_NOTFOUND; - } - rc = HashDeleteEntry(&(*pHash), pEntry, ppUserData); - return rc; -} -JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32 (*xStep)(SyHashEntry *, void *), void *pUserData) -{ - SyHashEntry_Pr *pEntry; - sxi32 rc; - sxu32 n; -#if defined(UNTRUST) - if( INVALID_HASH(pHash) || xStep == 0){ - return 0; - } -#endif - pEntry = pHash->pList; - for( n = 0 ; n < pHash->nEntry ; n++ ){ - /* Invoke the callback */ - rc = xStep((SyHashEntry *)pEntry, pUserData); - if( rc != SXRET_OK ){ - return rc; - } - /* Point to the next entry */ - pEntry = pEntry->pNext; - } - return SXRET_OK; -} -static sxi32 HashGrowTable(SyHash *pHash) -{ - sxu32 nNewSize = pHash->nBucketSize * 2; - SyHashEntry_Pr *pEntry; - SyHashEntry_Pr **apNew; - sxu32 n, iBucket; - - /* Allocate a new larger table */ - apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(pHash->pAllocator, nNewSize * sizeof(SyHashEntry_Pr *)); - if( apNew == 0 ){ - /* Not so fatal, simply a performance hit */ - return SXRET_OK; - } - /* Zero the new table */ - SyZero((void *)apNew, nNewSize * sizeof(SyHashEntry_Pr *)); - /* Rehash all entries */ - for( n = 0, pEntry = pHash->pList; n < pHash->nEntry ; n++ ){ - pEntry->pNextCollide = pEntry->pPrevCollide = 0; - /* Install in the new bucket */ - iBucket = pEntry->nHash & (nNewSize - 1); - pEntry->pNextCollide = apNew[iBucket]; - if( apNew[iBucket] != 0 ){ - apNew[iBucket]->pPrevCollide = pEntry; - } - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - } - /* Release the old table and reflect the change */ - SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket); - pHash->apBucket = apNew; - pHash->nBucketSize = nNewSize; - return SXRET_OK; -} -static sxi32 HashInsert(SyHash *pHash, SyHashEntry_Pr *pEntry) -{ - sxu32 iBucket = pEntry->nHash & (pHash->nBucketSize - 1); - /* Insert the entry in its corresponding bcuket */ - pEntry->pNextCollide = pHash->apBucket[iBucket]; - if( pHash->apBucket[iBucket] != 0 ){ - pHash->apBucket[iBucket]->pPrevCollide = pEntry; - } - pHash->apBucket[iBucket] = pEntry; - /* Link to the entry list */ - MACRO_LD_PUSH(pHash->pList, pEntry); - if( pHash->nEntry == 0 ){ - pHash->pCurrent = pHash->pList; - } - pHash->nEntry++; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData) -{ - SyHashEntry_Pr *pEntry; - sxi32 rc; -#if defined(UNTRUST) - if( INVALID_HASH(pHash) || pKey == 0 ){ - return SXERR_CORRUPT; - } -#endif - if( pHash->nEntry >= pHash->nBucketSize * SXHASH_FILL_FACTOR ){ - rc = HashGrowTable(&(*pHash)); - if( rc != SXRET_OK ){ - return rc; - } - } - /* Allocate a new hash entry */ - pEntry = (SyHashEntry_Pr *)SyMemBackendPoolAlloc(pHash->pAllocator, sizeof(SyHashEntry_Pr)); - if( pEntry == 0 ){ - return SXERR_MEM; - } - /* Zero the entry */ - SyZero(pEntry, sizeof(SyHashEntry_Pr)); - pEntry->pHash = pHash; - pEntry->pKey = pKey; - pEntry->nKeyLen = nKeyLen; - pEntry->pUserData = pUserData; - pEntry->nHash = pHash->xHash(pEntry->pKey, pEntry->nKeyLen); - /* Finally insert the entry in its corresponding bucket */ - rc = HashInsert(&(*pHash), pEntry); - return rc; -} -/* SyRunTimeApi:sxutils.c */ -JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char **pzTail) -{ - const char *zCur, *zEnd; -#ifdef UNTRUST - if( SX_EMPTY_STR(zSrc) ){ - return SXERR_EMPTY; - } -#endif - zEnd = &zSrc[nLen]; - /* Jump leading white spaces */ - while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisSpace(zSrc[0]) ){ - zSrc++; - } - if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){ - zSrc++; - } - zCur = zSrc; - if( pReal ){ - *pReal = FALSE; - } - for(;;){ - if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++; - if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++; - if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++; - if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++; - }; - if( zSrc < zEnd && zSrc > zCur ){ - int c = zSrc[0]; - if( c == '.' ){ - zSrc++; - if( pReal ){ - *pReal = TRUE; - } - if( pzTail ){ - while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){ - zSrc++; - } - if( zSrc < zEnd && (zSrc[0] == 'e' || zSrc[0] == 'E') ){ - zSrc++; - if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){ - zSrc++; - } - while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){ - zSrc++; - } - } - } - }else if( c == 'e' || c == 'E' ){ - zSrc++; - if( pReal ){ - *pReal = TRUE; - } - if( pzTail ){ - if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){ - zSrc++; - } - while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){ - zSrc++; - } - } - } - } - if( pzTail ){ - /* Point to the non numeric part */ - *pzTail = zSrc; - } - return zSrc > zCur ? SXRET_OK /* String prefix is numeric */ : SXERR_INVALID /* Not a digit stream */; -} -#define SXINT32_MIN_STR "2147483648" -#define SXINT32_MAX_STR "2147483647" -#define SXINT64_MIN_STR "9223372036854775808" -#define SXINT64_MAX_STR "9223372036854775807" -JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest) -{ - int isNeg = FALSE; - const char *zEnd; - sxi32 nVal = 0; - sxi16 i; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zSrc) ){ - if( pOutVal ){ - *(sxi32 *)pOutVal = 0; - } - return SXERR_EMPTY; - } -#endif - zEnd = &zSrc[nLen]; - while(zSrc < zEnd && SyisSpace(zSrc[0]) ){ - zSrc++; - } - if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){ - isNeg = (zSrc[0] == '-') ? TRUE :FALSE; - zSrc++; - } - /* Skip leading zero */ - while(zSrc < zEnd && zSrc[0] == '0' ){ - zSrc++; - } - i = 10; - if( (sxu32)(zEnd-zSrc) >= 10 ){ - /* Handle overflow */ - i = SyMemcmp(zSrc, (isNeg == TRUE) ? SXINT32_MIN_STR : SXINT32_MAX_STR, nLen) <= 0 ? 10 : 9; - } - for(;;){ - if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; - if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; - if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; - if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; - } - /* Skip trailing spaces */ - while(zSrc < zEnd && SyisSpace(zSrc[0])){ - zSrc++; - } - if( zRest ){ - *zRest = (char *)zSrc; - } - if( pOutVal ){ - if( isNeg == TRUE && nVal != 0 ){ - nVal = -nVal; - } - *(sxi32 *)pOutVal = nVal; - } - return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX; -} -JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest) -{ - int isNeg = FALSE; - const char *zEnd; - sxi64 nVal; - sxi16 i; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zSrc) ){ - if( pOutVal ){ - *(sxi32 *)pOutVal = 0; - } - return SXERR_EMPTY; - } -#endif - zEnd = &zSrc[nLen]; - while(zSrc < zEnd && SyisSpace(zSrc[0]) ){ - zSrc++; - } - if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){ - isNeg = (zSrc[0] == '-') ? TRUE :FALSE; - zSrc++; - } - /* Skip leading zero */ - while(zSrc < zEnd && zSrc[0] == '0' ){ - zSrc++; - } - i = 19; - if( (sxu32)(zEnd-zSrc) >= 19 ){ - i = SyMemcmp(zSrc, isNeg ? SXINT64_MIN_STR : SXINT64_MAX_STR, 19) <= 0 ? 19 : 18 ; - } - nVal = 0; - for(;;){ - if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; - if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; - if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; - if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; - } - /* Skip trailing spaces */ - while(zSrc < zEnd && SyisSpace(zSrc[0])){ - zSrc++; - } - if( zRest ){ - *zRest = (char *)zSrc; - } - if( pOutVal ){ - if( isNeg == TRUE && nVal != 0 ){ - nVal = -nVal; - } - *(sxi64 *)pOutVal = nVal; - } - return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX; -} -JX9_PRIVATE sxi32 SyHexToint(sxi32 c) -{ - switch(c){ - case '0': return 0; - case '1': return 1; - case '2': return 2; - case '3': return 3; - case '4': return 4; - case '5': return 5; - case '6': return 6; - case '7': return 7; - case '8': return 8; - case '9': return 9; - case 'A': case 'a': return 10; - case 'B': case 'b': return 11; - case 'C': case 'c': return 12; - case 'D': case 'd': return 13; - case 'E': case 'e': return 14; - case 'F': case 'f': return 15; - } - return -1; -} -JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest) -{ - const char *zIn, *zEnd; - int isNeg = FALSE; - sxi64 nVal = 0; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zSrc) ){ - if( pOutVal ){ - *(sxi32 *)pOutVal = 0; - } - return SXERR_EMPTY; - } -#endif - zEnd = &zSrc[nLen]; - while( zSrc < zEnd && SyisSpace(zSrc[0]) ){ - zSrc++; - } - if( zSrc < zEnd && ( *zSrc == '-' || *zSrc == '+' ) ){ - isNeg = (zSrc[0] == '-') ? TRUE :FALSE; - zSrc++; - } - if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'x' || zSrc[1] == 'X') ){ - /* Bypass hex prefix */ - zSrc += sizeof(char) * 2; - } - /* Skip leading zero */ - while(zSrc < zEnd && zSrc[0] == '0' ){ - zSrc++; - } - zIn = zSrc; - for(;;){ - if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ; - if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ; - if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ; - if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ; - } - while( zSrc < zEnd && SyisSpace(zSrc[0]) ){ - zSrc++; - } - if( zRest ){ - *zRest = zSrc; - } - if( pOutVal ){ - if( isNeg == TRUE && nVal != 0 ){ - nVal = -nVal; - } - *(sxi64 *)pOutVal = nVal; - } - return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX; -} -JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest) -{ - const char *zIn, *zEnd; - int isNeg = FALSE; - sxi64 nVal = 0; - int c; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zSrc) ){ - if( pOutVal ){ - *(sxi32 *)pOutVal = 0; - } - return SXERR_EMPTY; - } -#endif - zEnd = &zSrc[nLen]; - while(zSrc < zEnd && SyisSpace(zSrc[0]) ){ - zSrc++; - } - if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){ - isNeg = (zSrc[0] == '-') ? TRUE :FALSE; - zSrc++; - } - /* Skip leading zero */ - while(zSrc < zEnd && zSrc[0] == '0' ){ - zSrc++; - } - zIn = zSrc; - for(;;){ - if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++; - if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++; - if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++; - if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++; - } - /* Skip trailing spaces */ - while(zSrc < zEnd && SyisSpace(zSrc[0])){ - zSrc++; - } - if( zRest ){ - *zRest = zSrc; - } - if( pOutVal ){ - if( isNeg == TRUE && nVal != 0 ){ - nVal = -nVal; - } - *(sxi64 *)pOutVal = nVal; - } - return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX; -} -JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest) -{ - const char *zIn, *zEnd; - int isNeg = FALSE; - sxi64 nVal = 0; - int c; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zSrc) ){ - if( pOutVal ){ - *(sxi32 *)pOutVal = 0; - } - return SXERR_EMPTY; - } -#endif - zEnd = &zSrc[nLen]; - while(zSrc < zEnd && SyisSpace(zSrc[0]) ){ - zSrc++; - } - if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){ - isNeg = (zSrc[0] == '-') ? TRUE :FALSE; - zSrc++; - } - if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'b' || zSrc[1] == 'B') ){ - /* Bypass binary prefix */ - zSrc += sizeof(char) * 2; - } - /* Skip leading zero */ - while(zSrc < zEnd && zSrc[0] == '0' ){ - zSrc++; - } - zIn = zSrc; - for(;;){ - if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++; - if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++; - if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++; - if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++; - } - /* Skip trailing spaces */ - while(zSrc < zEnd && SyisSpace(zSrc[0])){ - zSrc++; - } - if( zRest ){ - *zRest = zSrc; - } - if( pOutVal ){ - if( isNeg == TRUE && nVal != 0 ){ - nVal = -nVal; - } - *(sxi64 *)pOutVal = nVal; - } - return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX; -} -JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest) -{ -#define SXDBL_DIG 15 -#define SXDBL_MAX_EXP 308 -#define SXDBL_MIN_EXP_PLUS 307 - static const sxreal aTab[] = { - 10, - 1.0e2, - 1.0e4, - 1.0e8, - 1.0e16, - 1.0e32, - 1.0e64, - 1.0e128, - 1.0e256 - }; - sxu8 neg = FALSE; - sxreal Val = 0.0; - const char *zEnd; - sxi32 Lim, exp; - sxreal *p = 0; -#ifdef UNTRUST - if( SX_EMPTY_STR(zSrc) ){ - if( pOutVal ){ - *(sxreal *)pOutVal = 0.0; - } - return SXERR_EMPTY; - } -#endif - zEnd = &zSrc[nLen]; - while( zSrc < zEnd && SyisSpace(zSrc[0]) ){ - zSrc++; - } - if( zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+' ) ){ - neg = zSrc[0] == '-' ? TRUE : FALSE ; - zSrc++; - } - Lim = SXDBL_DIG ; - for(;;){ - if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim; - if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim; - if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim; - if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim; - } - if( zSrc < zEnd && ( zSrc[0] == '.' || zSrc[0] == ',' ) ){ - sxreal dec = 1.0; - zSrc++; - for(;;){ - if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim; - if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim; - if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim; - if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim; - } - Val /= dec; - } - if( neg == TRUE && Val != 0.0 ) { - Val = -Val ; - } - if( Lim <= 0 ){ - /* jump overflow digit */ - while( zSrc < zEnd ){ - if( zSrc[0] == 'e' || zSrc[0] == 'E' ){ - break; - } - zSrc++; - } - } - neg = FALSE; - if( zSrc < zEnd && ( zSrc[0] == 'e' || zSrc[0] == 'E' ) ){ - zSrc++; - if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+') ){ - neg = zSrc[0] == '-' ? TRUE : FALSE ; - zSrc++; - } - exp = 0; - while( zSrc < zEnd && SyisDigit(zSrc[0]) && exp < SXDBL_MAX_EXP ){ - exp = exp * 10 + (zSrc[0] - '0'); - zSrc++; - } - if( neg ){ - if( exp > SXDBL_MIN_EXP_PLUS ) exp = SXDBL_MIN_EXP_PLUS ; - }else if ( exp > SXDBL_MAX_EXP ){ - exp = SXDBL_MAX_EXP; - } - for( p = (sxreal *)aTab ; exp ; exp >>= 1 , p++ ){ - if( exp & 01 ){ - if( neg ){ - Val /= *p ; - }else{ - Val *= *p; - } - } - } - } - while( zSrc < zEnd && SyisSpace(zSrc[0]) ){ - zSrc++; - } - if( zRest ){ - *zRest = zSrc; - } - if( pOutVal ){ - *(sxreal *)pOutVal = Val; - } - return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX; -} -/* SyRunTimeApi:sxlib.c */ -JX9_PRIVATE sxu32 SyBinHash(const void *pSrc, sxu32 nLen) -{ - register unsigned char *zIn = (unsigned char *)pSrc; - unsigned char *zEnd; - sxu32 nH = 5381; - zEnd = &zIn[nLen]; - for(;;){ - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - } - return nH; -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData) -{ - static const unsigned char zBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - unsigned char *zIn = (unsigned char *)zSrc; - unsigned char z64[4]; - sxu32 i; - sxi32 rc; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zSrc) || xConsumer == 0){ - return SXERR_EMPTY; - } -#endif - for(i = 0; i + 2 < nLen; i += 3){ - z64[0] = zBase64[(zIn[i] >> 2) & 0x3F]; - z64[1] = zBase64[( ((zIn[i] & 0x03) << 4) | (zIn[i+1] >> 4)) & 0x3F]; - z64[2] = zBase64[( ((zIn[i+1] & 0x0F) << 2) | (zIn[i + 2] >> 6) ) & 0x3F]; - z64[3] = zBase64[ zIn[i + 2] & 0x3F]; - - rc = xConsumer((const void *)z64, sizeof(z64), pUserData); - if( rc != SXRET_OK ){return SXERR_ABORT;} - - } - if ( i+1 < nLen ){ - z64[0] = zBase64[(zIn[i] >> 2) & 0x3F]; - z64[1] = zBase64[( ((zIn[i] & 0x03) << 4) | (zIn[i+1] >> 4)) & 0x3F]; - z64[2] = zBase64[(zIn[i+1] & 0x0F) << 2 ]; - z64[3] = '='; - - rc = xConsumer((const void *)z64, sizeof(z64), pUserData); - if( rc != SXRET_OK ){return SXERR_ABORT;} - - }else if( i < nLen ){ - z64[0] = zBase64[(zIn[i] >> 2) & 0x3F]; - z64[1] = zBase64[(zIn[i] & 0x03) << 4]; - z64[2] = '='; - z64[3] = '='; - - rc = xConsumer((const void *)z64, sizeof(z64), pUserData); - if( rc != SXRET_OK ){return SXERR_ABORT;} - } - - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData) -{ - static const sxu32 aBase64Trans[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, - 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, - 0, 0, 0 - }; - sxu32 n, w, x, y, z; - sxi32 rc; - unsigned char zOut[10]; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zB64) || xConsumer == 0 ){ - return SXERR_EMPTY; - } -#endif - while(nLen > 0 && zB64[nLen - 1] == '=' ){ - nLen--; - } - for( n = 0 ; n+3>4) & 0x03); - zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F); - zOut[2] = ((y<<6) & 0xC0) | (z & 0x3F); - - rc = xConsumer((const void *)zOut, sizeof(unsigned char)*3, pUserData); - if( rc != SXRET_OK ){ return SXERR_ABORT;} - } - if( n+2 < nLen ){ - w = aBase64Trans[zB64[n] & 0x7F]; - x = aBase64Trans[zB64[n+1] & 0x7F]; - y = aBase64Trans[zB64[n+2] & 0x7F]; - - zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03); - zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F); - - rc = xConsumer((const void *)zOut, sizeof(unsigned char)*2, pUserData); - if( rc != SXRET_OK ){ return SXERR_ABORT;} - }else if( n+1 < nLen ){ - w = aBase64Trans[zB64[n] & 0x7F]; - x = aBase64Trans[zB64[n+1] & 0x7F]; - - zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03); - - rc = xConsumer((const void *)zOut, sizeof(unsigned char)*1, pUserData); - if( rc != SXRET_OK ){ return SXERR_ABORT;} - } - return SXRET_OK; -} -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -#define INVALID_LEXER(LEX) ( LEX == 0 || LEX->xTokenizer == 0 ) -JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData) -{ - SyStream *pStream; -#if defined (UNTRUST) - if ( pLex == 0 || xTokenizer == 0 ){ - return SXERR_CORRUPT; - } -#endif - pLex->pTokenSet = 0; - /* Initialize lexer fields */ - if( pSet ){ - if ( SySetElemSize(pSet) != sizeof(SyToken) ){ - return SXERR_INVALID; - } - pLex->pTokenSet = pSet; - } - pStream = &pLex->sStream; - pLex->xTokenizer = xTokenizer; - pLex->pUserData = pUserData; - - pStream->nLine = 1; - pStream->nIgn = 0; - pStream->zText = pStream->zEnd = 0; - pStream->pSet = pSet; - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp) -{ - const unsigned char *zCur; - SyStream *pStream; - SyToken sToken; - sxi32 rc; -#if defined (UNTRUST) - if ( INVALID_LEXER(pLex) || zInput == 0 ){ - return SXERR_CORRUPT; - } -#endif - pStream = &pLex->sStream; - /* Point to the head of the input */ - pStream->zText = pStream->zInput = (const unsigned char *)zInput; - /* Point to the end of the input */ - pStream->zEnd = &pStream->zInput[nLen]; - for(;;){ - if( pStream->zText >= pStream->zEnd ){ - /* End of the input reached */ - break; - } - zCur = pStream->zText; - /* Call the tokenizer callback */ - rc = pLex->xTokenizer(pStream, &sToken, pLex->pUserData, pCtxData); - if( rc != SXRET_OK && rc != SXERR_CONTINUE ){ - /* Tokenizer callback request an operation abort */ - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - break; - } - if( rc == SXERR_CONTINUE ){ - /* Request to ignore this token */ - pStream->nIgn++; - }else if( pLex->pTokenSet ){ - /* Put the token in the set */ - rc = SySetPut(pLex->pTokenSet, (const void *)&sToken); - if( rc != SXRET_OK ){ - break; - } - } - if( zCur >= pStream->zText ){ - /* Automatic advance of the stream cursor */ - pStream->zText = &zCur[1]; - } - } - if( xSort && pLex->pTokenSet ){ - SyToken *aToken = (SyToken *)SySetBasePtr(pLex->pTokenSet); - /* Sort the extrated tokens */ - if( xCmp == 0 ){ - /* Use a default comparison function */ - xCmp = SyMemcmp; - } - xSort(aToken, SySetUsed(pLex->pTokenSet), sizeof(SyToken), xCmp); - } - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex) -{ - sxi32 rc = SXRET_OK; -#if defined (UNTRUST) - if ( INVALID_LEXER(pLex) ){ - return SXERR_CORRUPT; - } -#else - SXUNUSED(pLex); /* Prevent compiler warning */ -#endif - return rc; -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -#define SAFE_HTTP(C) (SyisAlphaNum(c) || c == '_' || c == '-' || c == '$' || c == '.' ) -JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData) -{ - unsigned char *zIn = (unsigned char *)zSrc; - unsigned char zHex[3] = { '%', 0, 0 }; - unsigned char zOut[2]; - unsigned char *zCur, *zEnd; - sxi32 c; - sxi32 rc; -#ifdef UNTRUST - if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){ - return SXERR_EMPTY; - } -#endif - rc = SXRET_OK; - zEnd = &zIn[nLen]; zCur = zIn; - for(;;){ - if( zCur >= zEnd ){ - if( zCur != zIn ){ - rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData); - } - break; - } - c = zCur[0]; - if( SAFE_HTTP(c) ){ - zCur++; continue; - } - if( zCur != zIn && SXRET_OK != (rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData))){ - break; - } - if( c == ' ' ){ - zOut[0] = '+'; - rc = xConsumer((const void *)zOut, sizeof(unsigned char), pUserData); - }else{ - zHex[1] = "0123456789ABCDEF"[(c >> 4) & 0x0F]; - zHex[2] = "0123456789ABCDEF"[c & 0x0F]; - rc = xConsumer(zHex, sizeof(zHex), pUserData); - } - if( SXRET_OK != rc ){ - break; - } - zIn = &zCur[1]; zCur = zIn ; - } - return rc == SXRET_OK ? SXRET_OK : SXERR_ABORT; -} -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -static sxi32 SyAsciiToHex(sxi32 c) -{ - if( c >= 'a' && c <= 'f' ){ - c += 10 - 'a'; - return c; - } - if( c >= '0' && c <= '9' ){ - c -= '0'; - return c; - } - if( c >= 'A' && c <= 'F') { - c += 10 - 'A'; - return c; - } - return 0; -} -JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8) -{ - static const sxu8 Utf8Trans[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00 - }; - const char *zIn = zSrc; - const char *zEnd; - const char *zCur; - sxu8 *zOutPtr; - sxu8 zOut[10]; - sxi32 c, d; - sxi32 rc; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){ - return SXERR_EMPTY; - } -#endif - rc = SXRET_OK; - zEnd = &zSrc[nLen]; - zCur = zIn; - for(;;){ - while(zCur < zEnd && zCur[0] != '%' && zCur[0] != '+' ){ - zCur++; - } - if( zCur != zIn ){ - /* Consume input */ - rc = xConsumer(zIn, (unsigned int)(zCur-zIn), pUserData); - if( rc != SXRET_OK ){ - /* User consumer routine request an operation abort */ - break; - } - } - if( zCur >= zEnd ){ - rc = SXRET_OK; - break; - } - /* Decode unsafe HTTP characters */ - zOutPtr = zOut; - if( zCur[0] == '+' ){ - *zOutPtr++ = ' '; - zCur++; - }else{ - if( &zCur[2] >= zEnd ){ - rc = SXERR_OVERFLOW; - break; - } - c = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]); - zCur += 3; - if( c < 0x000C0 ){ - *zOutPtr++ = (sxu8)c; - }else{ - c = Utf8Trans[c-0xC0]; - while( zCur[0] == '%' ){ - d = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]); - if( (d&0xC0) != 0x80 ){ - break; - } - c = (c<<6) + (0x3f & d); - zCur += 3; - } - if( bUTF8 == FALSE ){ - *zOutPtr++ = (sxu8)c; - }else{ - SX_WRITE_UTF8(zOutPtr, c); - } - } - - } - /* Consume the decoded characters */ - rc = xConsumer((const void *)zOut, (unsigned int)(zOutPtr-zOut), pUserData); - if( rc != SXRET_OK ){ - break; - } - /* Synchronize pointers */ - zIn = zCur; - } - return rc; -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -static const char *zEngDay[] = { - "Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday" -}; -static const char *zEngMonth[] = { - "January", "February", "March", "April", - "May", "June", "July", "August", - "September", "October", "November", "December" -}; -static const char * GetDay(sxi32 i) -{ - return zEngDay[ i % 7 ]; -} -static const char * GetMonth(sxi32 i) -{ - return zEngMonth[ i % 12 ]; -} -JX9_PRIVATE const char * SyTimeGetDay(sxi32 iDay) -{ - return GetDay(iDay); -} -JX9_PRIVATE const char * SyTimeGetMonth(sxi32 iMonth) -{ - return GetMonth(iMonth); -} -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -/* SyRunTimeApi: sxfmt.c */ -#define SXFMT_BUFSIZ 1024 /* Conversion buffer size */ -/* -** Conversion types fall into various categories as defined by the -** following enumeration. -*/ -#define SXFMT_RADIX 1 /* Integer types.%d, %x, %o, and so forth */ -#define SXFMT_FLOAT 2 /* Floating point.%f */ -#define SXFMT_EXP 3 /* Exponentional notation.%e and %E */ -#define SXFMT_GENERIC 4 /* Floating or exponential, depending on exponent.%g */ -#define SXFMT_SIZE 5 /* Total number of characters processed so far.%n */ -#define SXFMT_STRING 6 /* Strings.%s */ -#define SXFMT_PERCENT 7 /* Percent symbol.%% */ -#define SXFMT_CHARX 8 /* Characters.%c */ -#define SXFMT_ERROR 9 /* Used to indicate no such conversion type */ -/* Extension by Symisc Systems */ -#define SXFMT_RAWSTR 13 /* %z Pointer to raw string (SyString *) */ -#define SXFMT_UNUSED 15 -/* -** Allowed values for SyFmtInfo.flags -*/ -#define SXFLAG_SIGNED 0x01 -#define SXFLAG_UNSIGNED 0x02 -/* Allowed values for SyFmtConsumer.nType */ -#define SXFMT_CONS_PROC 1 /* Consumer is a procedure */ -#define SXFMT_CONS_STR 2 /* Consumer is a managed string */ -#define SXFMT_CONS_FILE 5 /* Consumer is an open File */ -#define SXFMT_CONS_BLOB 6 /* Consumer is a BLOB */ -/* -** Each builtin conversion character (ex: the 'd' in "%d") is described -** by an instance of the following structure -*/ -typedef struct SyFmtInfo SyFmtInfo; -struct SyFmtInfo -{ - char fmttype; /* The format field code letter [i.e: 'd', 's', 'x'] */ - sxu8 base; /* The base for radix conversion */ - int flags; /* One or more of SXFLAG_ constants below */ - sxu8 type; /* Conversion paradigm */ - char *charset; /* The character set for conversion */ - char *prefix; /* Prefix on non-zero values in alt format */ -}; -typedef struct SyFmtConsumer SyFmtConsumer; -struct SyFmtConsumer -{ - sxu32 nLen; /* Total output length */ - sxi32 nType; /* Type of the consumer see below */ - sxi32 rc; /* Consumer return value;Abort processing if rc != SXRET_OK */ - union{ - struct{ - ProcConsumer xUserConsumer; - void *pUserData; - }sFunc; - SyBlob *pBlob; - }uConsumer; -}; -#ifndef SX_OMIT_FLOATINGPOINT -static int getdigit(sxlongreal *val, int *cnt) -{ - sxlongreal d; - int digit; - - if( (*cnt)++ >= 16 ){ - return '0'; - } - digit = (int)*val; - d = digit; - *val = (*val - d)*10.0; - return digit + '0' ; -} -#endif /* SX_OMIT_FLOATINGPOINT */ -/* - * The following routine was taken from the SQLITE2 source tree and was - * extended by Symisc Systems to fit its need. - * Status: Public Domain - */ -static sxi32 InternFormat(ProcConsumer xConsumer, void *pUserData, const char *zFormat, va_list ap) -{ - /* - * The following table is searched linearly, so it is good to put the most frequently - * used conversion types first. - */ -static const SyFmtInfo aFmt[] = { - { 'd', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0 }, - { 's', 0, 0, SXFMT_STRING, 0, 0 }, - { 'c', 0, 0, SXFMT_CHARX, 0, 0 }, - { 'x', 16, 0, SXFMT_RADIX, "0123456789abcdef", "x0" }, - { 'X', 16, 0, SXFMT_RADIX, "0123456789ABCDEF", "X0" }, - /* -- Extensions by Symisc Systems -- */ - { 'z', 0, 0, SXFMT_RAWSTR, 0, 0 }, /* Pointer to a raw string (SyString *) */ - { 'B', 2, 0, SXFMT_RADIX, "01", "b0"}, - /* -- End of Extensions -- */ - { 'o', 8, 0, SXFMT_RADIX, "01234567", "0" }, - { 'u', 10, 0, SXFMT_RADIX, "0123456789", 0 }, -#ifndef SX_OMIT_FLOATINGPOINT - { 'f', 0, SXFLAG_SIGNED, SXFMT_FLOAT, 0, 0 }, - { 'e', 0, SXFLAG_SIGNED, SXFMT_EXP, "e", 0 }, - { 'E', 0, SXFLAG_SIGNED, SXFMT_EXP, "E", 0 }, - { 'g', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "e", 0 }, - { 'G', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "E", 0 }, -#endif - { 'i', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0 }, - { 'n', 0, 0, SXFMT_SIZE, 0, 0 }, - { '%', 0, 0, SXFMT_PERCENT, 0, 0 }, - { 'p', 10, 0, SXFMT_RADIX, "0123456789", 0 } -}; - int c; /* Next character in the format string */ - char *bufpt; /* Pointer to the conversion buffer */ - int precision; /* Precision of the current field */ - int length; /* Length of the field */ - int idx; /* A general purpose loop counter */ - int width; /* Width of the current field */ - sxu8 flag_leftjustify; /* True if "-" flag is present */ - sxu8 flag_plussign; /* True if "+" flag is present */ - sxu8 flag_blanksign; /* True if " " flag is present */ - sxu8 flag_alternateform; /* True if "#" flag is present */ - sxu8 flag_zeropad; /* True if field width constant starts with zero */ - sxu8 flag_long; /* True if "l" flag is present */ - sxi64 longvalue; /* Value for integer types */ - const SyFmtInfo *infop; /* Pointer to the appropriate info structure */ - char buf[SXFMT_BUFSIZ]; /* Conversion buffer */ - char prefix; /* Prefix character."+" or "-" or " " or '\0'.*/ - sxu8 errorflag = 0; /* True if an error is encountered */ - sxu8 xtype; /* Conversion paradigm */ - char *zExtra; - static char spaces[] = " "; -#define etSPACESIZE ((int)sizeof(spaces)-1) -#ifndef SX_OMIT_FLOATINGPOINT - sxlongreal realvalue; /* Value for real types */ - int exp; /* exponent of real numbers */ - double rounder; /* Used for rounding floating point values */ - sxu8 flag_dp; /* True if decimal point should be shown */ - sxu8 flag_rtz; /* True if trailing zeros should be removed */ - sxu8 flag_exp; /* True to force display of the exponent */ - int nsd; /* Number of significant digits returned */ -#endif - int rc; - - length = 0; - bufpt = 0; - for(; (c=(*zFormat))!=0; ++zFormat){ - if( c!='%' ){ - unsigned int amt; - bufpt = (char *)zFormat; - amt = 1; - while( (c=(*++zFormat))!='%' && c!=0 ) amt++; - rc = xConsumer((const void *)bufpt, amt, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - if( c==0 ){ - return errorflag > 0 ? SXERR_FORMAT : SXRET_OK; - } - } - if( (c=(*++zFormat))==0 ){ - errorflag = 1; - rc = xConsumer("%", sizeof("%")-1, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - return errorflag > 0 ? SXERR_FORMAT : SXRET_OK; - } - /* Find out what flags are present */ - flag_leftjustify = flag_plussign = flag_blanksign = - flag_alternateform = flag_zeropad = 0; - do{ - switch( c ){ - case '-': flag_leftjustify = 1; c = 0; break; - case '+': flag_plussign = 1; c = 0; break; - case ' ': flag_blanksign = 1; c = 0; break; - case '#': flag_alternateform = 1; c = 0; break; - case '0': flag_zeropad = 1; c = 0; break; - default: break; - } - }while( c==0 && (c=(*++zFormat))!=0 ); - /* Get the field width */ - width = 0; - if( c=='*' ){ - width = va_arg(ap, int); - if( width<0 ){ - flag_leftjustify = 1; - width = -width; - } - c = *++zFormat; - }else{ - while( c>='0' && c<='9' ){ - width = width*10 + c - '0'; - c = *++zFormat; - } - } - if( width > SXFMT_BUFSIZ-10 ){ - width = SXFMT_BUFSIZ-10; - } - /* Get the precision */ - precision = -1; - if( c=='.' ){ - precision = 0; - c = *++zFormat; - if( c=='*' ){ - precision = va_arg(ap, int); - if( precision<0 ) precision = -precision; - c = *++zFormat; - }else{ - while( c>='0' && c<='9' ){ - precision = precision*10 + c - '0'; - c = *++zFormat; - } - } - } - /* Get the conversion type modifier */ - flag_long = 0; - if( c=='l' || c == 'q' /* BSD quad (expect a 64-bit integer) */ ){ - flag_long = (c == 'q') ? 2 : 1; - c = *++zFormat; - if( c == 'l' ){ - /* Standard printf emulation 'lld' (expect a 64bit integer) */ - flag_long = 2; - } - } - /* Fetch the info entry for the field */ - infop = 0; - xtype = SXFMT_ERROR; - for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){ - if( c==aFmt[idx].fmttype ){ - infop = &aFmt[idx]; - xtype = infop->type; - break; - } - } - zExtra = 0; - - /* - ** At this point, variables are initialized as follows: - ** - ** flag_alternateform TRUE if a '#' is present. - ** flag_plussign TRUE if a '+' is present. - ** flag_leftjustify TRUE if a '-' is present or if the - ** field width was negative. - ** flag_zeropad TRUE if the width began with 0. - ** flag_long TRUE if the letter 'l' (ell) or 'q'(BSD quad) prefixed - ** the conversion character. - ** flag_blanksign TRUE if a ' ' is present. - ** width The specified field width.This is - ** always non-negative.Zero is the default. - ** precision The specified precision.The default - ** is -1. - ** xtype The object of the conversion. - ** infop Pointer to the appropriate info struct. - */ - switch( xtype ){ - case SXFMT_RADIX: - if( flag_long > 0 ){ - if( flag_long > 1 ){ - /* BSD quad: expect a 64-bit integer */ - longvalue = va_arg(ap, sxi64); - }else{ - longvalue = va_arg(ap, sxlong); - } - }else{ - if( infop->flags & SXFLAG_SIGNED ){ - longvalue = va_arg(ap, sxi32); - }else{ - longvalue = va_arg(ap, sxu32); - } - } - /* Limit the precision to prevent overflowing buf[] during conversion */ - if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40; -#if 1 - /* For the format %#x, the value zero is printed "0" not "0x0". - ** I think this is stupid.*/ - if( longvalue==0 ) flag_alternateform = 0; -#else - /* More sensible: turn off the prefix for octal (to prevent "00"), - ** but leave the prefix for hex.*/ - if( longvalue==0 && infop->base==8 ) flag_alternateform = 0; -#endif - if( infop->flags & SXFLAG_SIGNED ){ - if( longvalue<0 ){ - longvalue = -longvalue; - /* Ticket 1433-003 */ - if( longvalue < 0 ){ - /* Overflow */ - longvalue= 0x7FFFFFFFFFFFFFFF; - } - prefix = '-'; - }else if( flag_plussign ) prefix = '+'; - else if( flag_blanksign ) prefix = ' '; - else prefix = 0; - }else{ - if( longvalue<0 ){ - longvalue = -longvalue; - /* Ticket 1433-003 */ - if( longvalue < 0 ){ - /* Overflow */ - longvalue= 0x7FFFFFFFFFFFFFFF; - } - } - prefix = 0; - } - if( flag_zeropad && precisioncharset; - base = infop->base; - do{ /* Convert to ascii */ - *(--bufpt) = cset[longvalue%base]; - longvalue = longvalue/base; - }while( longvalue>0 ); - } - length = &buf[SXFMT_BUFSIZ-1]-bufpt; - for(idx=precision-length; idx>0; idx--){ - *(--bufpt) = '0'; /* Zero pad */ - } - if( prefix ) *(--bufpt) = prefix; /* Add sign */ - if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ - char *pre, x; - pre = infop->prefix; - if( *bufpt!=pre[0] ){ - for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x; - } - } - length = &buf[SXFMT_BUFSIZ-1]-bufpt; - break; - case SXFMT_FLOAT: - case SXFMT_EXP: - case SXFMT_GENERIC: -#ifndef SX_OMIT_FLOATINGPOINT - realvalue = va_arg(ap, double); - if( precision<0 ) precision = 6; /* Set default precision */ - if( precision>SXFMT_BUFSIZ-40) precision = SXFMT_BUFSIZ-40; - if( realvalue<0.0 ){ - realvalue = -realvalue; - prefix = '-'; - }else{ - if( flag_plussign ) prefix = '+'; - else if( flag_blanksign ) prefix = ' '; - else prefix = 0; - } - if( infop->type==SXFMT_GENERIC && precision>0 ) precision--; - rounder = 0.0; -#if 0 - /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */ - for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); -#else - /* It makes more sense to use 0.5 */ - for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1); -#endif - if( infop->type==SXFMT_FLOAT ) realvalue += rounder; - /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ - exp = 0; - if( realvalue>0.0 ){ - while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } - while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } - while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; } - while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; } - if( exp>350 || exp<-350 ){ - bufpt = "NaN"; - length = 3; - break; - } - } - bufpt = buf; - /* - ** If the field type is etGENERIC, then convert to either etEXP - ** or etFLOAT, as appropriate. - */ - flag_exp = xtype==SXFMT_EXP; - if( xtype!=SXFMT_FLOAT ){ - realvalue += rounder; - if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } - } - if( xtype==SXFMT_GENERIC ){ - flag_rtz = !flag_alternateform; - if( exp<-4 || exp>precision ){ - xtype = SXFMT_EXP; - }else{ - precision = precision - exp; - xtype = SXFMT_FLOAT; - } - }else{ - flag_rtz = 0; - } - /* - ** The "exp+precision" test causes output to be of type etEXP if - ** the precision is too large to fit in buf[]. - */ - nsd = 0; - if( xtype==SXFMT_FLOAT && exp+precision0 || flag_alternateform); - if( prefix ) *(bufpt++) = prefix; /* Sign */ - if( exp<0 ) *(bufpt++) = '0'; /* Digits before "." */ - else for(; exp>=0; exp--) *(bufpt++) = (char)getdigit(&realvalue, &nsd); - if( flag_dp ) *(bufpt++) = '.'; /* The decimal point */ - for(exp++; exp<0 && precision>0; precision--, exp++){ - *(bufpt++) = '0'; - } - while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd); - *(bufpt--) = 0; /* Null terminate */ - if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */ - while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0; - if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0; - } - bufpt++; /* point to next free slot */ - }else{ /* etEXP or etGENERIC */ - flag_dp = (precision>0 || flag_alternateform); - if( prefix ) *(bufpt++) = prefix; /* Sign */ - *(bufpt++) = (char)getdigit(&realvalue, &nsd); /* First digit */ - if( flag_dp ) *(bufpt++) = '.'; /* Decimal point */ - while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd); - bufpt--; /* point to last digit */ - if( flag_rtz && flag_dp ){ /* Remove tail zeros */ - while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0; - if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0; - } - bufpt++; /* point to next free slot */ - if( exp || flag_exp ){ - *(bufpt++) = infop->charset[0]; - if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */ - else { *(bufpt++) = '+'; } - if( exp>=100 ){ - *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */ - exp %= 100; - } - *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */ - *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */ - } - } - /* The converted number is in buf[] and zero terminated.Output it. - ** Note that the number is in the usual order, not reversed as with - ** integer conversions.*/ - length = bufpt-buf; - bufpt = buf; - - /* Special case: Add leading zeros if the flag_zeropad flag is - ** set and we are not left justified */ - if( flag_zeropad && !flag_leftjustify && length < width){ - int i; - int nPad = width - length; - for(i=width; i>=nPad; i--){ - bufpt[i] = bufpt[i-nPad]; - } - i = prefix!=0; - while( nPad-- ) bufpt[i++] = '0'; - length = width; - } -#else - bufpt = " "; - length = (int)sizeof(" ") - 1; -#endif /* SX_OMIT_FLOATINGPOINT */ - break; - case SXFMT_SIZE:{ - int *pSize = va_arg(ap, int *); - *pSize = ((SyFmtConsumer *)pUserData)->nLen; - length = width = 0; - } - break; - case SXFMT_PERCENT: - buf[0] = '%'; - bufpt = buf; - length = 1; - break; - case SXFMT_CHARX: - c = va_arg(ap, int); - buf[0] = (char)c; - /* Limit the precision to prevent overflowing buf[] during conversion */ - if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40; - if( precision>=0 ){ - for(idx=1; idx=0 && precisionzString == 0 ){ - bufpt = " "; - length = (int)sizeof(char); - break; - } - bufpt = (char *)pStr->zString; - length = (int)pStr->nByte; - break; - } - case SXFMT_ERROR: - buf[0] = '?'; - bufpt = buf; - length = (int)sizeof(char); - if( c==0 ) zFormat--; - break; - }/* End switch over the format type */ - /* - ** The text of the conversion is pointed to by "bufpt" and is - ** "length" characters long.The field width is "width".Do - ** the output. - */ - if( !flag_leftjustify ){ - register int nspace; - nspace = width-length; - if( nspace>0 ){ - while( nspace>=etSPACESIZE ){ - rc = xConsumer(spaces, etSPACESIZE, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - nspace -= etSPACESIZE; - } - if( nspace>0 ){ - rc = xConsumer(spaces, (unsigned int)nspace, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - } - } - } - if( length>0 ){ - rc = xConsumer(bufpt, (unsigned int)length, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - } - if( flag_leftjustify ){ - register int nspace; - nspace = width-length; - if( nspace>0 ){ - while( nspace>=etSPACESIZE ){ - rc = xConsumer(spaces, etSPACESIZE, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - nspace -= etSPACESIZE; - } - if( nspace>0 ){ - rc = xConsumer(spaces, (unsigned int)nspace, pUserData); - if( rc != SXRET_OK ){ - return SXERR_ABORT; /* Consumer routine request an operation abort */ - } - } - } - } - }/* End for loop over the format string */ - return errorflag ? SXERR_FORMAT : SXRET_OK; -} -static sxi32 FormatConsumer(const void *pSrc, unsigned int nLen, void *pData) -{ - SyFmtConsumer *pConsumer = (SyFmtConsumer *)pData; - sxi32 rc = SXERR_ABORT; - switch(pConsumer->nType){ - case SXFMT_CONS_PROC: - /* User callback */ - rc = pConsumer->uConsumer.sFunc.xUserConsumer(pSrc, nLen, pConsumer->uConsumer.sFunc.pUserData); - break; - case SXFMT_CONS_BLOB: - /* Blob consumer */ - rc = SyBlobAppend(pConsumer->uConsumer.pBlob, pSrc, (sxu32)nLen); - break; - default: - /* Unknown consumer */ - break; - } - /* Update total number of bytes consumed so far */ - pConsumer->nLen += nLen; - pConsumer->rc = rc; - return rc; -} -static sxi32 FormatMount(sxi32 nType, void *pConsumer, ProcConsumer xUserCons, void *pUserData, sxu32 *pOutLen, const char *zFormat, va_list ap) -{ - SyFmtConsumer sCons; - sCons.nType = nType; - sCons.rc = SXRET_OK; - sCons.nLen = 0; - if( pOutLen ){ - *pOutLen = 0; - } - switch(nType){ - case SXFMT_CONS_PROC: -#if defined(UNTRUST) - if( xUserCons == 0 ){ - return SXERR_EMPTY; - } -#endif - sCons.uConsumer.sFunc.xUserConsumer = xUserCons; - sCons.uConsumer.sFunc.pUserData = pUserData; - break; - case SXFMT_CONS_BLOB: - sCons.uConsumer.pBlob = (SyBlob *)pConsumer; - break; - default: - return SXERR_UNKNOWN; - } - InternFormat(FormatConsumer, &sCons, zFormat, ap); - if( pOutLen ){ - *pOutLen = sCons.nLen; - } - return sCons.rc; -} -JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...) -{ - va_list ap; - sxi32 rc; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zFormat) ){ - return SXERR_EMPTY; - } -#endif - va_start(ap, zFormat); - rc = FormatMount(SXFMT_CONS_PROC, 0, xConsumer, pData, 0, zFormat, ap); - va_end(ap); - return rc; -} -JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...) -{ - va_list ap; - sxu32 n; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zFormat) ){ - return 0; - } -#endif - va_start(ap, zFormat); - FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap); - va_end(ap); - return n; -} -JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap) -{ - sxu32 n = 0; /* cc warning */ -#if defined(UNTRUST) - if( SX_EMPTY_STR(zFormat) ){ - return 0; - } -#endif - FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap); - return n; -} -JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...) -{ - SyBlob sBlob; - va_list ap; - sxu32 n; -#if defined(UNTRUST) - if( SX_EMPTY_STR(zFormat) ){ - return 0; - } -#endif - if( SXRET_OK != SyBlobInitFromBuf(&sBlob, zBuf, nLen - 1) ){ - return 0; - } - va_start(ap, zFormat); - FormatMount(SXFMT_CONS_BLOB, &sBlob, 0, 0, 0, zFormat, ap); - va_end(ap); - n = SyBlobLength(&sBlob); - /* Append the null terminator */ - sBlob.mByte++; - SyBlobAppend(&sBlob, "\0", sizeof(char)); - return n; -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -/* - * Zip File Format: - * - * Byte order: Little-endian - * - * [Local file header + Compressed data [+ Extended local header]?]* - * [Central directory]* - * [End of central directory record] - * - * Local file header:* - * Offset Length Contents - * 0 4 bytes Local file header signature (0x04034b50) - * 4 2 bytes Version needed to extract - * 6 2 bytes General purpose bit flag - * 8 2 bytes Compression method - * 10 2 bytes Last mod file time - * 12 2 bytes Last mod file date - * 14 4 bytes CRC-32 - * 18 4 bytes Compressed size (n) - * 22 4 bytes Uncompressed size - * 26 2 bytes Filename length (f) - * 28 2 bytes Extra field length (e) - * 30 (f)bytes Filename - * (e)bytes Extra field - * (n)bytes Compressed data - * - * Extended local header:* - * Offset Length Contents - * 0 4 bytes Extended Local file header signature (0x08074b50) - * 4 4 bytes CRC-32 - * 8 4 bytes Compressed size - * 12 4 bytes Uncompressed size - * - * Extra field:?(if any) - * Offset Length Contents - * 0 2 bytes Header ID (0x001 until 0xfb4a) see extended appnote from Info-zip - * 2 2 bytes Data size (g) - * (g) bytes (g) bytes of extra field - * - * Central directory:* - * Offset Length Contents - * 0 4 bytes Central file header signature (0x02014b50) - * 4 2 bytes Version made by - * 6 2 bytes Version needed to extract - * 8 2 bytes General purpose bit flag - * 10 2 bytes Compression method - * 12 2 bytes Last mod file time - * 14 2 bytes Last mod file date - * 16 4 bytes CRC-32 - * 20 4 bytes Compressed size - * 24 4 bytes Uncompressed size - * 28 2 bytes Filename length (f) - * 30 2 bytes Extra field length (e) - * 32 2 bytes File comment length (c) - * 34 2 bytes Disk number start - * 36 2 bytes Internal file attributes - * 38 4 bytes External file attributes - * 42 4 bytes Relative offset of local header - * 46 (f)bytes Filename - * (e)bytes Extra field - * (c)bytes File comment - * - * End of central directory record: - * Offset Length Contents - * 0 4 bytes End of central dir signature (0x06054b50) - * 4 2 bytes Number of this disk - * 6 2 bytes Number of the disk with the start of the central directory - * 8 2 bytes Total number of entries in the central dir on this disk - * 10 2 bytes Total number of entries in the central dir - * 12 4 bytes Size of the central directory - * 16 4 bytes Offset of start of central directory with respect to the starting disk number - * 20 2 bytes zipfile comment length (c) - * 22 (c)bytes zipfile comment - * - * compression method: (2 bytes) - * 0 - The file is stored (no compression) - * 1 - The file is Shrunk - * 2 - The file is Reduced with compression factor 1 - * 3 - The file is Reduced with compression factor 2 - * 4 - The file is Reduced with compression factor 3 - * 5 - The file is Reduced with compression factor 4 - * 6 - The file is Imploded - * 7 - Reserved for Tokenizing compression algorithm - * 8 - The file is Deflated - */ - -#define SXMAKE_ZIP_WORKBUF (SXU16_HIGH/2) /* 32KB Initial working buffer size */ -#define SXMAKE_ZIP_EXTRACT_VER 0x000a /* Version needed to extract */ -#define SXMAKE_ZIP_VER 0x003 /* Version made by */ - -#define SXZIP_CENTRAL_MAGIC 0x02014b50 -#define SXZIP_END_CENTRAL_MAGIC 0x06054b50 -#define SXZIP_LOCAL_MAGIC 0x04034b50 -/*#define SXZIP_CRC32_START 0xdebb20e3*/ - -#define SXZIP_LOCAL_HDRSZ 30 /* Local header size */ -#define SXZIP_LOCAL_EXT_HDRZ 16 /* Extended local header(footer) size */ -#define SXZIP_CENTRAL_HDRSZ 46 /* Central directory header size */ -#define SXZIP_END_CENTRAL_HDRSZ 22 /* End of central directory header size */ - -#define SXARCHIVE_HASH_SIZE 64 /* Starting hash table size(MUST BE POWER OF 2)*/ -static sxi32 SyLittleEndianUnpack32(sxu32 *uNB, const unsigned char *buf, sxu32 Len) -{ - if( Len < sizeof(sxu32) ){ - return SXERR_SHORT; - } - *uNB = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24); - return SXRET_OK; -} -static sxi32 SyLittleEndianUnpack16(sxu16 *pOut, const unsigned char *zBuf, sxu32 nLen) -{ - if( nLen < sizeof(sxu16) ){ - return SXERR_SHORT; - } - *pOut = zBuf[0] + (zBuf[1] <<8); - - return SXRET_OK; -} -/* - * Archive hashtable manager - */ -static sxi32 ArchiveHashGetEntry(SyArchive *pArch, const char *zName, sxu32 nLen, SyArchiveEntry **ppEntry) -{ - SyArchiveEntry *pBucketEntry; - SyString sEntry; - sxu32 nHash; - - nHash = pArch->xHash(zName, nLen); - pBucketEntry = pArch->apHash[nHash & (pArch->nSize - 1)]; - - SyStringInitFromBuf(&sEntry, zName, nLen); - - for(;;){ - if( pBucketEntry == 0 ){ - break; - } - if( nHash == pBucketEntry->nHash && pArch->xCmp(&sEntry, &pBucketEntry->sFileName) == 0 ){ - if( ppEntry ){ - *ppEntry = pBucketEntry; - } - return SXRET_OK; - } - pBucketEntry = pBucketEntry->pNextHash; - } - return SXERR_NOTFOUND; -} -static void ArchiveHashBucketInstall(SyArchiveEntry **apTable, sxu32 nBucket, SyArchiveEntry *pEntry) -{ - pEntry->pNextHash = apTable[nBucket]; - if( apTable[nBucket] != 0 ){ - apTable[nBucket]->pPrevHash = pEntry; - } - apTable[nBucket] = pEntry; -} -static sxi32 ArchiveHashGrowTable(SyArchive *pArch) -{ - sxu32 nNewSize = pArch->nSize * 2; - SyArchiveEntry **apNew; - SyArchiveEntry *pEntry; - sxu32 n; - - /* Allocate a new table */ - apNew = (SyArchiveEntry **)SyMemBackendAlloc(pArch->pAllocator, nNewSize * sizeof(SyArchiveEntry *)); - if( apNew == 0 ){ - return SXRET_OK; /* Not so fatal, simply a performance hit */ - } - SyZero(apNew, nNewSize * sizeof(SyArchiveEntry *)); - /* Rehash old entries */ - for( n = 0 , pEntry = pArch->pList ; n < pArch->nLoaded ; n++ , pEntry = pEntry->pNext ){ - pEntry->pNextHash = pEntry->pPrevHash = 0; - ArchiveHashBucketInstall(apNew, pEntry->nHash & (nNewSize - 1), pEntry); - } - /* Release the old table */ - SyMemBackendFree(pArch->pAllocator, pArch->apHash); - pArch->apHash = apNew; - pArch->nSize = nNewSize; - - return SXRET_OK; -} -static sxi32 ArchiveHashInstallEntry(SyArchive *pArch, SyArchiveEntry *pEntry) -{ - if( pArch->nLoaded > pArch->nSize * 3 ){ - ArchiveHashGrowTable(&(*pArch)); - } - pEntry->nHash = pArch->xHash(SyStringData(&pEntry->sFileName), SyStringLength(&pEntry->sFileName)); - /* Install the entry in its bucket */ - ArchiveHashBucketInstall(pArch->apHash, pEntry->nHash & (pArch->nSize - 1), pEntry); - MACRO_LD_PUSH(pArch->pList, pEntry); - pArch->nLoaded++; - - return SXRET_OK; -} - /* - * Parse the End of central directory and report status - */ - static sxi32 ParseEndOfCentralDirectory(SyArchive *pArch, const unsigned char *zBuf) - { - sxu32 nMagic = 0; /* cc -O6 warning */ - sxi32 rc; - - /* Sanity check */ - rc = SyLittleEndianUnpack32(&nMagic, zBuf, sizeof(sxu32)); - if( /* rc != SXRET_OK || */nMagic != SXZIP_END_CENTRAL_MAGIC ){ - return SXERR_CORRUPT; - } - /* # of entries */ - rc = SyLittleEndianUnpack16((sxu16 *)&pArch->nEntry, &zBuf[8], sizeof(sxu16)); - if( /* rc != SXRET_OK || */ pArch->nEntry > SXI16_HIGH /* SXU16_HIGH */ ){ - return SXERR_CORRUPT; - } - /* Size of central directory */ - rc = SyLittleEndianUnpack32(&pArch->nCentralSize, &zBuf[12], sizeof(sxu32)); - if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){ - return SXERR_CORRUPT; - } - /* Starting offset of central directory */ - rc = SyLittleEndianUnpack32(&pArch->nCentralOfft, &zBuf[16], sizeof(sxu32)); - if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){ - return SXERR_CORRUPT; - } - - return SXRET_OK; - } - /* - * Fill the zip entry with the appropriate information from the central directory - */ -static sxi32 GetCentralDirectoryEntry(SyArchive *pArch, SyArchiveEntry *pEntry, const unsigned char *zCentral, sxu32 *pNextOffset) - { - SyString *pName = &pEntry->sFileName; /* File name */ - sxu16 nDosDate, nDosTime; - sxu16 nComment = 0 ; - sxu32 nMagic = 0; /* cc -O6 warning */ - sxi32 rc; - nDosDate = nDosTime = 0; /* cc -O6 warning */ - SXUNUSED(pArch); - /* Sanity check */ - rc = SyLittleEndianUnpack32(&nMagic, zCentral, sizeof(sxu32)); - if( /* rc != SXRET_OK || */ nMagic != SXZIP_CENTRAL_MAGIC ){ - rc = SXERR_CORRUPT; - /* - * Try to recover by examing the next central directory record. - * Dont worry here, there is no risk of an infinite loop since - * the buffer size is delimited. - */ - - /* pName->nByte = 0; nComment = 0; pName->nExtra = 0 */ - goto update; - } - /* - * entry name length - */ - SyLittleEndianUnpack16((sxu16 *)&pName->nByte, &zCentral[28], sizeof(sxu16)); - if( pName->nByte > SXI16_HIGH /* SXU16_HIGH */){ - rc = SXERR_BIG; - goto update; - } - /* Extra information */ - SyLittleEndianUnpack16(&pEntry->nExtra, &zCentral[30], sizeof(sxu16)); - /* Comment length */ - SyLittleEndianUnpack16(&nComment, &zCentral[32], sizeof(sxu16)); - /* Compression method 0 == stored / 8 == deflated */ - rc = SyLittleEndianUnpack16(&pEntry->nComprMeth, &zCentral[10], sizeof(sxu16)); - /* DOS Timestamp */ - SyLittleEndianUnpack16(&nDosTime, &zCentral[12], sizeof(sxu16)); - SyLittleEndianUnpack16(&nDosDate, &zCentral[14], sizeof(sxu16)); - SyDosTimeFormat((nDosDate << 16 | nDosTime), &pEntry->sFmt); - /* Little hack to fix month index */ - pEntry->sFmt.tm_mon--; - /* CRC32 */ - rc = SyLittleEndianUnpack32(&pEntry->nCrc, &zCentral[16], sizeof(sxu32)); - /* Content size before compression */ - rc = SyLittleEndianUnpack32(&pEntry->nByte, &zCentral[24], sizeof(sxu32)); - if( pEntry->nByte > SXI32_HIGH ){ - rc = SXERR_BIG; - goto update; - } - /* - * Content size after compression. - * Note that if the file is stored pEntry->nByte should be equal to pEntry->nByteCompr - */ - rc = SyLittleEndianUnpack32(&pEntry->nByteCompr, &zCentral[20], sizeof(sxu32)); - if( pEntry->nByteCompr > SXI32_HIGH ){ - rc = SXERR_BIG; - goto update; - } - /* Finally grab the contents offset */ - SyLittleEndianUnpack32(&pEntry->nOfft, &zCentral[42], sizeof(sxu32)); - if( pEntry->nOfft > SXI32_HIGH ){ - rc = SXERR_BIG; - goto update; - } - rc = SXRET_OK; -update: - /* Update the offset to point to the next central directory record */ - *pNextOffset = SXZIP_CENTRAL_HDRSZ + pName->nByte + pEntry->nExtra + nComment; - return rc; /* Report failure or success */ -} -static sxi32 ZipFixOffset(SyArchiveEntry *pEntry, void *pSrc) -{ - sxu16 nExtra, nNameLen; - unsigned char *zHdr; - nExtra = nNameLen = 0; - zHdr = (unsigned char *)pSrc; - zHdr = &zHdr[pEntry->nOfft]; - if( SyMemcmp(zHdr, "PK\003\004", sizeof(sxu32)) != 0 ){ - return SXERR_CORRUPT; - } - SyLittleEndianUnpack16(&nNameLen, &zHdr[26], sizeof(sxu16)); - SyLittleEndianUnpack16(&nExtra, &zHdr[28], sizeof(sxu16)); - /* Fix contents offset */ - pEntry->nOfft += SXZIP_LOCAL_HDRSZ + nExtra + nNameLen; - return SXRET_OK; -} -/* - * Extract all valid entries from the central directory - */ -static sxi32 ZipExtract(SyArchive *pArch, const unsigned char *zCentral, sxu32 nLen, void *pSrc) -{ - SyArchiveEntry *pEntry, *pDup; - const unsigned char *zEnd ; /* End of central directory */ - sxu32 nIncr, nOfft; /* Central Offset */ - SyString *pName; /* Entry name */ - char *zName; - sxi32 rc; - - nOfft = nIncr = 0; - zEnd = &zCentral[nLen]; - - for(;;){ - if( &zCentral[nOfft] >= zEnd ){ - break; - } - /* Add a new entry */ - pEntry = (SyArchiveEntry *)SyMemBackendPoolAlloc(pArch->pAllocator, sizeof(SyArchiveEntry)); - if( pEntry == 0 ){ - break; - } - SyZero(pEntry, sizeof(SyArchiveEntry)); - pEntry->nMagic = SXARCH_MAGIC; - nIncr = 0; - rc = GetCentralDirectoryEntry(&(*pArch), pEntry, &zCentral[nOfft], &nIncr); - if( rc == SXRET_OK ){ - /* Fix the starting record offset so we can access entry contents correctly */ - rc = ZipFixOffset(pEntry, pSrc); - } - if(rc != SXRET_OK ){ - sxu32 nJmp = 0; - SyMemBackendPoolFree(pArch->pAllocator, pEntry); - /* Try to recover by brute-forcing for a valid central directory record */ - if( SXRET_OK == SyBlobSearch((const void *)&zCentral[nOfft + nIncr], (sxu32)(zEnd - &zCentral[nOfft + nIncr]), - (const void *)"PK\001\002", sizeof(sxu32), &nJmp)){ - nOfft += nIncr + nJmp; /* Check next entry */ - continue; - } - break; /* Giving up, archive is hopelessly corrupted */ - } - pName = &pEntry->sFileName; - pName->zString = (const char *)&zCentral[nOfft + SXZIP_CENTRAL_HDRSZ]; - if( pName->nByte <= 0 || ( pEntry->nByte <= 0 && pName->zString[pName->nByte - 1] != '/') ){ - /* Ignore zero length records (except folders) and records without names */ - SyMemBackendPoolFree(pArch->pAllocator, pEntry); - nOfft += nIncr; /* Check next entry */ - continue; - } - zName = SyMemBackendStrDup(pArch->pAllocator, pName->zString, pName->nByte); - if( zName == 0 ){ - SyMemBackendPoolFree(pArch->pAllocator, pEntry); - nOfft += nIncr; /* Check next entry */ - continue; - } - pName->zString = (const char *)zName; - /* Check for duplicates */ - rc = ArchiveHashGetEntry(&(*pArch), pName->zString, pName->nByte, &pDup); - if( rc == SXRET_OK ){ - /* Another entry with the same name exists ; link them together */ - pEntry->pNextName = pDup->pNextName; - pDup->pNextName = pEntry; - pDup->nDup++; - }else{ - /* Insert in hashtable */ - ArchiveHashInstallEntry(pArch, pEntry); - } - nOfft += nIncr; /* Check next record */ - } - pArch->pCursor = pArch->pList; - - return pArch->nLoaded > 0 ? SXRET_OK : SXERR_EMPTY; -} -JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen) - { - const unsigned char *zCentral, *zEnd; - sxi32 rc; -#if defined(UNTRUST) - if( SXARCH_INVALID(pArch) || zBuf == 0 ){ - return SXERR_INVALID; - } -#endif - /* The miminal size of a zip archive: - * LOCAL_HDR_SZ + CENTRAL_HDR_SZ + END_OF_CENTRAL_HDR_SZ - * 30 46 22 - */ - if( nLen < SXZIP_LOCAL_HDRSZ + SXZIP_CENTRAL_HDRSZ + SXZIP_END_CENTRAL_HDRSZ ){ - return SXERR_CORRUPT; /* Don't bother processing return immediately */ - } - - zEnd = (unsigned char *)&zBuf[nLen - SXZIP_END_CENTRAL_HDRSZ]; - /* Find the end of central directory */ - while( ((sxu32)((unsigned char *)&zBuf[nLen] - zEnd) < (SXZIP_END_CENTRAL_HDRSZ + SXI16_HIGH)) && - zEnd > (unsigned char *)zBuf && SyMemcmp(zEnd, "PK\005\006", sizeof(sxu32)) != 0 ){ - zEnd--; - } - /* Parse the end of central directory */ - rc = ParseEndOfCentralDirectory(&(*pArch), zEnd); - if( rc != SXRET_OK ){ - return rc; - } - - /* Find the starting offset of the central directory */ - zCentral = &zEnd[-(sxi32)pArch->nCentralSize]; - if( zCentral <= (unsigned char *)zBuf || SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){ - if( pArch->nCentralOfft >= nLen ){ - /* Corrupted central directory offset */ - return SXERR_CORRUPT; - } - zCentral = (unsigned char *)&zBuf[pArch->nCentralOfft]; - if( SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){ - /* Corrupted zip archive */ - return SXERR_CORRUPT; - } - /* Fall thru and extract all valid entries from the central directory */ - } - rc = ZipExtract(&(*pArch), zCentral, (sxu32)(zEnd - zCentral), (void *)zBuf); - return rc; - } -/* - * Default comparison function. - */ - static sxi32 ArchiveHashCmp(const SyString *pStr1, const SyString *pStr2) - { - sxi32 rc; - rc = SyStringCmp(pStr1, pStr2, SyMemcmp); - return rc; - } -JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp) - { - SyArchiveEntry **apHash; -#if defined(UNTRUST) - if( pArch == 0 ){ - return SXERR_EMPTY; - } -#endif - SyZero(pArch, sizeof(SyArchive)); - /* Allocate a new hashtable */ - apHash = (SyArchiveEntry **)SyMemBackendAlloc(&(*pAllocator), SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *)); - if( apHash == 0){ - return SXERR_MEM; - } - SyZero(apHash, SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *)); - pArch->apHash = apHash; - pArch->xHash = xHash ? xHash : SyBinHash; - pArch->xCmp = xCmp ? xCmp : ArchiveHashCmp; - pArch->nSize = SXARCHIVE_HASH_SIZE; - pArch->pAllocator = &(*pAllocator); - pArch->nMagic = SXARCH_MAGIC; - return SXRET_OK; - } - static sxi32 ArchiveReleaseEntry(SyMemBackend *pAllocator, SyArchiveEntry *pEntry) - { - SyArchiveEntry *pDup = pEntry->pNextName; - SyArchiveEntry *pNextDup; - - /* Release duplicates first since there are not stored in the hashtable */ - for(;;){ - if( pEntry->nDup == 0 ){ - break; - } - pNextDup = pDup->pNextName; - pDup->nMagic = 0x2661; - SyMemBackendFree(pAllocator, (void *)SyStringData(&pDup->sFileName)); - SyMemBackendPoolFree(pAllocator, pDup); - pDup = pNextDup; - pEntry->nDup--; - } - pEntry->nMagic = 0x2661; - SyMemBackendFree(pAllocator, (void *)SyStringData(&pEntry->sFileName)); - SyMemBackendPoolFree(pAllocator, pEntry); - return SXRET_OK; - } -JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch) - { - SyArchiveEntry *pEntry, *pNext; - pEntry = pArch->pList; - for(;;){ - if( pArch->nLoaded < 1 ){ - break; - } - pNext = pEntry->pNext; - MACRO_LD_REMOVE(pArch->pList, pEntry); - ArchiveReleaseEntry(pArch->pAllocator, pEntry); - pEntry = pNext; - pArch->nLoaded--; - } - SyMemBackendFree(pArch->pAllocator, pArch->apHash); - pArch->pCursor = 0; - pArch->nMagic = 0x2626; - return SXRET_OK; - } - JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch) - { - pArch->pCursor = pArch->pList; - return SXRET_OK; - } - JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry) - { - SyArchiveEntry *pNext; - if( pArch->pCursor == 0 ){ - /* Rewind the cursor */ - pArch->pCursor = pArch->pList; - return SXERR_EOF; - } - *ppEntry = pArch->pCursor; - pNext = pArch->pCursor->pNext; - /* Advance the cursor to the next entry */ - pArch->pCursor = pNext; - return SXRET_OK; - } -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -/* - * Psuedo Random Number Generator (PRNG) - * @authors: SQLite authors - * @status: Public Domain - * NOTE: - * Nothing in this file or anywhere else in the library does any kind of - * encryption.The RC4 algorithm is being used as a PRNG (pseudo-random - * number generator) not as an encryption device. - */ -#define SXPRNG_MAGIC 0x13C4 -#ifdef __UNIXES__ -#include -#include -#include -#include -#include -#include -#include -#endif -static sxi32 SyOSUtilRandomSeed(void *pBuf, sxu32 nLen, void *pUnused) -{ - char *zBuf = (char *)pBuf; -#ifdef __WINNT__ - DWORD nProcessID; /* Yes, keep it uninitialized when compiling using the MinGW32 builds tools */ -#elif defined(__UNIXES__) - pid_t pid; - int fd; -#else - char zGarbage[128]; /* Yes, keep this buffer uninitialized */ -#endif - SXUNUSED(pUnused); -#ifdef __WINNT__ -#ifndef __MINGW32__ - nProcessID = GetProcessId(GetCurrentProcess()); -#endif - SyMemcpy((const void *)&nProcessID, zBuf, SXMIN(nLen, sizeof(DWORD))); - if( (sxu32)(&zBuf[nLen] - &zBuf[sizeof(DWORD)]) >= sizeof(SYSTEMTIME) ){ - GetSystemTime((LPSYSTEMTIME)&zBuf[sizeof(DWORD)]); - } -#elif defined(__UNIXES__) - fd = open("/dev/urandom", O_RDONLY); - if (fd >= 0 ){ - if( read(fd, zBuf, nLen) > 0 ){ - return SXRET_OK; - } - /* FALL THRU */ - } - pid = getpid(); - SyMemcpy((const void *)&pid, zBuf, SXMIN(nLen, sizeof(pid_t))); - if( &zBuf[nLen] - &zBuf[sizeof(pid_t)] >= (int)sizeof(struct timeval) ){ - gettimeofday((struct timeval *)&zBuf[sizeof(pid_t)], 0); - } -#else - /* Fill with uninitialized data */ - SyMemcpy(zGarbage, zBuf, SXMIN(nLen, sizeof(zGarbage))); -#endif - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void * pUserData) -{ - char zSeed[256]; - sxu8 t; - sxi32 rc; - sxu32 i; - if( pCtx->nMagic == SXPRNG_MAGIC ){ - return SXRET_OK; /* Already initialized */ - } - /* Initialize the state of the random number generator once, - ** the first time this routine is called.The seed value does - ** not need to contain a lot of randomness since we are not - ** trying to do secure encryption or anything like that... - */ - if( xSeed == 0 ){ - xSeed = SyOSUtilRandomSeed; - } - rc = xSeed(zSeed, sizeof(zSeed), pUserData); - if( rc != SXRET_OK ){ - return rc; - } - pCtx->i = pCtx->j = 0; - for(i=0; i < SX_ARRAYSIZE(pCtx->s) ; i++){ - pCtx->s[i] = (unsigned char)i; - } - for(i=0; i < sizeof(zSeed) ; i++){ - pCtx->j += pCtx->s[i] + zSeed[i]; - t = pCtx->s[pCtx->j]; - pCtx->s[pCtx->j] = pCtx->s[i]; - pCtx->s[i] = t; - } - pCtx->nMagic = SXPRNG_MAGIC; - - return SXRET_OK; -} -/* - * Get a single 8-bit random value using the RC4 PRNG. - */ -static sxu8 randomByte(SyPRNGCtx *pCtx) -{ - sxu8 t; - - /* Generate and return single random byte */ - pCtx->i++; - t = pCtx->s[pCtx->i]; - pCtx->j += t; - pCtx->s[pCtx->i] = pCtx->s[pCtx->j]; - pCtx->s[pCtx->j] = t; - t += pCtx->s[pCtx->i]; - return pCtx->s[t]; -} -JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen) -{ - unsigned char *zBuf = (unsigned char *)pBuf; - unsigned char *zEnd = &zBuf[nLen]; -#if defined(UNTRUST) - if( pCtx == 0 || pBuf == 0 || nLen <= 0 ){ - return SXERR_EMPTY; - } -#endif - if(pCtx->nMagic != SXPRNG_MAGIC ){ - return SXERR_CORRUPT; - } - for(;;){ - if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; - if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; - if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; - if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; - } - return SXRET_OK; -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -#ifndef JX9_DISABLE_HASH_FUNC -/* SyRunTimeApi: sxhash.c */ -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest.This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ -#define SX_MD5_BINSZ 16 -#define SX_MD5_HEXSZ 32 -/* - * Note: this code is harmless on little-endian machines. - */ -static void byteReverse (unsigned char *buf, unsigned longs) -{ - sxu32 t; - do { - t = (sxu32)((unsigned)buf[3]<<8 | buf[2]) << 16 | - ((unsigned)buf[1]<<8 | buf[0]); - *(sxu32*)buf = t; - buf += 4; - } while (--longs); -} -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#ifdef F1 -#undef F1 -#endif -#ifdef F2 -#undef F2 -#endif -#ifdef F3 -#undef F3 -#endif -#ifdef F4 -#undef F4 -#endif - -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm.*/ -#define SX_MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data.MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -static void MD5Transform(sxu32 buf[4], const sxu32 in[16]) -{ - register sxu32 a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - SX_MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); - SX_MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); - SX_MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); - SX_MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); - SX_MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); - SX_MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); - SX_MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); - SX_MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); - SX_MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); - SX_MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); - SX_MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); - SX_MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); - SX_MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); - SX_MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); - SX_MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); - SX_MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); - - SX_MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); - SX_MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); - SX_MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); - SX_MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); - SX_MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); - SX_MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); - SX_MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); - SX_MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); - SX_MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); - SX_MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); - SX_MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); - SX_MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); - SX_MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); - SX_MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); - SX_MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); - SX_MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); - - SX_MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); - SX_MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); - SX_MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); - SX_MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); - SX_MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); - SX_MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); - SX_MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); - SX_MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); - SX_MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); - SX_MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); - SX_MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); - SX_MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); - SX_MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); - SX_MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); - SX_MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); - SX_MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); - - SX_MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); - SX_MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); - SX_MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); - SX_MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); - SX_MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); - SX_MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); - SX_MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); - SX_MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); - SX_MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); - SX_MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); - SX_MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); - SX_MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); - SX_MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); - SX_MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); - SX_MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); - SX_MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len) -{ - sxu32 t; - - /* Update bitcount */ - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((sxu32)len << 3)) < t) - ctx->bits[1]++; /* Carry from low to high */ - ctx->bits[1] += len >> 29; - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - /* Handle any leading odd-sized chunks */ - if ( t ) { - unsigned char *p = (unsigned char *)ctx->in + t; - - t = 64-t; - if (len < t) { - SyMemcpy(buf, p, len); - return; - } - SyMemcpy(buf, p, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (sxu32*)ctx->in); - buf += t; - len -= t; - } - /* Process data in 64-byte chunks */ - while (len >= 64) { - SyMemcpy(buf, ctx->in, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (sxu32*)ctx->in); - buf += 64; - len -= 64; - } - /* Handle any remaining bytes of data.*/ - SyMemcpy(buf, ctx->in, len); -} -/* - * Final wrapup - pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx){ - unsigned count; - unsigned char *p; - - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; - - /* Set the first char of padding to 0x80.This is safe since there is - always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - SyZero(p, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (sxu32*)ctx->in); - - /* Now fill the next block with 56 bytes */ - SyZero(ctx->in, 56); - } else { - /* Pad block to 56 bytes */ - SyZero(p, count-8); - } - byteReverse(ctx->in, 14); - - /* Append length in bits and transform */ - ((sxu32*)ctx->in)[ 14 ] = ctx->bits[0]; - ((sxu32*)ctx->in)[ 15 ] = ctx->bits[1]; - - MD5Transform(ctx->buf, (sxu32*)ctx->in); - byteReverse((unsigned char *)ctx->buf, 4); - SyMemcpy(ctx->buf, digest, 0x10); - SyZero(ctx, sizeof(ctx)); /* In case it's sensitive */ -} -#undef F1 -#undef F2 -#undef F3 -#undef F4 -JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx) -{ - pCtx->buf[0] = 0x67452301; - pCtx->buf[1] = 0xefcdab89; - pCtx->buf[2] = 0x98badcfe; - pCtx->buf[3] = 0x10325476; - pCtx->bits[0] = 0; - pCtx->bits[1] = 0; - - return SXRET_OK; -} -JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16]) -{ - MD5Context sCtx; - MD5Init(&sCtx); - MD5Update(&sCtx, (const unsigned char *)pIn, nLen); - MD5Final(zDigest, &sCtx); - return SXRET_OK; -} -/* - * SHA-1 in C - * By Steve Reid - * Status: Public Domain - */ -/* - * blk0() and blk() perform the initial expand. - * I got the idea of expanding during the round function from SSLeay - * - * blk0le() for little-endian and blk0be() for big-endian. - */ -#if __GNUC__ && (defined(__i386__) || defined(__x86_64__)) -/* - * GCC by itself only generates left rotates. Use right rotates if - * possible to be kinder to dinky implementations with iterative rotate - * instructions. - */ -#define SHA_ROT(op, x, k) \ - ({ unsigned int y; asm(op " %1, %0" : "=r" (y) : "I" (k), "0" (x)); y; }) -#define rol(x, k) SHA_ROT("roll", x, k) -#define ror(x, k) SHA_ROT("rorl", x, k) - -#else -/* Generic C equivalent */ -#define SHA_ROT(x, l, r) ((x) << (l) | (x) >> (r)) -#define rol(x, k) SHA_ROT(x, k, 32-(k)) -#define ror(x, k) SHA_ROT(x, 32-(k), k) -#endif - -#define blk0le(i) (block[i] = (ror(block[i], 8)&0xFF00FF00) \ - |(rol(block[i], 8)&0x00FF00FF)) -#define blk0be(i) block[i] -#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ - ^block[(i+2)&15]^block[i&15], 1)) - -/* - * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 - * - * Rl0() for little-endian and Rb0() for big-endian. Endianness is - * determined at run-time. - */ -#define Rl0(v, w, x, y, z, i) \ - z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v, 5);w=ror(w, 2); -#define Rb0(v, w, x, y, z, i) \ - z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v, 5);w=ror(w, 2); -#define R1(v, w, x, y, z, i) \ - z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v, 5);w=ror(w, 2); -#define R2(v, w, x, y, z, i) \ - z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v, 5);w=ror(w, 2); -#define R3(v, w, x, y, z, i) \ - z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v, 5);w=ror(w, 2); -#define R4(v, w, x, y, z, i) \ - z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v, 5);w=ror(w, 2); - -/* - * Hash a single 512-bit block. This is the core of the algorithm. - */ -#define a qq[0] -#define b qq[1] -#define c qq[2] -#define d qq[3] -#define e qq[4] - -static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]) -{ - unsigned int qq[5]; /* a, b, c, d, e; */ - static int one = 1; - unsigned int block[16]; - SyMemcpy(buffer, (void *)block, 64); - SyMemcpy(state, qq, 5*sizeof(unsigned int)); - - /* Copy context->state[] to working vars */ - /* - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - */ - - /* 4 rounds of 20 operations each. Loop unrolled. */ - if( 1 == *(unsigned char*)&one ){ - Rl0(a, b, c, d, e, 0); Rl0(e, a, b, c, d, 1); Rl0(d, e, a, b, c, 2); Rl0(c, d, e, a, b, 3); - Rl0(b, c, d, e, a, 4); Rl0(a, b, c, d, e, 5); Rl0(e, a, b, c, d, 6); Rl0(d, e, a, b, c, 7); - Rl0(c, d, e, a, b, 8); Rl0(b, c, d, e, a, 9); Rl0(a, b, c, d, e, 10); Rl0(e, a, b, c, d, 11); - Rl0(d, e, a, b, c, 12); Rl0(c, d, e, a, b, 13); Rl0(b, c, d, e, a, 14); Rl0(a, b, c, d, e, 15); - }else{ - Rb0(a, b, c, d, e, 0); Rb0(e, a, b, c, d, 1); Rb0(d, e, a, b, c, 2); Rb0(c, d, e, a, b, 3); - Rb0(b, c, d, e, a, 4); Rb0(a, b, c, d, e, 5); Rb0(e, a, b, c, d, 6); Rb0(d, e, a, b, c, 7); - Rb0(c, d, e, a, b, 8); Rb0(b, c, d, e, a, 9); Rb0(a, b, c, d, e, 10); Rb0(e, a, b, c, d, 11); - Rb0(d, e, a, b, c, 12); Rb0(c, d, e, a, b, 13); Rb0(b, c, d, e, a, 14); Rb0(a, b, c, d, e, 15); - } - R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19); - R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23); - R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27); - R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31); - R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35); - R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39); - R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43); - R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47); - R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51); - R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55); - R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59); - R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63); - R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67); - R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71); - R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75); - R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79); - - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; -} -#undef a -#undef b -#undef c -#undef d -#undef e -/* - * SHA1Init - Initialize new context - */ -JX9_PRIVATE void SHA1Init(SHA1Context *context){ - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} -/* - * Run your data through this. - */ -JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len){ - unsigned int i, j; - - j = context->count[0]; - if ((context->count[0] += len << 3) < j) - context->count[1] += (len>>29)+1; - j = (j >> 3) & 63; - if ((j + len) > 63) { - (void)SyMemcpy(data, &context->buffer[j], (i = 64-j)); - SHA1Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) - SHA1Transform(context->state, &data[i]); - j = 0; - } else { - i = 0; - } - (void)SyMemcpy(&data[i], &context->buffer[j], len - i); -} -/* - * Add padding and return the message digest. - */ -JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]){ - unsigned int i; - unsigned char finalcount[8]; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ - } - SHA1Update(context, (const unsigned char *)"\200", 1); - while ((context->count[0] & 504) != 448) - SHA1Update(context, (const unsigned char *)"\0", 1); - SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ - - if (digest) { - for (i = 0; i < 20; i++) - digest[i] = (unsigned char) - ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); - } -} -#undef Rl0 -#undef Rb0 -#undef R1 -#undef R2 -#undef R3 -#undef R4 - -JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20]) -{ - SHA1Context sCtx; - SHA1Init(&sCtx); - SHA1Update(&sCtx, (const unsigned char *)pIn, nLen); - SHA1Final(&sCtx, zDigest); - return SXRET_OK; -} -static const sxu32 crc32_table[] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; -#define CRC32C(c, d) (c = ( crc32_table[(c ^ (d)) & 0xFF] ^ (c>>8) ) ) -static sxu32 SyCrc32Update(sxu32 crc32, const void *pSrc, sxu32 nLen) -{ - register unsigned char *zIn = (unsigned char *)pSrc; - unsigned char *zEnd; - if( zIn == 0 ){ - return crc32; - } - zEnd = &zIn[nLen]; - for(;;){ - if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++; - if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++; - if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++; - if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++; - } - - return crc32; -} -JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen) -{ - return SyCrc32Update(SXU32_HIGH, pSrc, nLen); -} -#endif /* JX9_DISABLE_HASH_FUNC */ -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -#ifndef JX9_DISABLE_BUILTIN_FUNC -JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData) -{ - static const unsigned char zHexTab[] = "0123456789abcdef"; - const unsigned char *zIn, *zEnd; - unsigned char zOut[3]; - sxi32 rc; -#if defined(UNTRUST) - if( pIn == 0 || xConsumer == 0 ){ - return SXERR_EMPTY; - } -#endif - zIn = (const unsigned char *)pIn; - zEnd = &zIn[nLen]; - for(;;){ - if( zIn >= zEnd ){ - break; - } - zOut[0] = zHexTab[zIn[0] >> 4]; zOut[1] = zHexTab[zIn[0] & 0x0F]; - rc = xConsumer((const void *)zOut, sizeof(char)*2, pConsumerData); - if( rc != SXRET_OK ){ - return rc; - } - zIn++; - } - return SXRET_OK; -} -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -JX9_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb) -{ - buf[3] = nb & 0xFF ; nb >>=8; - buf[2] = nb & 0xFF ; nb >>=8; - buf[1] = nb & 0xFF ; nb >>=8; - buf[0] = (unsigned char)nb ; -} -JX9_PRIVATE void SyBigEndianUnpack32(const unsigned char *buf,sxu32 *uNB) -{ - *uNB = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24); -} -JX9_PRIVATE void SyBigEndianPack16(unsigned char *buf,sxu16 nb) -{ - buf[1] = nb & 0xFF ; nb >>=8; - buf[0] = (unsigned char)nb ; -} -JX9_PRIVATE void SyBigEndianUnpack16(const unsigned char *buf,sxu16 *uNB) -{ - *uNB = buf[1] + (buf[0] << 8); -} -JX9_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64) -{ - buf[7] = n64 & 0xFF; n64 >>=8; - buf[6] = n64 & 0xFF; n64 >>=8; - buf[5] = n64 & 0xFF; n64 >>=8; - buf[4] = n64 & 0xFF; n64 >>=8; - buf[3] = n64 & 0xFF; n64 >>=8; - buf[2] = n64 & 0xFF; n64 >>=8; - buf[1] = n64 & 0xFF; n64 >>=8; - buf[0] = (sxu8)n64 ; -} -JX9_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64) -{ - sxu32 u1,u2; - u1 = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24); - u2 = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24); - *n64 = (((sxu64)u2) << 32) | u1; -} -JX9_PRIVATE sxi32 SyBlobAppendBig64(SyBlob *pBlob,sxu64 n64) -{ - unsigned char zBuf[8]; - sxi32 rc; - SyBigEndianPack64(zBuf,n64); - rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf)); - return rc; -} -JX9_PRIVATE sxi32 SyBlobAppendBig32(SyBlob *pBlob,sxu32 n32) -{ - unsigned char zBuf[4]; - sxi32 rc; - SyBigEndianPack32(zBuf,n32); - rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf)); - return rc; -} -JX9_PRIVATE sxi32 SyBlobAppendBig16(SyBlob *pBlob,sxu16 n16) -{ - unsigned char zBuf[2]; - sxi32 rc; - SyBigEndianPack16(zBuf,n16); - rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf)); - return rc; -} -JX9_PRIVATE void SyTimeFormatToDos(Sytm *pFmt,sxu32 *pOut) -{ - sxi32 nDate,nTime; - nDate = ((pFmt->tm_year - 1980) << 9) + (pFmt->tm_mon << 5) + pFmt->tm_mday; - nTime = (pFmt->tm_hour << 11) + (pFmt->tm_min << 5)+ (pFmt->tm_sec >> 1); - *pOut = (nDate << 16) | nTime; -} -JX9_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut) -{ - sxu16 nDate; - sxu16 nTime; - nDate = nDosDate >> 16; - nTime = nDosDate & 0xFFFF; - pOut->tm_isdst = 0; - pOut->tm_year = 1980 + (nDate >> 9); - pOut->tm_mon = (nDate % (1<<9))>>5; - pOut->tm_mday = (nDate % (1<<9))&0x1F; - pOut->tm_hour = nTime >> 11; - pOut->tm_min = (nTime % (1<<11)) >> 5; - pOut->tm_sec = ((nTime % (1<<11))& 0x1F )<<1; -} -/* - * ---------------------------------------------------------- - * File: jx9_memobj.c - * MD5: 8692d7f4cb297c0946066b4a9034c637 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: memobj.c v2.7 FreeBSD 2012-08-09 03:40 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* This file manage low-level stuff related to indexed memory objects [i.e: jx9_value] */ -/* - * Notes on memory objects [i.e: jx9_value]. - * Internally, the JX9 virtual machine manipulates nearly all JX9 values - * [i.e: string, int, float, resource, object, bool, null..] as jx9_values structures. - * Each jx9_values struct may cache multiple representations (string, - * integer etc.) of the same value. - */ -/* - * Convert a 64-bit IEEE double into a 64-bit signed integer. - * If the double is too large, return 0x8000000000000000. - * - * Most systems appear to do this simply by assigning ariables and without - * the extra range tests. - * But there are reports that windows throws an expection if the floating - * point value is out of range. - */ -static sxi64 MemObjRealToInt(jx9_value *pObj) -{ -#ifdef JX9_OMIT_FLOATING_POINT - /* Real and 64bit integer are the same when floating point arithmetic - * is omitted from the build. - */ - return pObj->x.rVal; -#else - /* - ** Many compilers we encounter do not define constants for the - ** minimum and maximum 64-bit integers, or they define them - ** inconsistently. And many do not understand the "LL" notation. - ** So we define our own static constants here using nothing - ** larger than a 32-bit integer constant. - */ - static const sxi64 maxInt = LARGEST_INT64; - static const sxi64 minInt = SMALLEST_INT64; - jx9_real r = pObj->x.rVal; - if( r<(jx9_real)minInt ){ - return minInt; - }else if( r>(jx9_real)maxInt ){ - /* minInt is correct here - not maxInt. It turns out that assigning - ** a very large positive number to an integer results in a very large - ** negative integer. This makes no sense, but it is what x86 hardware - ** does so for compatibility we will do the same in software. */ - return minInt; - }else{ - return (sxi64)r; - } -#endif -} -/* - * Convert a raw token value typically a stream of digit [i.e: hex, octal, binary or decimal] - * to a 64-bit integer. - */ -JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pVal) -{ - sxi64 iVal = 0; - if( pVal->nByte <= 0 ){ - return 0; - } - if( pVal->zString[0] == '0' ){ - sxi32 c; - if( pVal->nByte == sizeof(char) ){ - return 0; - } - c = pVal->zString[1]; - if( c == 'x' || c == 'X' ){ - /* Hex digit stream */ - SyHexStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0); - }else if( c == 'b' || c == 'B' ){ - /* Binary digit stream */ - SyBinaryStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0); - }else{ - /* Octal digit stream */ - SyOctalStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0); - } - }else{ - /* Decimal digit stream */ - SyStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0); - } - return iVal; -} -/* - * Return some kind of 64-bit integer value which is the best we can - * do at representing the value that pObj describes as a string - * representation. - */ -static sxi64 MemObjStringToInt(jx9_value *pObj) -{ - SyString sVal; - SyStringInitFromBuf(&sVal, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); - return jx9TokenValueToInt64(&sVal); -} -/* - * Return some kind of integer value which is the best we can - * do at representing the value that pObj describes as an integer. - * If pObj is an integer, then the value is exact. If pObj is - * a floating-point then the value returned is the integer part. - * If pObj is a string, then we make an attempt to convert it into - * a integer and return that. - * If pObj represents a NULL value, return 0. - */ -static sxi64 MemObjIntValue(jx9_value *pObj) -{ - sxi32 iFlags; - iFlags = pObj->iFlags; - if (iFlags & MEMOBJ_REAL ){ - return MemObjRealToInt(&(*pObj)); - }else if( iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ - return pObj->x.iVal; - }else if (iFlags & MEMOBJ_STRING) { - return MemObjStringToInt(&(*pObj)); - }else if( iFlags & MEMOBJ_NULL ){ - return 0; - }else if( iFlags & MEMOBJ_HASHMAP ){ - jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther; - sxu32 n = pMap->nEntry; - jx9HashmapUnref(pMap); - /* Return total number of entries in the hashmap */ - return n; - }else if(iFlags & MEMOBJ_RES ){ - return pObj->x.pOther != 0; - } - /* CANT HAPPEN */ - return 0; -} -/* - * Return some kind of real value which is the best we can - * do at representing the value that pObj describes as a real. - * If pObj is a real, then the value is exact.If pObj is an - * integer then the integer is promoted to real and that value - * is returned. - * If pObj is a string, then we make an attempt to convert it - * into a real and return that. - * If pObj represents a NULL value, return 0.0 - */ -static jx9_real MemObjRealValue(jx9_value *pObj) -{ - sxi32 iFlags; - iFlags = pObj->iFlags; - if( iFlags & MEMOBJ_REAL ){ - return pObj->x.rVal; - }else if (iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ - return (jx9_real)pObj->x.iVal; - }else if (iFlags & MEMOBJ_STRING){ - SyString sString; -#ifdef JX9_OMIT_FLOATING_POINT - jx9_real rVal = 0; -#else - jx9_real rVal = 0.0; -#endif - SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); - if( SyBlobLength(&pObj->sBlob) > 0 ){ - /* Convert as much as we can */ -#ifdef JX9_OMIT_FLOATING_POINT - rVal = MemObjStringToInt(&(*pObj)); -#else - SyStrToReal(sString.zString, sString.nByte, (void *)&rVal, 0); -#endif - } - return rVal; - }else if( iFlags & MEMOBJ_NULL ){ -#ifdef JX9_OMIT_FLOATING_POINT - return 0; -#else - return 0.0; -#endif - }else if( iFlags & MEMOBJ_HASHMAP ){ - /* Return the total number of entries in the hashmap */ - jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther; - jx9_real n = (jx9_real)pMap->nEntry; - jx9HashmapUnref(pMap); - return n; - }else if(iFlags & MEMOBJ_RES ){ - return (jx9_real)(pObj->x.pOther != 0); - } - /* NOT REACHED */ - return 0; -} -/* - * Return the string representation of a given jx9_value. - * This function never fail and always return SXRET_OK. - */ -static sxi32 MemObjStringValue(SyBlob *pOut,jx9_value *pObj) -{ - if( pObj->iFlags & MEMOBJ_REAL ){ - SyBlobFormat(&(*pOut), "%.15g", pObj->x.rVal); - }else if( pObj->iFlags & MEMOBJ_INT ){ - SyBlobFormat(&(*pOut), "%qd", pObj->x.iVal); - /* %qd (BSD quad) is equivalent to %lld in the libc printf */ - }else if( pObj->iFlags & MEMOBJ_BOOL ){ - if( pObj->x.iVal ){ - SyBlobAppend(&(*pOut),"true", sizeof("true")-1); - }else{ - SyBlobAppend(&(*pOut),"false", sizeof("false")-1); - } - }else if( pObj->iFlags & MEMOBJ_HASHMAP ){ - /* Serialize JSON object or array */ - jx9JsonSerialize(pObj,pOut); - jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther); - }else if(pObj->iFlags & MEMOBJ_RES ){ - SyBlobFormat(&(*pOut), "ResourceID_%#x", pObj->x.pOther); - } - return SXRET_OK; -} -/* - * Return some kind of boolean value which is the best we can do - * at representing the value that pObj describes as a boolean. - * When converting to boolean, the following values are considered FALSE: - * NULL - * the boolean FALSE itself. - * the integer 0 (zero). - * the real 0.0 (zero). - * the empty string, a stream of zero [i.e: "0", "00", "000", ...] and the string - * "false". - * an array with zero elements. - */ -static sxi32 MemObjBooleanValue(jx9_value *pObj) -{ - sxi32 iFlags; - iFlags = pObj->iFlags; - if (iFlags & MEMOBJ_REAL ){ -#ifdef JX9_OMIT_FLOATING_POINT - return pObj->x.rVal ? 1 : 0; -#else - return pObj->x.rVal != 0.0 ? 1 : 0; -#endif - }else if( iFlags & MEMOBJ_INT ){ - return pObj->x.iVal ? 1 : 0; - }else if (iFlags & MEMOBJ_STRING) { - SyString sString; - SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); - if( sString.nByte == 0 ){ - /* Empty string */ - return 0; - }else if( (sString.nByte == sizeof("true") - 1 && SyStrnicmp(sString.zString, "true", sizeof("true")-1) == 0) || - (sString.nByte == sizeof("on") - 1 && SyStrnicmp(sString.zString, "on", sizeof("on")-1) == 0) || - (sString.nByte == sizeof("yes") - 1 && SyStrnicmp(sString.zString, "yes", sizeof("yes")-1) == 0) ){ - return 1; - }else if( sString.nByte == sizeof("false") - 1 && SyStrnicmp(sString.zString, "false", sizeof("false")-1) == 0 ){ - return 0; - }else{ - const char *zIn, *zEnd; - zIn = sString.zString; - zEnd = &zIn[sString.nByte]; - while( zIn < zEnd && zIn[0] == '0' ){ - zIn++; - } - return zIn >= zEnd ? 0 : 1; - } - }else if( iFlags & MEMOBJ_NULL ){ - return 0; - }else if( iFlags & MEMOBJ_HASHMAP ){ - jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther; - sxu32 n = pMap->nEntry; - jx9HashmapUnref(pMap); - return n > 0 ? TRUE : FALSE; - }else if(iFlags & MEMOBJ_RES ){ - return pObj->x.pOther != 0; - } - /* NOT REACHED */ - return 0; -} -/* - * If the jx9_value is of type real, try to make it an integer also. - */ -static sxi32 MemObjTryIntger(jx9_value *pObj) -{ - sxi64 iVal = MemObjRealToInt(&(*pObj)); - /* Only mark the value as an integer if - ** - ** (1) the round-trip conversion real->int->real is a no-op, and - ** (2) The integer is neither the largest nor the smallest - ** possible integer - ** - ** The second and third terms in the following conditional enforces - ** the second condition under the assumption that addition overflow causes - ** values to wrap around. On x86 hardware, the third term is always - ** true and could be omitted. But we leave it in because other - ** architectures might behave differently. - */ - if( pObj->x.rVal ==(jx9_real)iVal && iVal>SMALLEST_INT64 && iValx.iVal = iVal; - pObj->iFlags = MEMOBJ_INT; - } - return SXRET_OK; -} -/* - * Convert a jx9_value to type integer.Invalidate any prior representations. - */ -JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj) -{ - if( (pObj->iFlags & MEMOBJ_INT) == 0 ){ - /* Preform the conversion */ - pObj->x.iVal = MemObjIntValue(&(*pObj)); - /* Invalidate any prior representations */ - SyBlobRelease(&pObj->sBlob); - MemObjSetType(pObj, MEMOBJ_INT); - } - return SXRET_OK; -} -/* - * Convert a jx9_value to type real (Try to get an integer representation also). - * Invalidate any prior representations - */ -JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj) -{ - if((pObj->iFlags & MEMOBJ_REAL) == 0 ){ - /* Preform the conversion */ - pObj->x.rVal = MemObjRealValue(&(*pObj)); - /* Invalidate any prior representations */ - SyBlobRelease(&pObj->sBlob); - MemObjSetType(pObj, MEMOBJ_REAL); - } - return SXRET_OK; -} -/* - * Convert a jx9_value to type boolean.Invalidate any prior representations. - */ -JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj) -{ - if( (pObj->iFlags & MEMOBJ_BOOL) == 0 ){ - /* Preform the conversion */ - pObj->x.iVal = MemObjBooleanValue(&(*pObj)); - /* Invalidate any prior representations */ - SyBlobRelease(&pObj->sBlob); - MemObjSetType(pObj, MEMOBJ_BOOL); - } - return SXRET_OK; -} -/* - * Convert a jx9_value to type string.Prior representations are NOT invalidated. - */ -JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj) -{ - sxi32 rc = SXRET_OK; - if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){ - /* Perform the conversion */ - SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */ - rc = MemObjStringValue(&pObj->sBlob, &(*pObj)); - MemObjSetType(pObj, MEMOBJ_STRING); - } - return rc; -} -/* - * Nullify a jx9_value.In other words invalidate any prior - * representation. - */ -JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj) -{ - return jx9MemObjRelease(pObj); -} -/* - * Convert a jx9_value to type array.Invalidate any prior representations. - * According to the JX9 language reference manual. - * For any of the types: integer, float, string, boolean converting a value - * to an array results in an array with a single element with index zero - * and the value of the scalar which was converted. - */ -JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj) -{ - if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){ - jx9_hashmap *pMap; - /* Allocate a new hashmap instance */ - pMap = jx9NewHashmap(pObj->pVm, 0, 0); - if( pMap == 0 ){ - return SXERR_MEM; - } - if( (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){ - /* - * According to the JX9 language reference manual. - * For any of the types: integer, float, string, boolean converting a value - * to an array results in an array with a single element with index zero - * and the value of the scalar which was converted. - */ - /* Insert a single element */ - jx9HashmapInsert(pMap, 0/* Automatic index assign */, &(*pObj)); - SyBlobRelease(&pObj->sBlob); - } - /* Invalidate any prior representation */ - MemObjSetType(pObj, MEMOBJ_HASHMAP); - pObj->x.pOther = pMap; - } - return SXRET_OK; -} -/* - * Return a pointer to the appropriate convertion method associated - * with the given type. - * Note on type juggling. - * Accoding to the JX9 language reference manual - * JX9 does not require (or support) explicit type definition in variable - * declaration; a variable's type is determined by the context in which - * the variable is used. That is to say, if a string value is assigned - * to variable $var, $var becomes a string. If an integer value is then - * assigned to $var, it becomes an integer. - */ -JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags) -{ - if( iFlags & MEMOBJ_STRING ){ - return jx9MemObjToString; - }else if( iFlags & MEMOBJ_INT ){ - return jx9MemObjToInteger; - }else if( iFlags & MEMOBJ_REAL ){ - return jx9MemObjToReal; - }else if( iFlags & MEMOBJ_BOOL ){ - return jx9MemObjToBool; - }else if( iFlags & MEMOBJ_HASHMAP ){ - return jx9MemObjToHashmap; - } - /* NULL cast */ - return jx9MemObjToNull; -} -/* - * Check whether the jx9_value is numeric [i.e: int/float/bool] or looks - * like a numeric number [i.e: if the jx9_value is of type string.]. - * Return TRUE if numeric.FALSE otherwise. - */ -JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj) -{ - if( pObj->iFlags & ( MEMOBJ_BOOL|MEMOBJ_INT|MEMOBJ_REAL) ){ - return TRUE; - }else if( pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES) ){ - return FALSE; - }else if( pObj->iFlags & MEMOBJ_STRING ){ - SyString sStr; - sxi32 rc; - SyStringInitFromBuf(&sStr, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); - if( sStr.nByte <= 0 ){ - /* Empty string */ - return FALSE; - } - /* Check if the string representation looks like a numeric number */ - rc = SyStrIsNumeric(sStr.zString, sStr.nByte, 0, 0); - return rc == SXRET_OK ? TRUE : FALSE; - } - /* NOT REACHED */ - return FALSE; -} -/* - * Check whether the jx9_value is empty.Return TRUE if empty. - * FALSE otherwise. - * An jx9_value is considered empty if the following are true: - * NULL value. - * Boolean FALSE. - * Integer/Float with a 0 (zero) value. - * An empty string or a stream of 0 (zero) [i.e: "0", "00", "000", ...]. - * An empty array. - * NOTE - * OBJECT VALUE MUST NOT BE MODIFIED. - */ -JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj) -{ - if( pObj->iFlags & MEMOBJ_NULL ){ - return TRUE; - }else if( pObj->iFlags & MEMOBJ_INT ){ - return pObj->x.iVal == 0 ? TRUE : FALSE; - }else if( pObj->iFlags & MEMOBJ_REAL ){ - return pObj->x.rVal == (jx9_real)0 ? TRUE : FALSE; - }else if( pObj->iFlags & MEMOBJ_BOOL ){ - return !pObj->x.iVal; - }else if( pObj->iFlags & MEMOBJ_STRING ){ - if( SyBlobLength(&pObj->sBlob) <= 0 ){ - return TRUE; - }else{ - const char *zIn, *zEnd; - zIn = (const char *)SyBlobData(&pObj->sBlob); - zEnd = &zIn[SyBlobLength(&pObj->sBlob)]; - while( zIn < zEnd ){ - if( zIn[0] != '0' ){ - break; - } - zIn++; - } - return zIn >= zEnd ? TRUE : FALSE; - } - }else if( pObj->iFlags & MEMOBJ_HASHMAP ){ - jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther; - return pMap->nEntry == 0 ? TRUE : FALSE; - }else if ( pObj->iFlags & (MEMOBJ_RES) ){ - return FALSE; - } - /* Assume empty by default */ - return TRUE; -} -/* - * Convert a jx9_value so that it has types MEMOBJ_REAL or MEMOBJ_INT - * or both. - * Invalidate any prior representations. Every effort is made to force - * the conversion, even if the input is a string that does not look - * completely like a number.Convert as much of the string as we can - * and ignore the rest. - */ -JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj) -{ - if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) ){ - if( pObj->iFlags & (MEMOBJ_BOOL|MEMOBJ_NULL) ){ - if( pObj->iFlags & MEMOBJ_NULL ){ - pObj->x.iVal = 0; - } - MemObjSetType(pObj, MEMOBJ_INT); - } - /* Already numeric */ - return SXRET_OK; - } - if( pObj->iFlags & MEMOBJ_STRING ){ - sxi32 rc = SXERR_INVALID; - sxu8 bReal = FALSE; - SyString sString; - SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); - /* Check if the given string looks like a numeric number */ - if( sString.nByte > 0 ){ - rc = SyStrIsNumeric(sString.zString, sString.nByte, &bReal, 0); - } - if( bReal ){ - jx9MemObjToReal(&(*pObj)); - }else{ - if( rc != SXRET_OK ){ - /* The input does not look at all like a number, set the value to 0 */ - pObj->x.iVal = 0; - }else{ - /* Convert as much as we can */ - pObj->x.iVal = MemObjStringToInt(&(*pObj)); - } - MemObjSetType(pObj, MEMOBJ_INT); - SyBlobRelease(&pObj->sBlob); - } - }else if(pObj->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)){ - jx9MemObjToInteger(pObj); - }else{ - /* Perform a blind cast */ - jx9MemObjToReal(&(*pObj)); - } - return SXRET_OK; -} -/* - * Try a get an integer representation of the given jx9_value. - * If the jx9_value is not of type real, this function is a no-op. - */ -JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj) -{ - if( pObj->iFlags & MEMOBJ_REAL ){ - /* Work only with reals */ - MemObjTryIntger(&(*pObj)); - } - return SXRET_OK; -} -/* - * Initialize a jx9_value to the null type. - */ -JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj) -{ - /* Zero the structure */ - SyZero(pObj, sizeof(jx9_value)); - /* Initialize fields */ - pObj->pVm = pVm; - SyBlobInit(&pObj->sBlob, &pVm->sAllocator); - /* Set the NULL type */ - pObj->iFlags = MEMOBJ_NULL; - return SXRET_OK; -} -/* - * Initialize a jx9_value to the integer type. - */ -JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal) -{ - /* Zero the structure */ - SyZero(pObj, sizeof(jx9_value)); - /* Initialize fields */ - pObj->pVm = pVm; - SyBlobInit(&pObj->sBlob, &pVm->sAllocator); - /* Set the desired type */ - pObj->x.iVal = iVal; - pObj->iFlags = MEMOBJ_INT; - return SXRET_OK; -} -/* - * Initialize a jx9_value to the boolean type. - */ -JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal) -{ - /* Zero the structure */ - SyZero(pObj, sizeof(jx9_value)); - /* Initialize fields */ - pObj->pVm = pVm; - SyBlobInit(&pObj->sBlob, &pVm->sAllocator); - /* Set the desired type */ - pObj->x.iVal = iVal ? 1 : 0; - pObj->iFlags = MEMOBJ_BOOL; - return SXRET_OK; -} -#if 0 -/* - * Initialize a jx9_value to the real type. - */ -JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal) -{ - /* Zero the structure */ - SyZero(pObj, sizeof(jx9_value)); - /* Initialize fields */ - pObj->pVm = pVm; - SyBlobInit(&pObj->sBlob, &pVm->sAllocator); - /* Set the desired type */ - pObj->x.rVal = rVal; - pObj->iFlags = MEMOBJ_REAL; - return SXRET_OK; -} -#endif -/* - * Initialize a jx9_value to the array type. - */ -JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray) -{ - /* Zero the structure */ - SyZero(pObj, sizeof(jx9_value)); - /* Initialize fields */ - pObj->pVm = pVm; - SyBlobInit(&pObj->sBlob, &pVm->sAllocator); - /* Set the desired type */ - pObj->iFlags = MEMOBJ_HASHMAP; - pObj->x.pOther = pArray; - return SXRET_OK; -} -/* - * Initialize a jx9_value to the string type. - */ -JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal) -{ - /* Zero the structure */ - SyZero(pObj, sizeof(jx9_value)); - /* Initialize fields */ - pObj->pVm = pVm; - SyBlobInit(&pObj->sBlob, &pVm->sAllocator); - if( pVal ){ - /* Append contents */ - SyBlobAppend(&pObj->sBlob, (const void *)pVal->zString, pVal->nByte); - } - /* Set the desired type */ - pObj->iFlags = MEMOBJ_STRING; - return SXRET_OK; -} -/* - * Append some contents to the internal buffer of a given jx9_value. - * If the given jx9_value is not of type string, this function - * invalidate any prior representation and set the string type. - * Then a simple append operation is performed. - */ -JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen) -{ - sxi32 rc; - if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){ - /* Invalidate any prior representation */ - jx9MemObjRelease(pObj); - MemObjSetType(pObj, MEMOBJ_STRING); - } - /* Append contents */ - rc = SyBlobAppend(&pObj->sBlob, zData, nLen); - return rc; -} -#if 0 -/* - * Format and append some contents to the internal buffer of a given jx9_value. - * If the given jx9_value is not of type string, this function invalidate - * any prior representation and set the string type. - * Then a simple format and append operation is performed. - */ -JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap) -{ - sxi32 rc; - if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){ - /* Invalidate any prior representation */ - jx9MemObjRelease(pObj); - MemObjSetType(pObj, MEMOBJ_STRING); - } - /* Format and append contents */ - rc = SyBlobFormatAp(&pObj->sBlob, zFormat, ap); - return rc; -} -#endif -/* - * Duplicate the contents of a jx9_value. - */ -JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest) -{ - jx9_hashmap *pMap = 0; - sxi32 rc; - if( pSrc->iFlags & MEMOBJ_HASHMAP ){ - /* Increment reference count */ - ((jx9_hashmap *)pSrc->x.pOther)->iRef++; - } - if( pDest->iFlags & MEMOBJ_HASHMAP ){ - pMap = (jx9_hashmap *)pDest->x.pOther; - } - SyMemcpy((const void *)&(*pSrc), &(*pDest), sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32))); - rc = SXRET_OK; - if( SyBlobLength(&pSrc->sBlob) > 0 ){ - SyBlobReset(&pDest->sBlob); - rc = SyBlobDup(&pSrc->sBlob, &pDest->sBlob); - }else{ - if( SyBlobLength(&pDest->sBlob) > 0 ){ - SyBlobRelease(&pDest->sBlob); - } - } - if( pMap ){ - jx9HashmapUnref(pMap); - } - return rc; -} -/* - * Duplicate the contents of a jx9_value but do not copy internal - * buffer contents, simply point to it. - */ -JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest) -{ - SyMemcpy((const void *)&(*pSrc), &(*pDest), - sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32))); - if( pSrc->iFlags & MEMOBJ_HASHMAP ){ - /* Increment reference count */ - ((jx9_hashmap *)pSrc->x.pOther)->iRef++; - } - if( SyBlobLength(&pDest->sBlob) > 0 ){ - SyBlobRelease(&pDest->sBlob); - } - if( SyBlobLength(&pSrc->sBlob) > 0 ){ - SyBlobReadOnly(&pDest->sBlob, SyBlobData(&pSrc->sBlob), SyBlobLength(&pSrc->sBlob)); - } - return SXRET_OK; -} -/* - * Invalidate any prior representation of a given jx9_value. - */ -JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj) -{ - if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){ - if( pObj->iFlags & MEMOBJ_HASHMAP ){ - jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther); - } - /* Release the internal buffer */ - SyBlobRelease(&pObj->sBlob); - /* Invalidate any prior representation */ - pObj->iFlags = MEMOBJ_NULL; - } - return SXRET_OK; -} -/* - * Compare two jx9_values. - * Return 0 if the values are equals, > 0 if pObj1 is greater than pObj2 - * or < 0 if pObj2 is greater than pObj1. - * Type comparison table taken from the JX9 language reference manual. - * Comparisons of $x with JX9 functions Expression - * gettype() empty() is_null() isset() boolean : if($x) - * $x = ""; string TRUE FALSE TRUE FALSE - * $x = null NULL TRUE TRUE FALSE FALSE - * var $x; NULL TRUE TRUE FALSE FALSE - * $x is undefined NULL TRUE TRUE FALSE FALSE - * $x = array(); array TRUE FALSE TRUE FALSE - * $x = false; boolean TRUE FALSE TRUE FALSE - * $x = true; boolean FALSE FALSE TRUE TRUE - * $x = 1; integer FALSE FALSE TRUE TRUE - * $x = 42; integer FALSE FALSE TRUE TRUE - * $x = 0; integer TRUE FALSE TRUE FALSE - * $x = -1; integer FALSE FALSE TRUE TRUE - * $x = "1"; string FALSE FALSE TRUE TRUE - * $x = "0"; string TRUE FALSE TRUE FALSE - * $x = "-1"; string FALSE FALSE TRUE TRUE - * $x = "jx9"; string FALSE FALSE TRUE TRUE - * $x = "true"; string FALSE FALSE TRUE TRUE - * $x = "false"; string FALSE FALSE TRUE TRUE - * Loose comparisons with == - * TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "jx9" "" - * TRUE TRUE FALSE TRUE FALSE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE - * FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE TRUE - * 1 TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE - * 0 FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE TRUE TRUE - * -1 TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE - * "1" TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE - * "0" FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE - * "-1" TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE - * NULL FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE - * array() FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE - * "jx9" TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE - * "" FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE - * Strict comparisons with === - * TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "jx9" "" - * TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE - * FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE - * 1 FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE - * 0 FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE - * -1 FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE - * "1" FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE - * "0" FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE - * "-1" FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE - * NULL FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE - * array() FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE - * "jx9" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE - * "" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE - */ -JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest) -{ - sxi32 iComb; - sxi32 rc; - if( bStrict ){ - sxi32 iF1, iF2; - /* Strict comparisons with === */ - iF1 = pObj1->iFlags; - iF2 = pObj2->iFlags; - if( iF1 != iF2 ){ - /* Not of the same type */ - return 1; - } - } - /* Combine flag together */ - iComb = pObj1->iFlags|pObj2->iFlags; - if( iComb & (MEMOBJ_NULL|MEMOBJ_RES|MEMOBJ_BOOL) ){ - /* Convert to boolean: Keep in mind FALSE < TRUE */ - if( (pObj1->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pObj1); - } - if( (pObj2->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pObj2); - } - return (sxi32)((pObj1->x.iVal != 0) - (pObj2->x.iVal != 0)); - }else if ( iComb & MEMOBJ_HASHMAP ){ - /* Hashmap aka 'array' comparison */ - if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* Array is always greater */ - return -1; - } - if( (pObj2->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* Array is always greater */ - return 1; - } - /* Perform the comparison */ - rc = jx9HashmapCmp((jx9_hashmap *)pObj1->x.pOther, (jx9_hashmap *)pObj2->x.pOther, bStrict); - return rc; - }else if ( iComb & MEMOBJ_STRING ){ - SyString s1, s2; - /* Perform a strict string comparison.*/ - if( (pObj1->iFlags&MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pObj1); - } - if( (pObj2->iFlags&MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pObj2); - } - SyStringInitFromBuf(&s1, SyBlobData(&pObj1->sBlob), SyBlobLength(&pObj1->sBlob)); - SyStringInitFromBuf(&s2, SyBlobData(&pObj2->sBlob), SyBlobLength(&pObj2->sBlob)); - /* - * Strings are compared using memcmp(). If one value is an exact prefix of the - * other, then the shorter value is less than the longer value. - */ - rc = SyMemcmp((const void *)s1.zString, (const void *)s2.zString, SXMIN(s1.nByte, s2.nByte)); - if( rc == 0 ){ - if( s1.nByte != s2.nByte ){ - rc = s1.nByte < s2.nByte ? -1 : 1; - } - } - return rc; - }else if( iComb & (MEMOBJ_INT|MEMOBJ_REAL) ){ - /* Perform a numeric comparison if one of the operand is numeric(integer or real) */ - if( (pObj1->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){ - jx9MemObjToNumeric(pObj1); - } - if( (pObj2->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){ - jx9MemObjToNumeric(pObj2); - } - if( (pObj1->iFlags & pObj2->iFlags & MEMOBJ_INT) == 0) { - jx9_real r1, r2; - /* Compare as reals */ - if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pObj1); - } - r1 = pObj1->x.rVal; - if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pObj2); - } - r2 = pObj2->x.rVal; - if( r1 > r2 ){ - return 1; - }else if( r1 < r2 ){ - return -1; - } - return 0; - }else{ - /* Integer comparison */ - if( pObj1->x.iVal > pObj2->x.iVal ){ - return 1; - }else if( pObj1->x.iVal < pObj2->x.iVal ){ - return -1; - } - return 0; - } - } - /* NOT REACHED */ - SXUNUSED(iNest); - return 0; -} -/* - * Perform an addition operation of two jx9_values. - * The reason this function is implemented here rather than 'vm.c' - * is that the '+' operator is overloaded. - * That is, the '+' operator is used for arithmetic operation and also - * used for operation on arrays [i.e: union]. When used with an array - * The + operator returns the right-hand array appended to the left-hand array. - * For keys that exist in both arrays, the elements from the left-hand array - * will be used, and the matching elements from the right-hand array will - * be ignored. - * This function take care of handling all the scenarios. - */ -JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore) -{ - if( ((pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP) == 0 ){ - /* Arithemtic operation */ - jx9MemObjToNumeric(pObj1); - jx9MemObjToNumeric(pObj2); - if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_REAL ){ - /* Floating point arithmetic */ - jx9_real a, b; - if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pObj1); - } - if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pObj2); - } - a = pObj1->x.rVal; - b = pObj2->x.rVal; - pObj1->x.rVal = a+b; - MemObjSetType(pObj1, MEMOBJ_REAL); - /* Try to get an integer representation also */ - MemObjTryIntger(&(*pObj1)); - }else{ - /* Integer arithmetic */ - sxi64 a, b; - a = pObj1->x.iVal; - b = pObj2->x.iVal; - pObj1->x.iVal = a+b; - MemObjSetType(pObj1, MEMOBJ_INT); - } - }else{ - if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP ){ - jx9_hashmap *pMap; - sxi32 rc; - if( bAddStore ){ - /* Do not duplicate the hashmap, use the left one since its an add&store operation. - */ - if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* Force a hashmap cast */ - rc = jx9MemObjToHashmap(pObj1); - if( rc != SXRET_OK ){ - jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array"); - return rc; - } - } - /* Point to the structure that describe the hashmap */ - pMap = (jx9_hashmap *)pObj1->x.pOther; - }else{ - /* Create a new hashmap */ - pMap = jx9NewHashmap(pObj1->pVm, 0, 0); - if( pMap == 0){ - jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array"); - return SXERR_MEM; - } - } - if( !bAddStore ){ - if(pObj1->iFlags & MEMOBJ_HASHMAP ){ - /* Perform a hashmap duplication */ - jx9HashmapDup((jx9_hashmap *)pObj1->x.pOther, pMap); - }else{ - if((pObj1->iFlags & MEMOBJ_NULL) == 0 ){ - /* Simple insertion */ - jx9HashmapInsert(pMap, 0, pObj1); - } - } - } - /* Perform the union */ - if(pObj2->iFlags & MEMOBJ_HASHMAP ){ - jx9HashmapUnion(pMap, (jx9_hashmap *)pObj2->x.pOther); - }else{ - if((pObj2->iFlags & MEMOBJ_NULL) == 0 ){ - /* Simple insertion */ - jx9HashmapInsert(pMap, 0, pObj2); - } - } - /* Reflect the change */ - if( pObj1->iFlags & MEMOBJ_STRING ){ - SyBlobRelease(&pObj1->sBlob); - } - pObj1->x.pOther = pMap; - MemObjSetType(pObj1, MEMOBJ_HASHMAP); - } - } - return SXRET_OK; -} -/* - * Return a printable representation of the type of a given - * jx9_value. - */ -JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal) -{ - const char *zType = ""; - if( pVal->iFlags & MEMOBJ_NULL ){ - zType = "null"; - }else if( pVal->iFlags & MEMOBJ_INT ){ - zType = "int"; - }else if( pVal->iFlags & MEMOBJ_REAL ){ - zType = "float"; - }else if( pVal->iFlags & MEMOBJ_STRING ){ - zType = "string"; - }else if( pVal->iFlags & MEMOBJ_BOOL ){ - zType = "bool"; - }else if( pVal->iFlags & MEMOBJ_HASHMAP ){ - jx9_hashmap *pMap = (jx9_hashmap *)pVal->x.pOther; - if( pMap->iFlags & HASHMAP_JSON_OBJECT ){ - zType = "JSON Object"; - }else{ - zType = "JSON Array"; - } - }else if( pVal->iFlags & MEMOBJ_RES ){ - zType = "resource"; - } - return zType; -} -/* - * Dump a jx9_value [i.e: get a printable representation of it's type and contents.]. - * Store the dump in the given blob. - */ -JX9_PRIVATE sxi32 jx9MemObjDump( - SyBlob *pOut, /* Store the dump here */ - jx9_value *pObj /* Dump this */ - ) -{ - sxi32 rc = SXRET_OK; - const char *zType; - /* Get value type first */ - zType = jx9MemObjTypeDump(pObj); - SyBlobAppend(&(*pOut), zType, SyStrlen(zType)); - if((pObj->iFlags & MEMOBJ_NULL) == 0 ){ - SyBlobAppend(&(*pOut), "(", sizeof(char)); - if( pObj->iFlags & MEMOBJ_HASHMAP ){ - jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther; - SyBlobFormat(pOut,"%u ",pMap->nEntry); - /* Dump hashmap entries */ - rc = jx9JsonSerialize(pObj,pOut); - }else{ - SyBlob *pContents = &pObj->sBlob; - /* Get a printable representation of the contents */ - if((pObj->iFlags & MEMOBJ_STRING) == 0 ){ - MemObjStringValue(&(*pOut), &(*pObj)); - }else{ - /* Append length first */ - SyBlobFormat(&(*pOut), "%u '", SyBlobLength(&pObj->sBlob)); - if( SyBlobLength(pContents) > 0 ){ - SyBlobAppend(&(*pOut), SyBlobData(pContents), SyBlobLength(pContents)); - } - SyBlobAppend(&(*pOut), "'", sizeof(char)); - } - } - SyBlobAppend(&(*pOut), ")", sizeof(char)); - } -#ifdef __WINNT__ - SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n")-1); -#else - SyBlobAppend(&(*pOut), "\n", sizeof(char)); -#endif - return rc; -} -/* - * ---------------------------------------------------------- - * File: jx9_parse.c - * MD5: d8fcac4c6cd7672f0103c0bf4a4b61fc - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: parse.c v1.2 FreeBSD 2012-12-11 00:46 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* Expression parser for the Jx9 programming language */ -/* Operators associativity */ -#define EXPR_OP_ASSOC_LEFT 0x01 /* Left associative operator */ -#define EXPR_OP_ASSOC_RIGHT 0x02 /* Right associative operator */ -#define EXPR_OP_NON_ASSOC 0x04 /* Non-associative operator */ -/* - * Operators table - * This table is sorted by operators priority (highest to lowest) according - * the JX9 language reference manual. - * JX9 implements all the 60 JX9 operators and have introduced the eq and ne operators. - * The operators precedence table have been improved dramatically so that you can do same - * amazing things now such as array dereferencing, on the fly function call, anonymous function - * as array values, object member access on instantiation and so on. - * Refer to the following page for a full discussion on these improvements: - * http://jx9.symisc.net/features.html - */ -static const jx9_expr_op aOpTable[] = { - /* Postfix operators */ - /* Precedence 2(Highest), left-associative */ - { {".", sizeof(char)}, EXPR_OP_DOT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_MEMBER }, - { {"[", sizeof(char)}, EXPR_OP_SUBSCRIPT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_LOAD_IDX}, - /* Precedence 3, non-associative */ - { {"++", sizeof(char)*2}, EXPR_OP_INCR, 3, EXPR_OP_NON_ASSOC , JX9_OP_INCR}, - { {"--", sizeof(char)*2}, EXPR_OP_DECR, 3, EXPR_OP_NON_ASSOC , JX9_OP_DECR}, - /* Unary operators */ - /* Precedence 4, right-associative */ - { {"-", sizeof(char)}, EXPR_OP_UMINUS, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UMINUS }, - { {"+", sizeof(char)}, EXPR_OP_UPLUS, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UPLUS }, - { {"~", sizeof(char)}, EXPR_OP_BITNOT, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_BITNOT }, - { {"!", sizeof(char)}, EXPR_OP_LOGNOT, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_LNOT }, - /* Cast operators */ - { {"(int)", sizeof("(int)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_INT }, - { {"(bool)", sizeof("(bool)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_BOOL }, - { {"(string)", sizeof("(string)")-1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_STR }, - { {"(float)", sizeof("(float)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_REAL }, - { {"(array)", sizeof("(array)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */ - { {"(object)", sizeof("(object)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */ - /* Binary operators */ - /* Precedence 7, left-associative */ - { {"*", sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MUL}, - { {"/", sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_DIV}, - { {"%", sizeof(char)}, EXPR_OP_MOD, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MOD}, - /* Precedence 8, left-associative */ - { {"+", sizeof(char)}, EXPR_OP_ADD, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_ADD}, - { {"-", sizeof(char)}, EXPR_OP_SUB, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_SUB}, - { {"..", sizeof(char)*2},EXPR_OP_DDOT, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_CAT}, - /* Precedence 9, left-associative */ - { {"<<", sizeof(char)*2}, EXPR_OP_SHL, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHL}, - { {">>", sizeof(char)*2}, EXPR_OP_SHR, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHR}, - /* Precedence 10, non-associative */ - { {"<", sizeof(char)}, EXPR_OP_LT, 10, EXPR_OP_NON_ASSOC, JX9_OP_LT}, - { {">", sizeof(char)}, EXPR_OP_GT, 10, EXPR_OP_NON_ASSOC, JX9_OP_GT}, - { {"<=", sizeof(char)*2}, EXPR_OP_LE, 10, EXPR_OP_NON_ASSOC, JX9_OP_LE}, - { {">=", sizeof(char)*2}, EXPR_OP_GE, 10, EXPR_OP_NON_ASSOC, JX9_OP_GE}, - { {"<>", sizeof(char)*2}, EXPR_OP_NE, 10, EXPR_OP_NON_ASSOC, JX9_OP_NEQ}, - /* Precedence 11, non-associative */ - { {"==", sizeof(char)*2}, EXPR_OP_EQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_EQ}, - { {"!=", sizeof(char)*2}, EXPR_OP_NE, 11, EXPR_OP_NON_ASSOC, JX9_OP_NEQ}, - { {"===", sizeof(char)*3}, EXPR_OP_TEQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_TEQ}, - { {"!==", sizeof(char)*3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, JX9_OP_TNE}, - /* Precedence 12, left-associative */ - { {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT, JX9_OP_BAND}, - /* Binary operators */ - /* Precedence 13, left-associative */ - { {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, JX9_OP_BXOR}, - /* Precedence 14, left-associative */ - { {"|", sizeof(char)}, EXPR_OP_BOR, 14, EXPR_OP_ASSOC_LEFT, JX9_OP_BOR}, - /* Precedence 15, left-associative */ - { {"&&", sizeof(char)*2}, EXPR_OP_LAND, 15, EXPR_OP_ASSOC_LEFT, JX9_OP_LAND}, - /* Precedence 16, left-associative */ - { {"||", sizeof(char)*2}, EXPR_OP_LOR, 16, EXPR_OP_ASSOC_LEFT, JX9_OP_LOR}, - /* Ternary operator */ - /* Precedence 17, left-associative */ - { {"?", sizeof(char)}, EXPR_OP_QUESTY, 17, EXPR_OP_ASSOC_LEFT, 0}, - /* Combined binary operators */ - /* Precedence 18, right-associative */ - { {"=", sizeof(char)}, EXPR_OP_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_STORE}, - { {"+=", sizeof(char)*2}, EXPR_OP_ADD_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_ADD_STORE }, - { {"-=", sizeof(char)*2}, EXPR_OP_SUB_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SUB_STORE }, - { {".=", sizeof(char)*2}, EXPR_OP_DOT_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_CAT_STORE }, - { {"*=", sizeof(char)*2}, EXPR_OP_MUL_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_MUL_STORE }, - { {"/=", sizeof(char)*2}, EXPR_OP_DIV_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_DIV_STORE }, - { {"%=", sizeof(char)*2}, EXPR_OP_MOD_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_MOD_STORE }, - { {"&=", sizeof(char)*2}, EXPR_OP_AND_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BAND_STORE }, - { {"|=", sizeof(char)*2}, EXPR_OP_OR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BOR_STORE }, - { {"^=", sizeof(char)*2}, EXPR_OP_XOR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BXOR_STORE }, - { {"<<=", sizeof(char)*3}, EXPR_OP_SHL_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SHL_STORE }, - { {">>=", sizeof(char)*3}, EXPR_OP_SHR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SHR_STORE }, - /* Precedence 22, left-associative [Lowest operator] */ - { {",", sizeof(char)}, EXPR_OP_COMMA, 22, EXPR_OP_ASSOC_LEFT, 0}, /* IMP-0139-COMMA: Symisc eXtension */ -}; -/* Function call operator need special handling */ -static const jx9_expr_op sFCallOp = {{"(", sizeof(char)}, EXPR_OP_FUNC_CALL, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_CALL}; -/* - * Check if the given token is a potential operator or not. - * This function is called by the lexer each time it extract a token that may - * look like an operator. - * Return a structure [i.e: jx9_expr_op instnace ] that describe the operator on success. - * Otherwise NULL. - * Note that the function take care of handling ambiguity [i.e: whether we are dealing with - * a binary minus or unary minus.] - */ -JX9_PRIVATE const jx9_expr_op * jx9ExprExtractOperator(SyString *pStr, SyToken *pLast) -{ - sxu32 n = 0; - sxi32 rc; - /* Do a linear lookup on the operators table */ - for(;;){ - if( n >= SX_ARRAYSIZE(aOpTable) ){ - break; - } - rc = SyStringCmp(pStr, &aOpTable[n].sOp, SyMemcmp); - if( rc == 0 ){ - if( aOpTable[n].sOp.nByte != sizeof(char) || (aOpTable[n].iOp != EXPR_OP_UMINUS && aOpTable[n].iOp != EXPR_OP_UPLUS) || pLast == 0 ){ - if( aOpTable[n].iOp == EXPR_OP_SUBSCRIPT && (pLast == 0 || (pLast->nType & (JX9_TK_ID|JX9_TK_CSB/*]*/|JX9_TK_RPAREN/*)*/)) == 0) ){ - /* JSON Array not subscripting, return NULL */ - return 0; - } - /* There is no ambiguity here, simply return the first operator seen */ - return &aOpTable[n]; - } - /* Handle ambiguity */ - if( pLast->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_COLON/*:*/|JX9_TK_COMMA/*, '*/) ){ - /* Unary opertors have prcedence here over binary operators */ - return &aOpTable[n]; - } - if( pLast->nType & JX9_TK_OP ){ - const jx9_expr_op *pOp = (const jx9_expr_op *)pLast->pUserData; - /* Ticket 1433-31: Handle the '++', '--' operators case */ - if( pOp->iOp != EXPR_OP_INCR && pOp->iOp != EXPR_OP_DECR ){ - /* Unary opertors have prcedence here over binary operators */ - return &aOpTable[n]; - } - - } - } - ++n; /* Next operator in the table */ - } - /* No such operator */ - return 0; -} -/* - * Delimit a set of token stream. - * This function take care of handling the nesting level and stops when it hit - * the end of the input or the ending token is found and the nesting level is zero. - */ -JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn,SyToken *pEnd,sxu32 nTokStart,sxu32 nTokEnd,SyToken **ppEnd) -{ - SyToken *pCur = pIn; - sxi32 iNest = 1; - for(;;){ - if( pCur >= pEnd ){ - break; - } - if( pCur->nType & nTokStart ){ - /* Increment nesting level */ - iNest++; - }else if( pCur->nType & nTokEnd ){ - /* Decrement nesting level */ - iNest--; - if( iNest <= 0 ){ - break; - } - } - /* Advance cursor */ - pCur++; - } - /* Point to the end of the chunk */ - *ppEnd = pCur; -} -/* - * Retrun TRUE if the given ID represent a language construct [i.e: print, print..]. FALSE otherwise. - * Note on reserved keywords. - * According to the JX9 language reference manual: - * These words have special meaning in JX9. Some of them represent things which look like - * functions, some look like constants, and so on--but they're not, really: they are language - * constructs. You cannot use any of the following words as constants, object names, function - * or method names. Using them as variable names is generally OK, but could lead to confusion. - */ -JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID) -{ - if( nKeyID == JX9_TKWRD_PRINT || nKeyID == JX9_TKWRD_EXIT || nKeyID == JX9_TKWRD_DIE - || nKeyID == JX9_TKWRD_INCLUDE|| nKeyID == JX9_TKWRD_IMPORT ){ - return TRUE; - } - /* Not a language construct */ - return FALSE; -} -/* - * Point to the next expression that should be evaluated shortly. - * The cursor stops when it hit a comma ', ' or a semi-colon and the nesting - * level is zero. - */ -JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart,SyToken *pEnd,SyToken **ppNext) -{ - SyToken *pCur = pStart; - sxi32 iNest = 0; - if( pCur >= pEnd || (pCur->nType & JX9_TK_SEMI/*';'*/) ){ - /* Last expression */ - return SXERR_EOF; - } - while( pCur < pEnd ){ - if( (pCur->nType & (JX9_TK_COMMA/*','*/|JX9_TK_SEMI/*';'*/)) && iNest <= 0){ - break; - } - if( pCur->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OSB/*'['*/|JX9_TK_OCB/*'{'*/) ){ - iNest++; - }else if( pCur->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*']'*/|JX9_TK_CCB/*'}*/) ){ - iNest--; - } - pCur++; - } - *ppNext = pCur; - return SXRET_OK; -} -/* - * Collect and assemble tokens holding annonymous functions/closure body. - * When errors, JX9 take care of generating the appropriate error message. - * Note on annonymous functions. - * According to the JX9 language reference manual: - * Anonymous functions, also known as closures, allow the creation of functions - * which have no specified name. They are most useful as the value of callback - * parameters, but they have many other uses. - * Closures may also inherit variables from the parent scope. Any such variables - * must be declared in the function header. Inheriting variables from the parent - * scope is not the same as using global variables. Global variables exist in the global scope - * which is the same no matter what function is executing. The parent scope of a closure is the - * function in which the closure was declared (not necessarily the function it was called from). - * - * Some example: - * $greet = function($name) - * { - * printf("Hello %s\r\n", $name); - * }; - * $greet('World'); - * $greet('JX9'); - * - * $double = function($a) { - * return $a * 2; - * }; - * // This is our range of numbers - * $numbers = range(1, 5); - * // Use the Annonymous function as a callback here to - * // double the size of each element in our - * // range - * $new_numbers = array_map($double, $numbers); - * print implode(' ', $new_numbers); - */ -static sxi32 ExprAssembleAnnon(jx9_gen_state *pGen,SyToken **ppCur, SyToken *pEnd) -{ - SyToken *pIn = *ppCur; - sxu32 nLine; - sxi32 rc; - /* Jump the 'function' keyword */ - nLine = pIn->nLine; - pIn++; - if( pIn < pEnd && (pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) ){ - pIn++; - } - if( pIn >= pEnd || (pIn->nType & JX9_TK_LPAREN) == 0 ){ - /* Syntax error */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing opening parenthesis '(' while declaring annonymous function"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - goto Synchronize; - } - pIn++; /* Jump the leading parenthesis '(' */ - jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_LPAREN/*'('*/, JX9_TK_RPAREN/*')'*/, &pIn); - if( pIn >= pEnd || &pIn[1] >= pEnd ){ - /* Syntax error */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - goto Synchronize; - } - pIn++; /* Jump the trailing parenthesis */ - if( pIn->nType & JX9_TK_OCB /*'{'*/ ){ - pIn++; /* Jump the leading curly '{' */ - jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_OCB/*'{'*/, JX9_TK_CCB/*'}'*/, &pIn); - if( pIn < pEnd ){ - pIn++; - } - }else{ - /* Syntax error */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function, missing '{'"); - if( rc == SXERR_ABORT ){ - return SXERR_ABORT; - } - } - rc = SXRET_OK; -Synchronize: - /* Synchronize pointers */ - *ppCur = pIn; - return rc; -} -/* - * Make sure we are dealing with a valid expression tree. - * This function check for balanced parenthesis, braces, brackets and so on. - * When errors, JX9 take care of generating the appropriate error message. - * Return SXRET_OK on success. Any other return value indicates syntax error. - */ -static sxi32 ExprVerifyNodes(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nNode) -{ - sxi32 iParen, iSquare, iBraces; - sxi32 i, rc; - - if( nNode > 0 && apNode[0]->pOp && (apNode[0]->pOp->iOp == EXPR_OP_ADD || apNode[0]->pOp->iOp == EXPR_OP_SUB) ){ - /* Fix and mark as an unary not binary plus/minus operator */ - apNode[0]->pOp = jx9ExprExtractOperator(&apNode[0]->pStart->sData, 0); - apNode[0]->pStart->pUserData = (void *)apNode[0]->pOp; - } - iParen = iSquare = iBraces = 0; - for( i = 0 ; i < nNode ; ++i ){ - if( apNode[i]->pStart->nType & JX9_TK_LPAREN /*'('*/){ - if( i > 0 && ( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral || - (apNode[i - 1]->pStart->nType & (JX9_TK_ID|JX9_TK_KEYWORD|JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*]*/))) ){ - /* Ticket 1433-033: Take care to ignore alpha-stream [i.e: or, xor] operators followed by an opening parenthesis */ - if( (apNode[i - 1]->pStart->nType & JX9_TK_OP) == 0 ){ - /* We are dealing with a postfix [i.e: function call] operator - * not a simple left parenthesis. Mark the node. - */ - apNode[i]->pStart->nType |= JX9_TK_OP; - apNode[i]->pStart->pUserData = (void *)&sFCallOp; /* Function call operator */ - apNode[i]->pOp = &sFCallOp; - } - } - iParen++; - }else if( apNode[i]->pStart->nType & JX9_TK_RPAREN/*')*/){ - if( iParen <= 0 ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ')'"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - iParen--; - }else if( apNode[i]->pStart->nType & JX9_TK_OSB /*'['*/ && apNode[i]->xCode == 0 ){ - iSquare++; - }else if (apNode[i]->pStart->nType & JX9_TK_CSB /*']'*/){ - if( iSquare <= 0 ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ']'"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - iSquare--; - }else if( apNode[i]->pStart->nType & JX9_TK_OCB /*'{'*/ && apNode[i]->xCode == 0 ){ - iBraces++; - }else if (apNode[i]->pStart->nType & JX9_TK_CCB /*'}'*/){ - if( iBraces <= 0 ){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token '}'"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - iBraces--; - }else if( apNode[i]->pStart->nType & JX9_TK_OP ){ - const jx9_expr_op *pOp = (const jx9_expr_op *)apNode[i]->pOp; - if( i > 0 && (pOp->iOp == EXPR_OP_UMINUS || pOp->iOp == EXPR_OP_UPLUS)){ - if( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ){ - sxi32 iExprOp = EXPR_OP_SUB; /* Binary minus */ - sxu32 n = 0; - if( pOp->iOp == EXPR_OP_UPLUS ){ - iExprOp = EXPR_OP_ADD; /* Binary plus */ - } - /* - * TICKET 1433-013: This is a fix around an obscure bug when the user uses - * a variable name which is an alpha-stream operator [i.e: $and, $xor, $eq..]. - */ - while( n < SX_ARRAYSIZE(aOpTable) && aOpTable[n].iOp != iExprOp ){ - ++n; - } - pOp = &aOpTable[n]; - /* Mark as binary '+' or '-', not an unary */ - apNode[i]->pOp = pOp; - apNode[i]->pStart->pUserData = (void *)pOp; - } - } - } - } - if( iParen != 0 || iSquare != 0 || iBraces != 0){ - rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[0]->pStart->nLine, "Syntax error, mismatched '(', '[' or '{'"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - return SXRET_OK; -} -/* - * Extract a single expression node from the input. - * On success store the freshly extractd node in ppNode. - * When errors, JX9 take care of generating the appropriate error message. - * An expression node can be a variable [i.e: $var], an operator [i.e: ++] - * an annonymous function [i.e: function(){ return "Hello"; }, a double/single - * quoted string, a heredoc/nowdoc, a literal [i.e: JX9_EOL], a namespace path - * [i.e: namespaces\path\to..], a array/list [i.e: array(4, 5, 6)] and so on. - */ -static sxi32 ExprExtractNode(jx9_gen_state *pGen, jx9_expr_node **ppNode) -{ - jx9_expr_node *pNode; - SyToken *pCur; - sxi32 rc; - /* Allocate a new node */ - pNode = (jx9_expr_node *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_expr_node)); - if( pNode == 0 ){ - /* If the supplied memory subsystem is so sick that we are unable to allocate - * a tiny chunk of memory, there is no much we can do here. - */ - return SXERR_MEM; - } - /* Zero the structure */ - SyZero(pNode, sizeof(jx9_expr_node)); - SySetInit(&pNode->aNodeArgs, &pGen->pVm->sAllocator, sizeof(jx9_expr_node **)); - /* Point to the head of the token stream */ - pCur = pNode->pStart = pGen->pIn; - /* Start collecting tokens */ - if( pCur->nType & JX9_TK_OP ){ - /* Point to the instance that describe this operator */ - pNode->pOp = (const jx9_expr_op *)pCur->pUserData; - /* Advance the stream cursor */ - pCur++; - }else if( pCur->nType & JX9_TK_DOLLAR ){ - /* Isolate variable */ - pCur++; /* Jump the dollar sign */ - if( pCur >= pGen->pEnd ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"Invalid variable name"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); - return rc; - } - pCur++; /* Jump the variable name */ - pNode->xCode = jx9CompileVariable; - }else if( pCur->nType & JX9_TK_OCB /* '{' */ ){ - /* JSON Object, assemble tokens */ - pCur++; - jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OCB /* '[' */, JX9_TK_CCB /* ']' */, &pCur); - if( pCur < pGen->pEnd ){ - pCur++; - }else{ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Object: Missing closing braces '}'"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); - return rc; - } - pNode->xCode = jx9CompileJsonObject; - }else if( pCur->nType & JX9_TK_OSB /* '[' */ && !(pCur->nType & JX9_TK_OP) ){ - /* JSON Array, assemble tokens */ - pCur++; - jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OSB /* '[' */, JX9_TK_CSB /* ']' */, &pCur); - if( pCur < pGen->pEnd ){ - pCur++; - }else{ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Array: Missing closing square bracket ']'"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); - return rc; - } - pNode->xCode = jx9CompileJsonArray; - }else if( pCur->nType & JX9_TK_KEYWORD ){ - int nKeyword = SX_PTR_TO_INT(pCur->pUserData); - if( nKeyword == JX9_TKWRD_FUNCTION ){ - /* Annonymous function */ - if( &pCur[1] >= pGen->pEnd ){ - /* Assume a literal */ - pCur++; - pNode->xCode = jx9CompileLiteral; - }else{ - /* Assemble annonymous functions body */ - rc = ExprAssembleAnnon(&(*pGen), &pCur, pGen->pEnd); - if( rc != SXRET_OK ){ - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); - return rc; - } - pNode->xCode = jx9CompileAnnonFunc; - } - }else if( jx9IsLangConstruct(nKeyword) && &pCur[1] < pGen->pEnd ){ - /* Language constructs [i.e: print,die...] require special handling */ - jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_LPAREN|JX9_TK_OCB|JX9_TK_OSB, JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB, &pCur); - pNode->xCode = jx9CompileLangConstruct; - }else{ - /* Assume a literal */ - pCur++; - pNode->xCode = jx9CompileLiteral; - } - }else if( pCur->nType & (JX9_TK_ID) ){ - /* Constants, function name, namespace path, object name... */ - pCur++; - pNode->xCode = jx9CompileLiteral; - }else{ - if( (pCur->nType & (JX9_TK_LPAREN|JX9_TK_RPAREN|JX9_TK_COMMA|JX9_TK_CSB|JX9_TK_OCB|JX9_TK_CCB|JX9_TK_COLON)) == 0 ){ - /* Point to the code generator routine */ - pNode->xCode = jx9GetNodeHandler(pCur->nType); - if( pNode->xCode == 0 ){ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Unexpected token '%z'", &pNode->pStart->sData); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); - return rc; - } - } - /* Advance the stream cursor */ - pCur++; - } - /* Point to the end of the token stream */ - pNode->pEnd = pCur; - /* Save the node for later processing */ - *ppNode = pNode; - /* Synchronize cursors */ - pGen->pIn = pCur; - return SXRET_OK; -} -/* - * Free an expression tree. - */ -static void ExprFreeTree(jx9_gen_state *pGen, jx9_expr_node *pNode) -{ - if( pNode->pLeft ){ - /* Release the left tree */ - ExprFreeTree(&(*pGen), pNode->pLeft); - } - if( pNode->pRight ){ - /* Release the right tree */ - ExprFreeTree(&(*pGen), pNode->pRight); - } - if( pNode->pCond ){ - /* Release the conditional tree used by the ternary operator */ - ExprFreeTree(&(*pGen), pNode->pCond); - } - if( SySetUsed(&pNode->aNodeArgs) > 0 ){ - jx9_expr_node **apArg; - sxu32 n; - /* Release node arguments */ - apArg = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs); - for( n = 0 ; n < SySetUsed(&pNode->aNodeArgs) ; ++n ){ - ExprFreeTree(&(*pGen), apArg[n]); - } - SySetRelease(&pNode->aNodeArgs); - } - /* Finally, release this node */ - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); -} -/* - * Free an expression tree. - * This function is a wrapper around ExprFreeTree() defined above. - */ -JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet) -{ - jx9_expr_node **apNode; - sxu32 n; - apNode = (jx9_expr_node **)SySetBasePtr(pNodeSet); - for( n = 0 ; n < SySetUsed(pNodeSet) ; ++n ){ - if( apNode[n] ){ - ExprFreeTree(&(*pGen), apNode[n]); - } - } - return SXRET_OK; -} -/* - * Check if the given node is a modifialbe l/r-value. - * Return TRUE if modifiable.FALSE otherwise. - */ -static int ExprIsModifiableValue(jx9_expr_node *pNode) -{ - sxi32 iExprOp; - if( pNode->pOp == 0 ){ - return pNode->xCode == jx9CompileVariable ? TRUE : FALSE; - } - iExprOp = pNode->pOp->iOp; - if( iExprOp == EXPR_OP_DOT /*'.' */ ){ - return TRUE; - } - if( iExprOp == EXPR_OP_SUBSCRIPT/*'[]'*/ ){ - if( pNode->pLeft->pOp ) { - if( pNode->pLeft->pOp->iOp != EXPR_OP_SUBSCRIPT /*'['*/ && pNode->pLeft->pOp->iOp != EXPR_OP_DOT /*'.'*/){ - return FALSE; - } - }else if( pNode->pLeft->xCode != jx9CompileVariable ){ - return FALSE; - } - return TRUE; - } - /* Not a modifiable l or r-value */ - return FALSE; -} -/* Forward declaration */ -static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken); -/* Macro to check if the given node is a terminal */ -#define NODE_ISTERM(NODE) (apNode[NODE] && (!apNode[NODE]->pOp || apNode[NODE]->pLeft )) -/* - * Buid an expression tree for each given function argument. - * When errors, JX9 take care of generating the appropriate error message. - */ -static sxi32 ExprProcessFuncArguments(jx9_gen_state *pGen, jx9_expr_node *pOp, jx9_expr_node **apNode, sxi32 nToken) -{ - sxi32 iNest, iCur, iNode; - sxi32 rc; - /* Process function arguments from left to right */ - iCur = 0; - for(;;){ - if( iCur >= nToken ){ - /* No more arguments to process */ - break; - } - iNode = iCur; - iNest = 0; - while( iCur < nToken ){ - if( apNode[iCur] ){ - if( (apNode[iCur]->pStart->nType & JX9_TK_COMMA) && apNode[iCur]->pLeft == 0 && iNest <= 0 ){ - break; - }else if( apNode[iCur]->pStart->nType & (JX9_TK_LPAREN|JX9_TK_OSB|JX9_TK_OCB) ){ - iNest++; - }else if( apNode[iCur]->pStart->nType & (JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB) ){ - iNest--; - } - } - iCur++; - } - if( iCur > iNode ){ - ExprMakeTree(&(*pGen), &apNode[iNode], iCur-iNode); - if( apNode[iNode] ){ - /* Put a pointer to the root of the tree in the arguments set */ - SySetPut(&pOp->aNodeArgs, (const void *)&apNode[iNode]); - }else{ - /* Empty function argument */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Empty function argument"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - }else{ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - /* Jump trailing comma */ - if( iCur < nToken && apNode[iCur] && (apNode[iCur]->pStart->nType & JX9_TK_COMMA) ){ - iCur++; - if( iCur >= nToken ){ - /* missing function argument */ - rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - } - } - return SXRET_OK; -} -/* - * Create an expression tree from an array of tokens. - * If successful, the root of the tree is stored in apNode[0]. - * When errors, JX9 take care of generating the appropriate error message. - */ - static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken) - { - sxi32 i, iLeft, iRight; - jx9_expr_node *pNode; - sxi32 iCur; - sxi32 rc; - if( nToken <= 0 || (nToken == 1 && apNode[0]->xCode) ){ - /* TICKET 1433-17: self evaluating node */ - return SXRET_OK; - } - /* Process expressions enclosed in parenthesis first */ - for( iCur = 0 ; iCur < nToken ; ++iCur ){ - sxi32 iNest; - /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator - * since the LPAREN token can also be an operator [i.e: Function call]. - */ - if( apNode[iCur] == 0 || apNode[iCur]->pStart->nType != JX9_TK_LPAREN ){ - continue; - } - iNest = 1; - iLeft = iCur; - /* Find the closing parenthesis */ - iCur++; - while( iCur < nToken ){ - if( apNode[iCur] ){ - if( apNode[iCur]->pStart->nType & JX9_TK_RPAREN /* ')' */){ - /* Decrement nesting level */ - iNest--; - if( iNest <= 0 ){ - break; - } - }else if( apNode[iCur]->pStart->nType & JX9_TK_LPAREN /* '(' */ ){ - /* Increment nesting level */ - iNest++; - } - } - iCur++; - } - if( iCur - iLeft > 1 ){ - /* Recurse and process this expression */ - rc = ExprMakeTree(&(*pGen), &apNode[iLeft + 1], iCur - iLeft - 1); - if( rc != SXRET_OK ){ - return rc; - } - } - /* Free the left and right nodes */ - ExprFreeTree(&(*pGen), apNode[iLeft]); - ExprFreeTree(&(*pGen), apNode[iCur]); - apNode[iLeft] = 0; - apNode[iCur] = 0; - } - /* Handle postfix [i.e: function call, member access] operators with precedence 2 */ - iLeft = -1; - for( iCur = 0 ; iCur < nToken ; ++iCur ){ - if( apNode[iCur] == 0 ){ - continue; - } - pNode = apNode[iCur]; - if( pNode->pOp && pNode->pOp->iPrec == 2 && pNode->pLeft == 0 ){ - if( pNode->pOp->iOp == EXPR_OP_FUNC_CALL ){ - /* Collect function arguments */ - sxi32 iPtr = 0; - sxi32 nFuncTok = 0; - while( nFuncTok + iCur < nToken ){ - if( apNode[nFuncTok+iCur] ){ - if( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_LPAREN /*'('*/ ){ - iPtr++; - }else if ( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_RPAREN /*')'*/){ - iPtr--; - if( iPtr <= 0 ){ - break; - } - } - } - nFuncTok++; - } - if( nFuncTok + iCur >= nToken ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Missing right parenthesis ')'"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - if( iLeft < 0 || !NODE_ISTERM(iLeft) /*|| ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2)*/ ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Invalid function name"); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - if( nFuncTok > 1 ){ - /* Process function arguments */ - rc = ExprProcessFuncArguments(&(*pGen), pNode, &apNode[iCur+1], nFuncTok-1); - if( rc != SXRET_OK ){ - return rc; - } - } - /* Link the node to the tree */ - pNode->pLeft = apNode[iLeft]; - apNode[iLeft] = 0; - for( iPtr = 1; iPtr <= nFuncTok ; iPtr++ ){ - apNode[iCur+iPtr] = 0; - } - }else if (pNode->pOp->iOp == EXPR_OP_SUBSCRIPT ){ - /* Subscripting */ - sxi32 iArrTok = iCur + 1; - sxi32 iNest = 1; - if( iLeft >= 0 && (apNode[iLeft]->xCode == jx9CompileVariable || (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* postfix */) ) ){ - /* Collect index tokens */ - while( iArrTok < nToken ){ - if( apNode[iArrTok] ){ - if( apNode[iArrTok]->pStart->nType & JX9_TK_OSB /*'['*/){ - /* Increment nesting level */ - iNest++; - }else if( apNode[iArrTok]->pStart->nType & JX9_TK_CSB /*']'*/){ - /* Decrement nesting level */ - iNest--; - if( iNest <= 0 ){ - break; - } - } - } - ++iArrTok; - } - if( iArrTok > iCur + 1 ){ - /* Recurse and process this expression */ - rc = ExprMakeTree(&(*pGen), &apNode[iCur+1], iArrTok - iCur - 1); - if( rc != SXRET_OK ){ - return rc; - } - /* Link the node to it's index */ - SySetPut(&pNode->aNodeArgs, (const void *)&apNode[iCur+1]); - } - /* Link the node to the tree */ - pNode->pLeft = apNode[iLeft]; - pNode->pRight = 0; - apNode[iLeft] = 0; - for( iNest = iCur + 1 ; iNest <= iArrTok ; ++iNest ){ - apNode[iNest] = 0; - } - } - }else{ - /* Member access operators [i.e: '.' ] */ - iRight = iCur + 1; - while( iRight < nToken && apNode[iRight] == 0 ){ - iRight++; - } - if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid member name", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - /* Link the node to the tree */ - pNode->pLeft = apNode[iLeft]; - if( pNode->pLeft->pOp == 0 && pNode->pLeft->xCode != jx9CompileVariable ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, - "'%z': Expecting a variable as left operand", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - pNode->pRight = apNode[iRight]; - apNode[iLeft] = apNode[iRight] = 0; - } - } - iLeft = iCur; - } - /* Handle post/pre icrement/decrement [i.e: ++/--] operators with precedence 3 */ - iLeft = -1; - for( iCur = 0 ; iCur < nToken ; ++iCur ){ - if( apNode[iCur] == 0 ){ - continue; - } - pNode = apNode[iCur]; - if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){ - if( iLeft >= 0 && ((apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* Postfix */) - || apNode[iLeft]->xCode == jx9CompileVariable) ){ - /* Link the node to the tree */ - pNode->pLeft = apNode[iLeft]; - apNode[iLeft] = 0; - } - } - iLeft = iCur; - } - iLeft = -1; - for( iCur = nToken - 1 ; iCur >= 0 ; iCur-- ){ - if( apNode[iCur] == 0 ){ - continue; - } - pNode = apNode[iCur]; - if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){ - if( iLeft < 0 || (apNode[iLeft]->pOp == 0 && apNode[iLeft]->xCode != jx9CompileVariable) - || ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* Postfix */) ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z' operator needs l-value", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - /* Link the node to the tree */ - pNode->pLeft = apNode[iLeft]; - apNode[iLeft] = 0; - /* Mark as pre-increment/decrement node */ - pNode->iFlags |= EXPR_NODE_PRE_INCR; - } - iLeft = iCur; - } - /* Handle right associative unary and cast operators [i.e: !, (string), ~...] with precedence 4 */ - iLeft = 0; - for( iCur = nToken - 1 ; iCur >= 0 ; iCur-- ){ - if( apNode[iCur] ){ - pNode = apNode[iCur]; - if( pNode->pOp && pNode->pOp->iPrec == 4 && pNode->pLeft == 0){ - if( iLeft > 0 ){ - /* Link the node to the tree */ - pNode->pLeft = apNode[iLeft]; - apNode[iLeft] = 0; - if( pNode->pLeft && pNode->pLeft->pOp && pNode->pLeft->pOp->iPrec > 4 ){ - if( pNode->pLeft->pLeft == 0 || pNode->pLeft->pRight == 0 ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pLeft->pStart->nLine, "'%z': Missing operand", &pNode->pLeft->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - } - }else{ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing operand", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - } - /* Save terminal position */ - iLeft = iCur; - } - } - /* Process left and non-associative binary operators [i.e: *, /, &&, ||...]*/ - for( i = 7 ; i < 17 ; i++ ){ - iLeft = -1; - for( iCur = 0 ; iCur < nToken ; ++iCur ){ - if( apNode[iCur] == 0 ){ - continue; - } - pNode = apNode[iCur]; - if( pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0 ){ - /* Get the right node */ - iRight = iCur + 1; - while( iRight < nToken && apNode[iRight] == 0 ){ - iRight++; - } - if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - /* Link the node to the tree */ - pNode->pLeft = apNode[iLeft]; - pNode->pRight = apNode[iRight]; - apNode[iLeft] = apNode[iRight] = 0; - } - iLeft = iCur; - } - } - /* Handle the ternary operator. (expr1) ? (expr2) : (expr3) - * Note that we do not need a precedence loop here since - * we are dealing with a single operator. - */ - iLeft = -1; - for( iCur = 0 ; iCur < nToken ; ++iCur ){ - if( apNode[iCur] == 0 ){ - continue; - } - pNode = apNode[iCur]; - if( pNode->pOp && pNode->pOp->iOp == EXPR_OP_QUESTY && pNode->pLeft == 0 ){ - sxi32 iNest = 1; - if( iLeft < 0 || !NODE_ISTERM(iLeft) ){ - /* Missing condition */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Syntax error", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - /* Get the right node */ - iRight = iCur + 1; - while( iRight < nToken ){ - if( apNode[iRight] ){ - if( apNode[iRight]->pOp && apNode[iRight]->pOp->iOp == EXPR_OP_QUESTY && apNode[iRight]->pCond == 0){ - /* Increment nesting level */ - ++iNest; - }else if( apNode[iRight]->pStart->nType & JX9_TK_COLON /*:*/ ){ - /* Decrement nesting level */ - --iNest; - if( iNest <= 0 ){ - break; - } - } - } - iRight++; - } - if( iRight > iCur + 1 ){ - /* Recurse and process the then expression */ - rc = ExprMakeTree(&(*pGen), &apNode[iCur + 1], iRight - iCur - 1); - if( rc != SXRET_OK ){ - return rc; - } - /* Link the node to the tree */ - pNode->pLeft = apNode[iCur + 1]; - }else{ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'then' expression", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - apNode[iCur + 1] = 0; - if( iRight + 1 < nToken ){ - /* Recurse and process the else expression */ - rc = ExprMakeTree(&(*pGen), &apNode[iRight + 1], nToken - iRight - 1); - if( rc != SXRET_OK ){ - return rc; - } - /* Link the node to the tree */ - pNode->pRight = apNode[iRight + 1]; - apNode[iRight + 1] = apNode[iRight] = 0; - }else{ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'else' expression", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - /* Point to the condition */ - pNode->pCond = apNode[iLeft]; - apNode[iLeft] = 0; - break; - } - iLeft = iCur; - } - /* Process right associative binary operators [i.e: '=', '+=', '/='] - * Note: All right associative binary operators have precedence 18 - * so there is no need for a precedence loop here. - */ - iRight = -1; - for( iCur = nToken - 1 ; iCur >= 0 ; iCur--){ - if( apNode[iCur] == 0 ){ - continue; - } - pNode = apNode[iCur]; - if( pNode->pOp && pNode->pOp->iPrec == 18 && pNode->pLeft == 0 ){ - /* Get the left node */ - iLeft = iCur - 1; - while( iLeft >= 0 && apNode[iLeft] == 0 ){ - iLeft--; - } - if( iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - if( ExprIsModifiableValue(apNode[iLeft]) == FALSE ){ - if( pNode->pOp->iVmOp != JX9_OP_STORE ){ - /* Left operand must be a modifiable l-value */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, - "'%z': Left operand must be a modifiable l-value", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - } - /* Link the node to the tree (Reverse) */ - pNode->pLeft = apNode[iRight]; - pNode->pRight = apNode[iLeft]; - apNode[iLeft] = apNode[iRight] = 0; - } - iRight = iCur; - } - /* Process the lowest precedence operator (22, comma) */ - iLeft = -1; - for( iCur = 0 ; iCur < nToken ; ++iCur ){ - if( apNode[iCur] == 0 ){ - continue; - } - pNode = apNode[iCur]; - if( pNode->pOp && pNode->pOp->iPrec == 22 /* ',' */ && pNode->pLeft == 0 ){ - /* Get the right node */ - iRight = iCur + 1; - while( iRight < nToken && apNode[iRight] == 0 ){ - iRight++; - } - if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ - /* Syntax error */ - rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - /* Link the node to the tree */ - pNode->pLeft = apNode[iLeft]; - pNode->pRight = apNode[iRight]; - apNode[iLeft] = apNode[iRight] = 0; - } - iLeft = iCur; - } - /* Point to the root of the expression tree */ - for( iCur = 1 ; iCur < nToken ; ++iCur ){ - if( apNode[iCur] ){ - if( (apNode[iCur]->pOp || apNode[iCur]->xCode ) && apNode[0] != 0){ - rc = jx9GenCompileError(pGen, E_ERROR, apNode[iCur]->pStart->nLine, "Unexpected token '%z'", &apNode[iCur]->pStart->sData); - if( rc != SXERR_ABORT ){ - rc = SXERR_SYNTAX; - } - return rc; - } - apNode[0] = apNode[iCur]; - apNode[iCur] = 0; - } - } - return SXRET_OK; - } - /* - * Build an expression tree from the freshly extracted raw tokens. - * If successful, the root of the tree is stored in ppRoot. - * When errors, JX9 take care of generating the appropriate error message. - * This is the public interface used by the most code generator routines. - */ -JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot) -{ - jx9_expr_node **apNode; - jx9_expr_node *pNode; - sxi32 rc; - /* Reset node container */ - SySetReset(pExprNode); - pNode = 0; /* Prevent compiler warning */ - /* Extract nodes one after one until we hit the end of the input */ - while( pGen->pIn < pGen->pEnd ){ - rc = ExprExtractNode(&(*pGen), &pNode); - if( rc != SXRET_OK ){ - return rc; - } - /* Save the extracted node */ - SySetPut(pExprNode, (const void *)&pNode); - } - if( SySetUsed(pExprNode) < 1 ){ - /* Empty expression [i.e: A semi-colon;] */ - *ppRoot = 0; - return SXRET_OK; - } - apNode = (jx9_expr_node **)SySetBasePtr(pExprNode); - /* Make sure we are dealing with valid nodes */ - rc = ExprVerifyNodes(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode)); - if( rc != SXRET_OK ){ - /* Don't worry about freeing memory, upper layer will - * cleanup the mess left behind. - */ - *ppRoot = 0; - return rc; - } - /* Build the tree */ - rc = ExprMakeTree(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode)); - if( rc != SXRET_OK ){ - /* Something goes wrong [i.e: Syntax error] */ - *ppRoot = 0; - return rc; - } - /* Point to the root of the tree */ - *ppRoot = apNode[0]; - return SXRET_OK; -} -/* - * ---------------------------------------------------------- - * File: jx9_vfs.c - * MD5: 8b73046a366acaf6aa7227c2133e16c0 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: vfs.c v2.1 Ubuntu 2012-12-13 00:013 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* - * This file implement a virtual file systems (VFS) for the JX9 engine. - */ -/* - * Given a string containing the path of a file or directory, this function - * return the parent directory's path. - */ -JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen) -{ - const char *zEnd = &zPath[nByte - 1]; - int c, d; - c = d = '/'; -#ifdef __WINNT__ - d = '\\'; -#endif - while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ - zEnd--; - } - *pLen = (int)(zEnd-zPath); -#ifdef __WINNT__ - if( (*pLen) == (int)sizeof(char) && zPath[0] == '/' ){ - /* Normalize path on windows */ - return "\\"; - } -#endif - if( zEnd == zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d) ){ - /* No separator, return "." as the current directory */ - *pLen = sizeof(char); - return "."; - } - if( (*pLen) == 0 ){ - *pLen = sizeof(char); -#ifdef __WINNT__ - return "\\"; -#else - return "/"; -#endif - } - return zPath; -} -/* - * Omit the vfs layer implementation from the built if the JX9_DISABLE_BUILTIN_FUNC directive is defined. - */ -#ifndef JX9_DISABLE_BUILTIN_FUNC -/* - * bool chdir(string $directory) - * Change the current directory. - * Parameters - * $directory - * The new current directory - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_chdir(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xChdir == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xChdir(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool chroot(string $directory) - * Change the root directory. - * Parameters - * $directory - * The path to change the root directory to - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_chroot(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xChroot == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xChroot(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * string getcwd(void) - * Gets the current working directory. - * Parameters - * None - * Return - * Returns the current working directory on success, or FALSE on failure. - */ -static int jx9Vfs_getcwd(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_vfs *pVfs; - int rc; - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xGetcwd == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - jx9_result_string(pCtx, "", 0); - /* Perform the requested operation */ - rc = pVfs->xGetcwd(pCtx); - if( rc != JX9_OK ){ - /* Error, return FALSE */ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * bool rmdir(string $directory) - * Removes directory. - * Parameters - * $directory - * The path to the directory - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_rmdir(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xRmdir == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xRmdir(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool is_dir(string $filename) - * Tells whether the given filename is a directory. - * Parameters - * $filename - * Path to the file. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_is_dir(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xIsdir == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xIsdir(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool mkdir(string $pathname[, int $mode = 0777]) - * Make a directory. - * Parameters - * $pathname - * The directory path. - * $mode - * The mode is 0777 by default, which means the widest possible access. - * Note: - * mode is ignored on Windows. - * Note that you probably want to specify the mode as an octal number, which means - * it should have a leading zero. The mode is also modified by the current umask - * which you can change using umask(). - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_mkdir(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int iRecursive = 0; - const char *zPath; - jx9_vfs *pVfs; - int iMode, rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xMkdir == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); -#ifdef __WINNT__ - iMode = 0; -#else - /* Assume UNIX */ - iMode = 0777; -#endif - if( nArg > 1 ){ - iMode = jx9_value_to_int(apArg[1]); - if( nArg > 2 ){ - iRecursive = jx9_value_to_bool(apArg[2]); - } - } - /* Perform the requested operation */ - rc = pVfs->xMkdir(zPath, iMode, iRecursive); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool rename(string $oldname, string $newname) - * Attempts to rename oldname to newname. - * Parameters - * $oldname - * Old name. - * $newname - * New name. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_rename(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zOld, *zNew; - jx9_vfs *pVfs; - int rc; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xRename == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - zOld = jx9_value_to_string(apArg[0], 0); - zNew = jx9_value_to_string(apArg[1], 0); - rc = pVfs->xRename(zOld, zNew); - /* IO result */ - jx9_result_bool(pCtx, rc == JX9_OK ); - return JX9_OK; -} -/* - * string realpath(string $path) - * Returns canonicalized absolute pathname. - * Parameters - * $path - * Target path. - * Return - * Canonicalized absolute pathname on success. or FALSE on failure. - */ -static int jx9Vfs_realpath(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xRealpath == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Set an empty string untnil the underlying OS interface change that */ - jx9_result_string(pCtx, "", 0); - /* Perform the requested operation */ - zPath = jx9_value_to_string(apArg[0], 0); - rc = pVfs->xRealpath(zPath, pCtx); - if( rc != JX9_OK ){ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * int sleep(int $seconds) - * Delays the program execution for the given number of seconds. - * Parameters - * $seconds - * Halt time in seconds. - * Return - * Zero on success or FALSE on failure. - */ -static int jx9Vfs_sleep(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_vfs *pVfs; - int rc, nSleep; - if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xSleep == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Amount to sleep */ - nSleep = jx9_value_to_int(apArg[0]); - if( nSleep < 0 ){ - /* Invalid value, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation (Microseconds) */ - rc = pVfs->xSleep((unsigned int)(nSleep * SX_USEC_PER_SEC)); - if( rc != JX9_OK ){ - /* Return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* Return zero */ - jx9_result_int(pCtx, 0); - } - return JX9_OK; -} -/* - * void usleep(int $micro_seconds) - * Delays program execution for the given number of micro seconds. - * Parameters - * $micro_seconds - * Halt time in micro seconds. A micro second is one millionth of a second. - * Return - * None. - */ -static int jx9Vfs_usleep(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_vfs *pVfs; - int nSleep; - if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){ - /* Missing/Invalid argument, return immediately */ - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xSleep == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS", - jx9_function_name(pCtx) - ); - return JX9_OK; - } - /* Amount to sleep */ - nSleep = jx9_value_to_int(apArg[0]); - if( nSleep < 0 ){ - /* Invalid value, return immediately */ - return JX9_OK; - } - /* Perform the requested operation (Microseconds) */ - pVfs->xSleep((unsigned int)nSleep); - return JX9_OK; -} -/* - * bool unlink (string $filename) - * Delete a file. - * Parameters - * $filename - * Path to the file. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_unlink(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xUnlink == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xUnlink(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool chmod(string $filename, int $mode) - * Attempts to change the mode of the specified file to that given in mode. - * Parameters - * $filename - * Path to the file. - * $mode - * Mode (Must be an integer) - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_chmod(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int iMode; - int rc; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xChmod == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Extract the mode */ - iMode = jx9_value_to_int(apArg[1]); - /* Perform the requested operation */ - rc = pVfs->xChmod(zPath, iMode); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool chown(string $filename, string $user) - * Attempts to change the owner of the file filename to user user. - * Parameters - * $filename - * Path to the file. - * $user - * Username. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_chown(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath, *zUser; - jx9_vfs *pVfs; - int rc; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xChown == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Extract the user */ - zUser = jx9_value_to_string(apArg[1], 0); - /* Perform the requested operation */ - rc = pVfs->xChown(zPath, zUser); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool chgrp(string $filename, string $group) - * Attempts to change the group of the file filename to group. - * Parameters - * $filename - * Path to the file. - * $group - * groupname. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_chgrp(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath, *zGroup; - jx9_vfs *pVfs; - int rc; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xChgrp == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Extract the user */ - zGroup = jx9_value_to_string(apArg[1], 0); - /* Perform the requested operation */ - rc = pVfs->xChgrp(zPath, zGroup); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * int64 disk_free_space(string $directory) - * Returns available space on filesystem or disk partition. - * Parameters - * $directory - * A directory of the filesystem or disk partition. - * Return - * Returns the number of available bytes as a 64-bit integer or FALSE on failure. - */ -static int jx9Vfs_disk_free_space(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_int64 iSize; - jx9_vfs *pVfs; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xFreeSpace == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - iSize = pVfs->xFreeSpace(zPath); - /* IO return value */ - jx9_result_int64(pCtx, iSize); - return JX9_OK; -} -/* - * int64 disk_total_space(string $directory) - * Returns the total size of a filesystem or disk partition. - * Parameters - * $directory - * A directory of the filesystem or disk partition. - * Return - * Returns the number of available bytes as a 64-bit integer or FALSE on failure. - */ -static int jx9Vfs_disk_total_space(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_int64 iSize; - jx9_vfs *pVfs; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xTotalSpace == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - iSize = pVfs->xTotalSpace(zPath); - /* IO return value */ - jx9_result_int64(pCtx, iSize); - return JX9_OK; -} -/* - * bool file_exists(string $filename) - * Checks whether a file or directory exists. - * Parameters - * $filename - * Path to the file. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_file_exists(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xFileExists == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xFileExists(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * int64 file_size(string $filename) - * Gets the size for the given file. - * Parameters - * $filename - * Path to the file. - * Return - * File size on success or FALSE on failure. - */ -static int jx9Vfs_file_size(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_int64 iSize; - jx9_vfs *pVfs; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xFileSize == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - iSize = pVfs->xFileSize(zPath); - /* IO return value */ - jx9_result_int64(pCtx, iSize); - return JX9_OK; -} -/* - * int64 fileatime(string $filename) - * Gets the last access time of the given file. - * Parameters - * $filename - * Path to the file. - * Return - * File atime on success or FALSE on failure. - */ -static int jx9Vfs_file_atime(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_int64 iTime; - jx9_vfs *pVfs; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xFileAtime == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - iTime = pVfs->xFileAtime(zPath); - /* IO return value */ - jx9_result_int64(pCtx, iTime); - return JX9_OK; -} -/* - * int64 filemtime(string $filename) - * Gets file modification time. - * Parameters - * $filename - * Path to the file. - * Return - * File mtime on success or FALSE on failure. - */ -static int jx9Vfs_file_mtime(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_int64 iTime; - jx9_vfs *pVfs; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xFileMtime == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - iTime = pVfs->xFileMtime(zPath); - /* IO return value */ - jx9_result_int64(pCtx, iTime); - return JX9_OK; -} -/* - * int64 filectime(string $filename) - * Gets inode change time of file. - * Parameters - * $filename - * Path to the file. - * Return - * File ctime on success or FALSE on failure. - */ -static int jx9Vfs_file_ctime(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_int64 iTime; - jx9_vfs *pVfs; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xFileCtime == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - iTime = pVfs->xFileCtime(zPath); - /* IO return value */ - jx9_result_int64(pCtx, iTime); - return JX9_OK; -} -/* - * bool is_file(string $filename) - * Tells whether the filename is a regular file. - * Parameters - * $filename - * Path to the file. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_is_file(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xIsfile == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xIsfile(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool is_link(string $filename) - * Tells whether the filename is a symbolic link. - * Parameters - * $filename - * Path to the file. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_is_link(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xIslink == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xIslink(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool is_readable(string $filename) - * Tells whether a file exists and is readable. - * Parameters - * $filename - * Path to the file. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_is_readable(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xReadable == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xReadable(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool is_writable(string $filename) - * Tells whether the filename is writable. - * Parameters - * $filename - * Path to the file. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_is_writable(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xWritable == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xWritable(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool is_executable(string $filename) - * Tells whether the filename is executable. - * Parameters - * $filename - * Path to the file. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_is_executable(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xExecutable == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xExecutable(zPath); - /* IO return value */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * string filetype(string $filename) - * Gets file type. - * Parameters - * $filename - * Path to the file. - * Return - * The type of the file. Possible values are fifo, char, dir, block, link - * file, socket and unknown. - */ -static int jx9Vfs_filetype(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - jx9_vfs *pVfs; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return 'unknown' */ - jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xFiletype == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the desired directory */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Set the empty string as the default return value */ - jx9_result_string(pCtx, "", 0); - /* Perform the requested operation */ - pVfs->xFiletype(zPath, pCtx); - return JX9_OK; -} -/* - * array stat(string $filename) - * Gives information about a file. - * Parameters - * $filename - * Path to the file. - * Return - * An associative array on success holding the following entries on success - * 0 dev device number - * 1 ino inode number (zero on windows) - * 2 mode inode protection mode - * 3 nlink number of links - * 4 uid userid of owner (zero on windows) - * 5 gid groupid of owner (zero on windows) - * 6 rdev device type, if inode device - * 7 size size in bytes - * 8 atime time of last access (Unix timestamp) - * 9 mtime time of last modification (Unix timestamp) - * 10 ctime time of last inode change (Unix timestamp) - * 11 blksize blocksize of filesystem IO (zero on windows) - * 12 blocks number of 512-byte blocks allocated. - * Note: - * FALSE is returned on failure. - */ -static int jx9Vfs_stat(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pArray, *pValue; - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xStat == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Create the array and the working value */ - pArray = jx9_context_new_array(pCtx); - pValue = jx9_context_new_scalar(pCtx); - if( pArray == 0 || pValue == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the file path */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xStat(zPath, pArray, pValue); - if( rc != JX9_OK ){ - /* IO error, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* Return the associative array */ - jx9_result_value(pCtx, pArray); - } - /* Don't worry about freeing memory here, everything will be released - * automatically as soon we return from this function. */ - return JX9_OK; -} -/* - * array lstat(string $filename) - * Gives information about a file or symbolic link. - * Parameters - * $filename - * Path to the file. - * Return - * An associative array on success holding the following entries on success - * 0 dev device number - * 1 ino inode number (zero on windows) - * 2 mode inode protection mode - * 3 nlink number of links - * 4 uid userid of owner (zero on windows) - * 5 gid groupid of owner (zero on windows) - * 6 rdev device type, if inode device - * 7 size size in bytes - * 8 atime time of last access (Unix timestamp) - * 9 mtime time of last modification (Unix timestamp) - * 10 ctime time of last inode change (Unix timestamp) - * 11 blksize blocksize of filesystem IO (zero on windows) - * 12 blocks number of 512-byte blocks allocated. - * Note: - * FALSE is returned on failure. - */ -static int jx9Vfs_lstat(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pArray, *pValue; - const char *zPath; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xlStat == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Create the array and the working value */ - pArray = jx9_context_new_array(pCtx); - pValue = jx9_context_new_scalar(pCtx); - if( pArray == 0 || pValue == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the file path */ - zPath = jx9_value_to_string(apArg[0], 0); - /* Perform the requested operation */ - rc = pVfs->xlStat(zPath, pArray, pValue); - if( rc != JX9_OK ){ - /* IO error, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* Return the associative array */ - jx9_result_value(pCtx, pArray); - } - /* Don't worry about freeing memory here, everything will be released - * automatically as soon we return from this function. */ - return JX9_OK; -} -/* - * string getenv(string $varname) - * Gets the value of an environment variable. - * Parameters - * $varname - * The variable name. - * Return - * Returns the value of the environment variable varname, or FALSE if the environment - * variable varname does not exist. - */ -static int jx9Vfs_getenv(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zEnv; - jx9_vfs *pVfs; - int iLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xGetenv == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the environment variable */ - zEnv = jx9_value_to_string(apArg[0], &iLen); - /* Set a boolean FALSE as the default return value */ - jx9_result_bool(pCtx, 0); - if( iLen < 1 ){ - /* Empty string */ - return JX9_OK; - } - /* Perform the requested operation */ - pVfs->xGetenv(zEnv, pCtx); - return JX9_OK; -} -/* - * bool putenv(string $settings) - * Set the value of an environment variable. - * Parameters - * $setting - * The setting, like "FOO=BAR" - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_putenv(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zName, *zValue; - char *zSettings, *zEnd; - jx9_vfs *pVfs; - int iLen, rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the setting variable */ - zSettings = (char *)jx9_value_to_string(apArg[0], &iLen); - if( iLen < 1 ){ - /* Empty string, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Parse the setting */ - zEnd = &zSettings[iLen]; - zValue = 0; - zName = zSettings; - while( zSettings < zEnd ){ - if( zSettings[0] == '=' ){ - /* Null terminate the name */ - zSettings[0] = 0; - zValue = &zSettings[1]; - break; - } - zSettings++; - } - /* Install the environment variable in the $_Env array */ - if( zValue == 0 || zName[0] == 0 || zValue >= zEnd || zName >= zValue ){ - /* Invalid settings, retun FALSE */ - jx9_result_bool(pCtx, 0); - if( zSettings < zEnd ){ - zSettings[0] = '='; - } - return JX9_OK; - } - jx9_vm_config(pCtx->pVm, JX9_VM_CONFIG_ENV_ATTR, zName, zValue, (int)(zEnd-zValue)); - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xSetenv == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - zSettings[0] = '='; - return JX9_OK; - } - /* Perform the requested operation */ - rc = pVfs->xSetenv(zName, zValue); - jx9_result_bool(pCtx, rc == JX9_OK ); - zSettings[0] = '='; - return JX9_OK; -} -/* - * bool touch(string $filename[, int64 $time = time()[, int64 $atime]]) - * Sets access and modification time of file. - * Note: On windows - * If the file does not exists, it will not be created. - * Parameters - * $filename - * The name of the file being touched. - * $time - * The touch time. If time is not supplied, the current system time is used. - * $atime - * If present, the access time of the given filename is set to the value of atime. - * Otherwise, it is set to the value passed to the time parameter. If neither are - * present, the current system time is used. - * Return - * TRUE on success or FALSE on failure. -*/ -static int jx9Vfs_touch(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_int64 nTime, nAccess; - const char *zFile; - jx9_vfs *pVfs; - int rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xTouch == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - nTime = nAccess = -1; - zFile = jx9_value_to_string(apArg[0], 0); - if( nArg > 1 ){ - nTime = jx9_value_to_int64(apArg[1]); - if( nArg > 2 ){ - nAccess = jx9_value_to_int64(apArg[1]); - }else{ - nAccess = nTime; - } - } - rc = pVfs->xTouch(zFile, nTime, nAccess); - /* IO result */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * Path processing functions that do not need access to the VFS layer - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * string dirname(string $path) - * Returns parent directory's path. - * Parameters - * $path - * Target path. - * On Windows, both slash (/) and backslash (\) are used as directory separator character. - * In other environments, it is the forward slash (/). - * Return - * The path of the parent directory. If there are no slashes in path, a dot ('.') - * is returned, indicating the current directory. - */ -static int jx9Builtin_dirname(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath, *zDir; - int iLen, iDirlen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Point to the target path */ - zPath = jx9_value_to_string(apArg[0], &iLen); - if( iLen < 1 ){ - /* Reuturn "." */ - jx9_result_string(pCtx, ".", sizeof(char)); - return JX9_OK; - } - /* Perform the requested operation */ - zDir = jx9ExtractDirName(zPath, iLen, &iDirlen); - /* Return directory name */ - jx9_result_string(pCtx, zDir, iDirlen); - return JX9_OK; -} -/* - * string basename(string $path[, string $suffix ]) - * Returns trailing name component of path. - * Parameters - * $path - * Target path. - * On Windows, both slash (/) and backslash (\) are used as directory separator character. - * In other environments, it is the forward slash (/). - * $suffix - * If the name component ends in suffix this will also be cut off. - * Return - * The base name of the given path. - */ -static int jx9Builtin_basename(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath, *zBase, *zEnd; - int c, d, iLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - c = d = '/'; -#ifdef __WINNT__ - d = '\\'; -#endif - /* Point to the target path */ - zPath = jx9_value_to_string(apArg[0], &iLen); - if( iLen < 1 ){ - /* Empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Perform the requested operation */ - zEnd = &zPath[iLen - 1]; - /* Ignore trailing '/' */ - while( zEnd > zPath && ( (int)zEnd[0] == c || (int)zEnd[0] == d ) ){ - zEnd--; - } - iLen = (int)(&zEnd[1]-zPath); - while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ - zEnd--; - } - zBase = (zEnd > zPath) ? &zEnd[1] : zPath; - zEnd = &zPath[iLen]; - if( nArg > 1 && jx9_value_is_string(apArg[1]) ){ - const char *zSuffix; - int nSuffix; - /* Strip suffix */ - zSuffix = jx9_value_to_string(apArg[1], &nSuffix); - if( nSuffix > 0 && nSuffix < iLen && SyMemcmp(&zEnd[-nSuffix], zSuffix, nSuffix) == 0 ){ - zEnd -= nSuffix; - } - } - /* Store the basename */ - jx9_result_string(pCtx, zBase, (int)(zEnd-zBase)); - return JX9_OK; -} -/* - * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ]) - * Returns information about a file path. - * Parameter - * $path - * The path to be parsed. - * $options - * If present, specifies a specific element to be returned; one of - * PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION or PATHINFO_FILENAME. - * Return - * If the options parameter is not passed, an associative array containing the following - * elements is returned: dirname, basename, extension (if any), and filename. - * If options is present, returns a string containing the requested element. - */ -typedef struct path_info path_info; -struct path_info -{ - SyString sDir; /* Directory [i.e: /var/www] */ - SyString sBasename; /* Basename [i.e httpd.conf] */ - SyString sExtension; /* File extension [i.e xml, pdf..] */ - SyString sFilename; /* Filename */ -}; -/* - * Extract path fields. - */ -static sxi32 ExtractPathInfo(const char *zPath, int nByte, path_info *pOut) -{ - const char *zPtr, *zEnd = &zPath[nByte - 1]; - SyString *pCur; - int c, d; - c = d = '/'; -#ifdef __WINNT__ - d = '\\'; -#endif - /* Zero the structure */ - SyZero(pOut, sizeof(path_info)); - /* Handle special case */ - if( nByte == sizeof(char) && ( (int)zPath[0] == c || (int)zPath[0] == d ) ){ -#ifdef __WINNT__ - SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char)); -#else - SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char)); -#endif - return SXRET_OK; - } - /* Extract the basename */ - while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ - zEnd--; - } - zPtr = (zEnd > zPath) ? &zEnd[1] : zPath; - zEnd = &zPath[nByte]; - /* dirname */ - pCur = &pOut->sDir; - SyStringInitFromBuf(pCur, zPath, zPtr-zPath); - if( pCur->nByte > 1 ){ - SyStringTrimTrailingChar(pCur, '/'); -#ifdef __WINNT__ - SyStringTrimTrailingChar(pCur, '\\'); -#endif - }else if( (int)zPath[0] == c || (int)zPath[0] == d ){ -#ifdef __WINNT__ - SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char)); -#else - SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char)); -#endif - } - /* basename/filename */ - pCur = &pOut->sBasename; - SyStringInitFromBuf(pCur, zPtr, zEnd-zPtr); - SyStringTrimLeadingChar(pCur, '/'); -#ifdef __WINNT__ - SyStringTrimLeadingChar(pCur, '\\'); -#endif - SyStringDupPtr(&pOut->sFilename, pCur); - if( pCur->nByte > 0 ){ - /* extension */ - zEnd--; - while( zEnd > pCur->zString /*basename*/ && zEnd[0] != '.' ){ - zEnd--; - } - if( zEnd > pCur->zString ){ - zEnd++; /* Jump leading dot */ - SyStringInitFromBuf(&pOut->sExtension, zEnd, &zPath[nByte]-zEnd); - /* Fix filename */ - pCur = &pOut->sFilename; - if( pCur->nByte > SyStringLength(&pOut->sExtension) ){ - pCur->nByte -= 1 + SyStringLength(&pOut->sExtension); - } - } - } - return SXRET_OK; -} -/* - * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ]) - * See block comment above. - */ -static int jx9Builtin_pathinfo(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zPath; - path_info sInfo; - SyString *pComp; - int iLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid argument, return the empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Point to the target path */ - zPath = jx9_value_to_string(apArg[0], &iLen); - if( iLen < 1 ){ - /* Empty string */ - jx9_result_string(pCtx, "", 0); - return JX9_OK; - } - /* Extract path info */ - ExtractPathInfo(zPath, iLen, &sInfo); - if( nArg > 1 && jx9_value_is_int(apArg[1]) ){ - /* Return path component */ - int nComp = jx9_value_to_int(apArg[1]); - switch(nComp){ - case 1: /* PATHINFO_DIRNAME */ - pComp = &sInfo.sDir; - if( pComp->nByte > 0 ){ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - }else{ - /* Expand the empty string */ - jx9_result_string(pCtx, "", 0); - } - break; - case 2: /*PATHINFO_BASENAME*/ - pComp = &sInfo.sBasename; - if( pComp->nByte > 0 ){ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - }else{ - /* Expand the empty string */ - jx9_result_string(pCtx, "", 0); - } - break; - case 3: /*PATHINFO_EXTENSION*/ - pComp = &sInfo.sExtension; - if( pComp->nByte > 0 ){ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - }else{ - /* Expand the empty string */ - jx9_result_string(pCtx, "", 0); - } - break; - case 4: /*PATHINFO_FILENAME*/ - pComp = &sInfo.sFilename; - if( pComp->nByte > 0 ){ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - }else{ - /* Expand the empty string */ - jx9_result_string(pCtx, "", 0); - } - break; - default: - /* Expand the empty string */ - jx9_result_string(pCtx, "", 0); - break; - } - }else{ - /* Return an associative array */ - jx9_value *pArray, *pValue; - pArray = jx9_context_new_array(pCtx); - pValue = jx9_context_new_scalar(pCtx); - if( pArray == 0 || pValue == 0 ){ - /* Out of mem, return NULL */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* dirname */ - pComp = &sInfo.sDir; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - /* Perform the insertion */ - jx9_array_add_strkey_elem(pArray, "dirname", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - /* basername */ - pComp = &sInfo.sBasename; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - /* Perform the insertion */ - jx9_array_add_strkey_elem(pArray, "basename", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - /* extension */ - pComp = &sInfo.sExtension; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - /* Perform the insertion */ - jx9_array_add_strkey_elem(pArray, "extension", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - /* filename */ - pComp = &sInfo.sFilename; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - /* Perform the insertion */ - jx9_array_add_strkey_elem(pArray, "filename", pValue); /* Will make it's own copy */ - } - /* Return the created array */ - jx9_result_value(pCtx, pArray); - /* Don't worry about freeing memory, everything will be released - * automatically as soon we return from this foreign function. - */ - } - return JX9_OK; -} -/* - * Globbing implementation extracted from the sqlite3 source tree. - * Original author: D. Richard Hipp (http://www.sqlite.org) - * Status: Public Domain - */ -typedef unsigned char u8; -/* An array to map all upper-case characters into their corresponding -** lower-case character. -** -** SQLite only considers US-ASCII (or EBCDIC) characters. We do not -** handle case conversions for the UTF character set since the tables -** involved are nearly as big or bigger than SQLite itself. -*/ -static const unsigned char sqlite3UpperToLower[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, - 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, - 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, - 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, - 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, - 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, - 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, - 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, - 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, - 252, 253, 254, 255 -}; -#define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; } -/* -** Assuming zIn points to the first byte of a UTF-8 character, -** advance zIn to point to the first byte of the next UTF-8 character. -*/ -#define SQLITE_SKIP_UTF8(zIn) { \ - if( (*(zIn++))>=0xc0 ){ \ - while( (*zIn & 0xc0)==0x80 ){ zIn++; } \ - } \ -} -/* -** Compare two UTF-8 strings for equality where the first string can -** potentially be a "glob" expression. Return true (1) if they -** are the same and false (0) if they are different. -** -** Globbing rules: -** -** '*' Matches any sequence of zero or more characters. -** -** '?' Matches exactly one character. -** -** [...] Matches one character from the enclosed list of -** characters. -** -** [^...] Matches one character not in the enclosed list. -** -** With the [...] and [^...] matching, a ']' character can be included -** in the list by making it the first character after '[' or '^'. A -** range of characters can be specified using '-'. Example: -** "[a-z]" matches any single lower-case letter. To match a '-', make -** it the last character in the list. -** -** This routine is usually quick, but can be N**2 in the worst case. -** -** Hints: to match '*' or '?', put them in "[]". Like this: -** -** abc[*]xyz Matches "abc*xyz" only -*/ -static int patternCompare( - const u8 *zPattern, /* The glob pattern */ - const u8 *zString, /* The string to compare against the glob */ - const int esc, /* The escape character */ - int noCase -){ - int c, c2; - int invert; - int seen; - u8 matchOne = '?'; - u8 matchAll = '*'; - u8 matchSet = '['; - int prevEscape = 0; /* True if the previous character was 'escape' */ - - if( !zPattern || !zString ) return 0; - while( (c = jx9Utf8Read(zPattern, 0, &zPattern))!=0 ){ - if( !prevEscape && c==matchAll ){ - while( (c= jx9Utf8Read(zPattern, 0, &zPattern)) == matchAll - || c == matchOne ){ - if( c==matchOne && jx9Utf8Read(zString, 0, &zString)==0 ){ - return 0; - } - } - if( c==0 ){ - return 1; - }else if( c==esc ){ - c = jx9Utf8Read(zPattern, 0, &zPattern); - if( c==0 ){ - return 0; - } - }else if( c==matchSet ){ - if( (esc==0) || (matchSet<0x80) ) return 0; - while( *zString && patternCompare(&zPattern[-1], zString, esc, noCase)==0 ){ - SQLITE_SKIP_UTF8(zString); - } - return *zString!=0; - } - while( (c2 = jx9Utf8Read(zString, 0, &zString))!=0 ){ - if( noCase ){ - GlogUpperToLower(c2); - GlogUpperToLower(c); - while( c2 != 0 && c2 != c ){ - c2 = jx9Utf8Read(zString, 0, &zString); - GlogUpperToLower(c2); - } - }else{ - while( c2 != 0 && c2 != c ){ - c2 = jx9Utf8Read(zString, 0, &zString); - } - } - if( c2==0 ) return 0; - if( patternCompare(zPattern, zString, esc, noCase) ) return 1; - } - return 0; - }else if( !prevEscape && c==matchOne ){ - if( jx9Utf8Read(zString, 0, &zString)==0 ){ - return 0; - } - }else if( c==matchSet ){ - int prior_c = 0; - if( esc == 0 ) return 0; - seen = 0; - invert = 0; - c = jx9Utf8Read(zString, 0, &zString); - if( c==0 ) return 0; - c2 = jx9Utf8Read(zPattern, 0, &zPattern); - if( c2=='^' ){ - invert = 1; - c2 = jx9Utf8Read(zPattern, 0, &zPattern); - } - if( c2==']' ){ - if( c==']' ) seen = 1; - c2 = jx9Utf8Read(zPattern, 0, &zPattern); - } - while( c2 && c2!=']' ){ - if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){ - c2 = jx9Utf8Read(zPattern, 0, &zPattern); - if( c>=prior_c && c<=c2 ) seen = 1; - prior_c = 0; - }else{ - if( c==c2 ){ - seen = 1; - } - prior_c = c2; - } - c2 = jx9Utf8Read(zPattern, 0, &zPattern); - } - if( c2==0 || (seen ^ invert)==0 ){ - return 0; - } - }else if( esc==c && !prevEscape ){ - prevEscape = 1; - }else{ - c2 = jx9Utf8Read(zString, 0, &zString); - if( noCase ){ - GlogUpperToLower(c); - GlogUpperToLower(c2); - } - if( c!=c2 ){ - return 0; - } - prevEscape = 0; - } - } - return *zString==0; -} -/* - * Wrapper around patternCompare() defined above. - * See block comment above for more information. - */ -static int Glob(const unsigned char *zPattern, const unsigned char *zString, int iEsc, int CaseCompare) -{ - int rc; - if( iEsc < 0 ){ - iEsc = '\\'; - } - rc = patternCompare(zPattern, zString, iEsc, CaseCompare); - return rc; -} -/* - * bool fnmatch(string $pattern, string $string[, int $flags = 0 ]) - * Match filename against a pattern. - * Parameters - * $pattern - * The shell wildcard pattern. - * $string - * The tested string. - * $flags - * A list of possible flags: - * FNM_NOESCAPE Disable backslash escaping. - * FNM_PATHNAME Slash in string only matches slash in the given pattern. - * FNM_PERIOD Leading period in string must be exactly matched by period in the given pattern. - * FNM_CASEFOLD Caseless match. - * Return - * TRUE if there is a match, FALSE otherwise. - */ -static int jx9Builtin_fnmatch(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString, *zPattern; - int iEsc = '\\'; - int noCase = 0; - int rc; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the pattern and the string */ - zPattern = jx9_value_to_string(apArg[0], 0); - zString = jx9_value_to_string(apArg[1], 0); - /* Extract the flags if avaialble */ - if( nArg > 2 && jx9_value_is_int(apArg[2]) ){ - rc = jx9_value_to_int(apArg[2]); - if( rc & 0x01 /*FNM_NOESCAPE*/){ - iEsc = 0; - } - if( rc & 0x08 /*FNM_CASEFOLD*/){ - noCase = 1; - } - } - /* Go globbing */ - rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, noCase); - /* Globbing result */ - jx9_result_bool(pCtx, rc); - return JX9_OK; -} -/* - * bool strglob(string $pattern, string $string) - * Match string against a pattern. - * Parameters - * $pattern - * The shell wildcard pattern. - * $string - * The tested string. - * Return - * TRUE if there is a match, FALSE otherwise. - */ -static int jx9Builtin_strglob(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zString, *zPattern; - int iEsc = '\\'; - int rc; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the pattern and the string */ - zPattern = jx9_value_to_string(apArg[0], 0); - zString = jx9_value_to_string(apArg[1], 0); - /* Go globbing */ - rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, 0); - /* Globbing result */ - jx9_result_bool(pCtx, rc); - return JX9_OK; -} -/* - * bool link(string $target, string $link) - * Create a hard link. - * Parameters - * $target - * Target of the link. - * $link - * The link name. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_link(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zTarget, *zLink; - jx9_vfs *pVfs; - int rc; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xLink == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the given arguments */ - zTarget = jx9_value_to_string(apArg[0], 0); - zLink = jx9_value_to_string(apArg[1], 0); - /* Perform the requested operation */ - rc = pVfs->xLink(zTarget, zLink, 0/*Not a symbolic link */); - /* IO result */ - jx9_result_bool(pCtx, rc == JX9_OK ); - return JX9_OK; -} -/* - * bool symlink(string $target, string $link) - * Creates a symbolic link. - * Parameters - * $target - * Target of the link. - * $link - * The link name. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Vfs_symlink(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zTarget, *zLink; - jx9_vfs *pVfs; - int rc; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xLink == 0 ){ - /* IO routine not implemented, return NULL */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", - jx9_function_name(pCtx) - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the given arguments */ - zTarget = jx9_value_to_string(apArg[0], 0); - zLink = jx9_value_to_string(apArg[1], 0); - /* Perform the requested operation */ - rc = pVfs->xLink(zTarget, zLink, 1/*A symbolic link */); - /* IO result */ - jx9_result_bool(pCtx, rc == JX9_OK ); - return JX9_OK; -} -/* - * int umask([ int $mask ]) - * Changes the current umask. - * Parameters - * $mask - * The new umask. - * Return - * umask() without arguments simply returns the current umask. - * Otherwise the old umask is returned. - */ -static int jx9Vfs_umask(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int iOld, iNew; - jx9_vfs *pVfs; - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xUmask == 0 ){ - /* IO routine not implemented, return -1 */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS", - jx9_function_name(pCtx) - ); - jx9_result_int(pCtx, 0); - return JX9_OK; - } - iNew = 0; - if( nArg > 0 ){ - iNew = jx9_value_to_int(apArg[0]); - } - /* Perform the requested operation */ - iOld = pVfs->xUmask(iNew); - /* Old mask */ - jx9_result_int(pCtx, iOld); - return JX9_OK; -} -/* - * string sys_get_temp_dir() - * Returns directory path used for temporary files. - * Parameters - * None - * Return - * Returns the path of the temporary directory. - */ -static int jx9Vfs_sys_get_temp_dir(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_vfs *pVfs; - /* Set the empty string as the default return value */ - jx9_result_string(pCtx, "", 0); - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xTempDir == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* IO routine not implemented, return "" */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS", - jx9_function_name(pCtx) - ); - return JX9_OK; - } - /* Perform the requested operation */ - pVfs->xTempDir(pCtx); - return JX9_OK; -} -/* - * string get_current_user() - * Returns the name of the current working user. - * Parameters - * None - * Return - * Returns the name of the current working user. - */ -static int jx9Vfs_get_current_user(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_vfs *pVfs; - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xUsername == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* IO routine not implemented */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS", - jx9_function_name(pCtx) - ); - /* Set a dummy username */ - jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); - return JX9_OK; - } - /* Perform the requested operation */ - pVfs->xUsername(pCtx); - return JX9_OK; -} -/* - * int64 getmypid() - * Gets process ID. - * Parameters - * None - * Return - * Returns the process ID. - */ -static int jx9Vfs_getmypid(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_int64 nProcessId; - jx9_vfs *pVfs; - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xProcessId == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* IO routine not implemented, return -1 */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS", - jx9_function_name(pCtx) - ); - jx9_result_int(pCtx, -1); - return JX9_OK; - } - /* Perform the requested operation */ - nProcessId = (jx9_int64)pVfs->xProcessId(); - /* Set the result */ - jx9_result_int64(pCtx, nProcessId); - return JX9_OK; -} -/* - * int getmyuid() - * Get user ID. - * Parameters - * None - * Return - * Returns the user ID. - */ -static int jx9Vfs_getmyuid(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_vfs *pVfs; - int nUid; - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xUid == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* IO routine not implemented, return -1 */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS", - jx9_function_name(pCtx) - ); - jx9_result_int(pCtx, -1); - return JX9_OK; - } - /* Perform the requested operation */ - nUid = pVfs->xUid(); - /* Set the result */ - jx9_result_int(pCtx, nUid); - return JX9_OK; -} -/* - * int getmygid() - * Get group ID. - * Parameters - * None - * Return - * Returns the group ID. - */ -static int jx9Vfs_getmygid(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_vfs *pVfs; - int nGid; - /* Point to the underlying vfs */ - pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); - if( pVfs == 0 || pVfs->xGid == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* IO routine not implemented, return -1 */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying VFS", - jx9_function_name(pCtx) - ); - jx9_result_int(pCtx, -1); - return JX9_OK; - } - /* Perform the requested operation */ - nGid = pVfs->xGid(); - /* Set the result */ - jx9_result_int(pCtx, nGid); - return JX9_OK; -} -#ifdef __WINNT__ -#include -#elif defined(__UNIXES__) -#include -#endif -/* - * string uname([ string $mode = "a" ]) - * Returns information about the host operating system. - * Parameters - * $mode - * mode is a single character that defines what information is returned: - * 'a': This is the default. Contains all modes in the sequence "s n r v m". - * 's': Operating system name. eg. FreeBSD. - * 'n': Host name. eg. localhost.example.com. - * 'r': Release name. eg. 5.1.2-RELEASE. - * 'v': Version information. Varies a lot between operating systems. - * 'm': Machine type. eg. i386. - * Return - * OS description as a string. - */ -static int jx9Vfs_uname(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ -#if defined(__WINNT__) - const char *zName = "Microsoft Windows"; - OSVERSIONINFOW sVer; -#elif defined(__UNIXES__) - struct utsname sName; -#endif - const char *zMode = "a"; - if( nArg > 0 && jx9_value_is_string(apArg[0]) ){ - /* Extract the desired mode */ - zMode = jx9_value_to_string(apArg[0], 0); - } -#if defined(__WINNT__) - sVer.dwOSVersionInfoSize = sizeof(sVer); - if( TRUE != GetVersionExW(&sVer)){ - jx9_result_string(pCtx, zName, -1); - return JX9_OK; - } - if( sVer.dwPlatformId == VER_PLATFORM_WIN32_NT ){ - if( sVer.dwMajorVersion <= 4 ){ - zName = "Microsoft Windows NT"; - }else if( sVer.dwMajorVersion == 5 ){ - switch(sVer.dwMinorVersion){ - case 0: zName = "Microsoft Windows 2000"; break; - case 1: zName = "Microsoft Windows XP"; break; - case 2: zName = "Microsoft Windows Server 2003"; break; - } - }else if( sVer.dwMajorVersion == 6){ - switch(sVer.dwMinorVersion){ - case 0: zName = "Microsoft Windows Vista"; break; - case 1: zName = "Microsoft Windows 7"; break; - case 2: zName = "Microsoft Windows 8"; break; - default: break; - } - } - } - switch(zMode[0]){ - case 's': - /* Operating system name */ - jx9_result_string(pCtx, zName, -1/* Compute length automatically*/); - break; - case 'n': - /* Host name */ - jx9_result_string(pCtx, "localhost", (int)sizeof("localhost")-1); - break; - case 'r': - case 'v': - /* Version information. */ - jx9_result_string_format(pCtx, "%u.%u build %u", - sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber - ); - break; - case 'm': - /* Machine name */ - jx9_result_string(pCtx, "x86", (int)sizeof("x86")-1); - break; - default: - jx9_result_string_format(pCtx, "%s localhost %u.%u build %u x86", - zName, - sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber - ); - break; - } -#elif defined(__UNIXES__) - if( uname(&sName) != 0 ){ - jx9_result_string(pCtx, "Unix", (int)sizeof("Unix")-1); - return JX9_OK; - } - switch(zMode[0]){ - case 's': - /* Operating system name */ - jx9_result_string(pCtx, sName.sysname, -1/* Compute length automatically*/); - break; - case 'n': - /* Host name */ - jx9_result_string(pCtx, sName.nodename, -1/* Compute length automatically*/); - break; - case 'r': - /* Release information */ - jx9_result_string(pCtx, sName.release, -1/* Compute length automatically*/); - break; - case 'v': - /* Version information. */ - jx9_result_string(pCtx, sName.version, -1/* Compute length automatically*/); - break; - case 'm': - /* Machine name */ - jx9_result_string(pCtx, sName.machine, -1/* Compute length automatically*/); - break; - default: - jx9_result_string_format(pCtx, - "%s %s %s %s %s", - sName.sysname, - sName.release, - sName.version, - sName.nodename, - sName.machine - ); - break; - } -#else - jx9_result_string(pCtx, "Host Operating System/localhost", (int)sizeof("Host Operating System/localhost")-1); -#endif - return JX9_OK; -} -/* - * Section: - * IO stream implementation. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -typedef struct io_private io_private; -struct io_private -{ - const jx9_io_stream *pStream; /* Underlying IO device */ - void *pHandle; /* IO handle */ - /* Unbuffered IO */ - SyBlob sBuffer; /* Working buffer */ - sxu32 nOfft; /* Current read offset */ - sxu32 iMagic; /* Sanity check to avoid misuse */ -}; -#define IO_PRIVATE_MAGIC 0xFEAC14 -/* Make sure we are dealing with a valid io_private instance */ -#define IO_PRIVATE_INVALID(IO) ( IO == 0 || IO->iMagic != IO_PRIVATE_MAGIC ) -/* Forward declaration */ -static void ResetIOPrivate(io_private *pDev); -/* - * bool ftruncate(resource $handle, int64 $size) - * Truncates a file to a given length. - * Parameters - * $handle - * The file pointer. - * Note: - * The handle must be open for writing. - * $size - * The size to truncate to. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Builtin_ftruncate(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - int rc; - if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xTrunc == 0){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - rc = pStream->xTrunc(pDev->pHandle, jx9_value_to_int64(apArg[1])); - if( rc == JX9_OK ){ - /* Discard buffered data */ - ResetIOPrivate(pDev); - } - /* IO result */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * int fseek(resource $handle, int $offset[, int $whence = SEEK_SET ]) - * Seeks on a file pointer. - * Parameters - * $handle - * A file system pointer resource that is typically created using fopen(). - * $offset - * The offset. - * To move to a position before the end-of-file, you need to pass a negative - * value in offset and set whence to SEEK_END. - * whence - * whence values are: - * SEEK_SET - Set position equal to offset bytes. - * SEEK_CUR - Set position to current location plus offset. - * SEEK_END - Set position to end-of-file plus offset. - * Return - * 0 on success, -1 on failure - */ -static int jx9Builtin_fseek(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - jx9_int64 iOfft; - int whence; - int rc; - if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_int(pCtx, -1); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_int(pCtx, -1); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xSeek == 0){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_int(pCtx, -1); - return JX9_OK; - } - /* Extract the offset */ - iOfft = jx9_value_to_int64(apArg[1]); - whence = 0;/* SEEK_SET */ - if( nArg > 2 && jx9_value_is_int(apArg[2]) ){ - whence = jx9_value_to_int(apArg[2]); - } - /* Perform the requested operation */ - rc = pStream->xSeek(pDev->pHandle, iOfft, whence); - if( rc == JX9_OK ){ - /* Ignore buffered data */ - ResetIOPrivate(pDev); - } - /* IO result */ - jx9_result_int(pCtx, rc == JX9_OK ? 0 : - 1); - return JX9_OK; -} -/* - * int64 ftell(resource $handle) - * Returns the current position of the file read/write pointer. - * Parameters - * $handle - * The file pointer. - * Return - * Returns the position of the file pointer referenced by handle - * as an integer; i.e., its offset into the file stream. - * FALSE is returned on failure. - */ -static int jx9Builtin_ftell(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - jx9_int64 iOfft; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xTell == 0){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - iOfft = pStream->xTell(pDev->pHandle); - /* IO result */ - jx9_result_int64(pCtx, iOfft); - return JX9_OK; -} -/* - * bool rewind(resource $handle) - * Rewind the position of a file pointer. - * Parameters - * $handle - * The file pointer. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Builtin_rewind(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - int rc; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xSeek == 0){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - rc = pStream->xSeek(pDev->pHandle, 0, 0/*SEEK_SET*/); - if( rc == JX9_OK ){ - /* Ignore buffered data */ - ResetIOPrivate(pDev); - } - /* IO result */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool fflush(resource $handle) - * Flushes the output to a file. - * Parameters - * $handle - * The file pointer. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Builtin_fflush(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - int rc; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xSync == 0){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - rc = pStream->xSync(pDev->pHandle); - /* IO result */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * bool feof(resource $handle) - * Tests for end-of-file on a file pointer. - * Parameters - * $handle - * The file pointer. - * Return - * Returns TRUE if the file pointer is at EOF.FALSE otherwise - */ -static int jx9Builtin_feof(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - int rc; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 1); - return JX9_OK; - } - rc = SXERR_EOF; - /* Perform the requested operation */ - if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){ - /* Data is available */ - rc = JX9_OK; - }else{ - char zBuf[4096]; - jx9_int64 n; - /* Perform a buffered read */ - n = pStream->xRead(pDev->pHandle, zBuf, sizeof(zBuf)); - if( n > 0 ){ - /* Copy buffered data */ - SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n); - rc = JX9_OK; - } - } - /* EOF or not */ - jx9_result_bool(pCtx, rc == SXERR_EOF); - return JX9_OK; -} -/* - * Read n bytes from the underlying IO stream device. - * Return total numbers of bytes readen on success. A number < 1 on failure - * [i.e: IO error ] or EOF. - */ -static jx9_int64 StreamRead(io_private *pDev, void *pBuf, jx9_int64 nLen) -{ - const jx9_io_stream *pStream = pDev->pStream; - char *zBuf = (char *)pBuf; - jx9_int64 n, nRead; - n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft; - if( n > 0 ){ - if( n > nLen ){ - n = nLen; - } - /* Copy the buffered data */ - SyMemcpy(SyBlobDataAt(&pDev->sBuffer, pDev->nOfft), pBuf, (sxu32)n); - /* Update the read offset */ - pDev->nOfft += (sxu32)n; - if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){ - /* Reset the working buffer so that we avoid excessive memory allocation */ - SyBlobReset(&pDev->sBuffer); - pDev->nOfft = 0; - } - nLen -= n; - if( nLen < 1 ){ - /* All done */ - return n; - } - /* Advance the cursor */ - zBuf += n; - } - /* Read without buffering */ - nRead = pStream->xRead(pDev->pHandle, zBuf, nLen); - if( nRead > 0 ){ - n += nRead; - }else if( n < 1 ){ - /* EOF or IO error */ - return nRead; - } - return n; -} -/* - * Extract a single line from the buffered input. - */ -static sxi32 GetLine(io_private *pDev, jx9_int64 *pLen, const char **pzLine) -{ - const char *zIn, *zEnd, *zPtr; - zIn = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft); - zEnd = &zIn[SyBlobLength(&pDev->sBuffer)-pDev->nOfft]; - zPtr = zIn; - while( zIn < zEnd ){ - if( zIn[0] == '\n' ){ - /* Line found */ - zIn++; /* Include the line ending as requested by the JX9 specification */ - *pLen = (jx9_int64)(zIn-zPtr); - *pzLine = zPtr; - return SXRET_OK; - } - zIn++; - } - /* No line were found */ - return SXERR_NOTFOUND; -} -/* - * Read a single line from the underlying IO stream device. - */ -static jx9_int64 StreamReadLine(io_private *pDev, const char **pzData, jx9_int64 nMaxLen) -{ - const jx9_io_stream *pStream = pDev->pStream; - char zBuf[8192]; - jx9_int64 n; - sxi32 rc; - n = 0; - if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){ - /* Reset the working buffer so that we avoid excessive memory allocation */ - SyBlobReset(&pDev->sBuffer); - pDev->nOfft = 0; - } - if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){ - /* Check if there is a line */ - rc = GetLine(pDev, &n, pzData); - if( rc == SXRET_OK ){ - /* Got line, update the cursor */ - pDev->nOfft += (sxu32)n; - return n; - } - } - /* Perform the read operation until a new line is extracted or length - * limit is reached. - */ - for(;;){ - n = pStream->xRead(pDev->pHandle, zBuf, (nMaxLen > 0 && nMaxLen < sizeof(zBuf)) ? nMaxLen : sizeof(zBuf)); - if( n < 1 ){ - /* EOF or IO error */ - break; - } - /* Append the data just read */ - SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n); - /* Try to extract a line */ - rc = GetLine(pDev, &n, pzData); - if( rc == SXRET_OK ){ - /* Got one, return immediately */ - pDev->nOfft += (sxu32)n; - return n; - } - if( nMaxLen > 0 && (SyBlobLength(&pDev->sBuffer) - pDev->nOfft >= nMaxLen) ){ - /* Read limit reached, return the available data */ - *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft); - n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft; - /* Reset the working buffer */ - SyBlobReset(&pDev->sBuffer); - pDev->nOfft = 0; - return n; - } - } - if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){ - /* Read limit reached, return the available data */ - *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft); - n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft; - /* Reset the working buffer */ - SyBlobReset(&pDev->sBuffer); - pDev->nOfft = 0; - } - return n; -} -/* - * Open an IO stream handle. - * Notes on stream: - * According to the JX9 reference manual. - * In its simplest definition, a stream is a resource object which exhibits streamable behavior. - * That is, it can be read from or written to in a linear fashion, and may be able to fseek() - * to an arbitrary locations within the stream. - * A wrapper is additional code which tells the stream how to handle specific protocols/encodings. - * For example, the http wrapper knows how to translate a URL into an HTTP/1.0 request for a file - * on a remote server. - * A stream is referenced as: scheme://target - * scheme(string) - The name of the wrapper to be used. Examples include: file, http... - * If no wrapper is specified, the function default is used (typically file://). - * target - Depends on the wrapper used. For filesystem related streams this is typically a path - * and filename of the desired file. For network related streams this is typically a hostname, often - * with a path appended. - * - * Note that JX9 IO streams looks like JX9 streams but their implementation differ greately. - * Please refer to the official documentation for a full discussion. - * This function return a handle on success. Otherwise null. - */ -JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile, - int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew) -{ - void *pHandle = 0; /* cc warning */ - SyString sFile; - int rc; - if( pStream == 0 ){ - /* No such stream device */ - return 0; - } - SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile)); - if( use_include ){ - if( sFile.zString[0] == '/' || -#ifdef __WINNT__ - (sFile.nByte > 2 && sFile.zString[1] == ':' && (sFile.zString[2] == '\\' || sFile.zString[2] == '/') ) || -#endif - (sFile.nByte > 1 && sFile.zString[0] == '.' && sFile.zString[1] == '/') || - (sFile.nByte > 2 && sFile.zString[0] == '.' && sFile.zString[1] == '.' && sFile.zString[2] == '/') ){ - /* Open the file directly */ - rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle); - }else{ - SyString *pPath; - SyBlob sWorker; -#ifdef __WINNT__ - static const int c = '\\'; -#else - static const int c = '/'; -#endif - /* Init the path builder working buffer */ - SyBlobInit(&sWorker, &pVm->sAllocator); - /* Build a path from the set of include path */ - SySetResetCursor(&pVm->aPaths); - rc = SXERR_IO; - while( SXRET_OK == SySetGetNextEntry(&pVm->aPaths, (void **)&pPath) ){ - /* Build full path */ - SyBlobFormat(&sWorker, "%z%c%z", pPath, c, &sFile); - /* Append null terminator */ - if( SXRET_OK != SyBlobNullAppend(&sWorker) ){ - continue; - } - /* Try to open the file */ - rc = pStream->xOpen((const char *)SyBlobData(&sWorker), iFlags, pResource, &pHandle); - if( rc == JX9_OK ){ - if( bPushInclude ){ - /* Mark as included */ - jx9VmPushFilePath(pVm, (const char *)SyBlobData(&sWorker), SyBlobLength(&sWorker), FALSE, pNew); - } - break; - } - /* Reset the working buffer */ - SyBlobReset(&sWorker); - /* Check the next path */ - } - SyBlobRelease(&sWorker); - } - if( rc == JX9_OK ){ - if( bPushInclude ){ - /* Mark as included */ - jx9VmPushFilePath(pVm, sFile.zString, sFile.nByte, FALSE, pNew); - } - } - }else{ - /* Open the URI direcly */ - rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle); - } - if( rc != JX9_OK ){ - /* IO error */ - return 0; - } - /* Return the file handle */ - return pHandle; -} -/* - * Read the whole contents of an open IO stream handle [i.e local file/URL..] - * Store the read data in the given BLOB (last argument). - * The read operation is stopped when he hit the EOF or an IO error occurs. - */ -JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut) -{ - jx9_int64 nRead; - char zBuf[8192]; /* 8K */ - int rc; - /* Perform the requested operation */ - for(;;){ - nRead = pStream->xRead(pHandle, zBuf, sizeof(zBuf)); - if( nRead < 1 ){ - /* EOF or IO error */ - break; - } - /* Append contents */ - rc = SyBlobAppend(pOut, zBuf, (sxu32)nRead); - if( rc != SXRET_OK ){ - break; - } - } - return SyBlobLength(pOut) > 0 ? SXRET_OK : -1; -} -/* - * Close an open IO stream handle [i.e local file/URI..]. - */ -JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle) -{ - if( pStream->xClose ){ - pStream->xClose(pHandle); - } -} -/* - * string fgetc(resource $handle) - * Gets a character from the given file pointer. - * Parameters - * $handle - * The file pointer. - * Return - * Returns a string containing a single character read from the file - * pointed to by handle. Returns FALSE on EOF. - * WARNING - * This operation is extremely slow.Avoid using it. - */ -static int jx9Builtin_fgetc(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - int c, n; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - n = (int)StreamRead(pDev, (void *)&c, sizeof(char)); - /* IO result */ - if( n < 1 ){ - /* EOF or error, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* Return the string holding the character */ - jx9_result_string(pCtx, (const char *)&c, sizeof(char)); - } - return JX9_OK; -} -/* - * string fgets(resource $handle[, int64 $length ]) - * Gets line from file pointer. - * Parameters - * $handle - * The file pointer. - * $length - * Reading ends when length - 1 bytes have been read, on a newline - * (which is included in the return value), or on EOF (whichever comes first). - * If no length is specified, it will keep reading from the stream until it reaches - * the end of the line. - * Return - * Returns a string of up to length - 1 bytes read from the file pointed to by handle. - * If there is no more data to read in the file pointer, then FALSE is returned. - * If an error occurs, FALSE is returned. - */ -static int jx9Builtin_fgets(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - const char *zLine; - io_private *pDev; - jx9_int64 n, nLen; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - nLen = -1; - if( nArg > 1 ){ - /* Maximum data to read */ - nLen = jx9_value_to_int64(apArg[1]); - } - /* Perform the requested operation */ - n = StreamReadLine(pDev, &zLine, nLen); - if( n < 1 ){ - /* EOF or IO error, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* Return the freshly extracted line */ - jx9_result_string(pCtx, zLine, (int)n); - } - return JX9_OK; -} -/* - * string fread(resource $handle, int64 $length) - * Binary-safe file read. - * Parameters - * $handle - * The file pointer. - * $length - * Up to length number of bytes read. - * Return - * The data readen on success or FALSE on failure. - */ -static int jx9Builtin_fread(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - jx9_int64 nRead; - void *pBuf; - int nLen; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - nLen = 4096; - if( nArg > 1 ){ - nLen = jx9_value_to_int(apArg[1]); - if( nLen < 1 ){ - /* Invalid length, set a default length */ - nLen = 4096; - } - } - /* Allocate enough buffer */ - pBuf = jx9_context_alloc_chunk(pCtx, (unsigned int)nLen, FALSE, FALSE); - if( pBuf == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - nRead = StreamRead(pDev, pBuf, (jx9_int64)nLen); - if( nRead < 1 ){ - /* Nothing read, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* Make a copy of the data just read */ - jx9_result_string(pCtx, (const char *)pBuf, (int)nRead); - } - /* Release the buffer */ - jx9_context_free_chunk(pCtx, pBuf); - return JX9_OK; -} -/* - * array fgetcsv(resource $handle [, int $length = 0 - * [, string $delimiter = ', '[, string $enclosure = '"'[, string $escape='\\']]]]) - * Gets line from file pointer and parse for CSV fields. - * Parameters - * $handle - * The file pointer. - * $length - * Reading ends when length - 1 bytes have been read, on a newline - * (which is included in the return value), or on EOF (whichever comes first). - * If no length is specified, it will keep reading from the stream until it reaches - * the end of the line. - * $delimiter - * Set the field delimiter (one character only). - * $enclosure - * Set the field enclosure character (one character only). - * $escape - * Set the escape character (one character only). Defaults as a backslash (\) - * Return - * Returns a string of up to length - 1 bytes read from the file pointed to by handle. - * If there is no more data to read in the file pointer, then FALSE is returned. - * If an error occurs, FALSE is returned. - */ -static int jx9Builtin_fgetcsv(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - const char *zLine; - io_private *pDev; - jx9_int64 n, nLen; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - nLen = -1; - if( nArg > 1 ){ - /* Maximum data to read */ - nLen = jx9_value_to_int64(apArg[1]); - } - /* Perform the requested operation */ - n = StreamReadLine(pDev, &zLine, nLen); - if( n < 1 ){ - /* EOF or IO error, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - jx9_value *pArray; - int delim = ','; /* Delimiter */ - int encl = '"' ; /* Enclosure */ - int escape = '\\'; /* Escape character */ - if( nArg > 2 ){ - const char *zPtr; - int i; - if( jx9_value_is_string(apArg[2]) ){ - /* Extract the delimiter */ - zPtr = jx9_value_to_string(apArg[2], &i); - if( i > 0 ){ - delim = zPtr[0]; - } - } - if( nArg > 3 ){ - if( jx9_value_is_string(apArg[3]) ){ - /* Extract the enclosure */ - zPtr = jx9_value_to_string(apArg[3], &i); - if( i > 0 ){ - encl = zPtr[0]; - } - } - if( nArg > 4 ){ - if( jx9_value_is_string(apArg[4]) ){ - /* Extract the escape character */ - zPtr = jx9_value_to_string(apArg[4], &i); - if( i > 0 ){ - escape = zPtr[0]; - } - } - } - } - } - /* Create our array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_null(pCtx); - return JX9_OK; - } - /* Parse the raw input */ - jx9ProcessCsv(zLine, (int)n, delim, encl, escape, jx9CsvConsumer, pArray); - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - } - return JX9_OK; -} -/* - * string fgetss(resource $handle [, int $length [, string $allowable_tags ]]) - * Gets line from file pointer and strip HTML tags. - * Parameters - * $handle - * The file pointer. - * $length - * Reading ends when length - 1 bytes have been read, on a newline - * (which is included in the return value), or on EOF (whichever comes first). - * If no length is specified, it will keep reading from the stream until it reaches - * the end of the line. - * $allowable_tags - * You can use the optional second parameter to specify tags which should not be stripped. - * Return - * Returns a string of up to length - 1 bytes read from the file pointed to by - * handle, with all HTML and JX9 code stripped. If an error occurs, returns FALSE. - */ -static int jx9Builtin_fgetss(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - const char *zLine; - io_private *pDev; - jx9_int64 n, nLen; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - nLen = -1; - if( nArg > 1 ){ - /* Maximum data to read */ - nLen = jx9_value_to_int64(apArg[1]); - } - /* Perform the requested operation */ - n = StreamReadLine(pDev, &zLine, nLen); - if( n < 1 ){ - /* EOF or IO error, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - const char *zTaglist = 0; - int nTaglen = 0; - if( nArg > 2 && jx9_value_is_string(apArg[2]) ){ - /* Allowed tag */ - zTaglist = jx9_value_to_string(apArg[2], &nTaglen); - } - /* Process data just read */ - jx9StripTagsFromString(pCtx, zLine, (int)n, zTaglist, nTaglen); - } - return JX9_OK; -} -/* - * string readdir(resource $dir_handle) - * Read entry from directory handle. - * Parameter - * $dir_handle - * The directory handle resource previously opened with opendir(). - * Return - * Returns the filename on success or FALSE on failure. - */ -static int jx9Builtin_readdir(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - int rc; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xReadDir == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - jx9_result_bool(pCtx, 0); - /* Perform the requested operation */ - rc = pStream->xReadDir(pDev->pHandle, pCtx); - if( rc != JX9_OK ){ - /* Return FALSE */ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * void rewinddir(resource $dir_handle) - * Rewind directory handle. - * Parameter - * $dir_handle - * The directory handle resource previously opened with opendir(). - * Return - * FALSE on failure. - */ -static int jx9Builtin_rewinddir(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xRewindDir == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - pStream->xRewindDir(pDev->pHandle); - return JX9_OK; - } -/* Forward declaration */ -static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut); -static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev); -/* - * void closedir(resource $dir_handle) - * Close directory handle. - * Parameter - * $dir_handle - * The directory handle resource previously opened with opendir(). - * Return - * FALSE on failure. - */ -static int jx9Builtin_closedir(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xCloseDir == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - pStream->xCloseDir(pDev->pHandle); - /* Release the private stucture */ - ReleaseIOPrivate(pCtx, pDev); - jx9MemObjRelease(apArg[0]); - return JX9_OK; - } -/* - * resource opendir(string $path[, resource $context]) - * Open directory handle. - * Parameters - * $path - * The directory path that is to be opened. - * $context - * A context stream resource. - * Return - * A directory handle resource on success, or FALSE on failure. - */ -static int jx9Builtin_opendir(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - const char *zPath; - io_private *pDev; - int iLen, rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a directory path"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the target path */ - zPath = jx9_value_to_string(apArg[0], &iLen); - /* Try to extract a stream */ - pStream = jx9VmGetStreamDevice(pCtx->pVm, &zPath, iLen); - if( pStream == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "No stream device is associated with the given path(%s)", zPath); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( pStream->xOpenDir == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device", - jx9_function_name(pCtx), pStream->zName - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Allocate a new IO private instance */ - pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE); - if( pDev == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Initialize the structure */ - InitIOPrivate(pCtx->pVm, pStream, pDev); - /* Open the target directory */ - rc = pStream->xOpenDir(zPath, nArg > 1 ? apArg[1] : 0, &pDev->pHandle); - if( rc != JX9_OK ){ - /* IO error, return FALSE */ - ReleaseIOPrivate(pCtx, pDev); - jx9_result_bool(pCtx, 0); - }else{ - /* Return the handle as a resource */ - jx9_result_resource(pCtx, pDev); - } - return JX9_OK; -} -/* - * int readfile(string $filename[, bool $use_include_path = false [, resource $context ]]) - * Reads a file and writes it to the output buffer. - * Parameters - * $filename - * The filename being read. - * $use_include_path - * You can use the optional second parameter and set it to - * TRUE, if you want to search for the file in the include_path, too. - * $context - * A context stream resource. - * Return - * The number of bytes read from the file on success or FALSE on failure. - */ -static int jx9Builtin_readfile(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int use_include = FALSE; - const jx9_io_stream *pStream; - jx9_int64 n, nRead; - const char *zFile; - char zBuf[8192]; - void *pHandle; - int rc, nLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the file path */ - zFile = jx9_value_to_string(apArg[0], &nLen); - /* Point to the target IO stream device */ - pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); - if( pStream == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( nArg > 1 ){ - use_include = jx9_value_to_bool(apArg[1]); - } - /* Try to open the file in read-only mode */ - pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, - use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0); - if( pHandle == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - nRead = 0; - for(;;){ - n = pStream->xRead(pHandle, zBuf, sizeof(zBuf)); - if( n < 1 ){ - /* EOF or IO error, break immediately */ - break; - } - /* Output data */ - rc = jx9_context_output(pCtx, zBuf, (int)n); - if( rc == JX9_ABORT ){ - break; - } - /* Increment counter */ - nRead += n; - } - /* Close the stream */ - jx9StreamCloseHandle(pStream, pHandle); - /* Total number of bytes readen */ - jx9_result_int64(pCtx, nRead); - return JX9_OK; -} -/* - * string file_get_contents(string $filename[, bool $use_include_path = false - * [, resource $context [, int $offset = -1 [, int $maxlen ]]]]) - * Reads entire file into a string. - * Parameters - * $filename - * The filename being read. - * $use_include_path - * You can use the optional second parameter and set it to - * TRUE, if you want to search for the file in the include_path, too. - * $context - * A context stream resource. - * $offset - * The offset where the reading starts on the original stream. - * $maxlen - * Maximum length of data read. The default is to read until end of file - * is reached. Note that this parameter is applied to the stream processed by the filters. - * Return - * The function returns the read data or FALSE on failure. - */ -static int jx9Builtin_file_get_contents(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - jx9_int64 n, nRead, nMaxlen; - int use_include = FALSE; - const char *zFile; - char zBuf[8192]; - void *pHandle; - int nLen; - - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the file path */ - zFile = jx9_value_to_string(apArg[0], &nLen); - /* Point to the target IO stream device */ - pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); - if( pStream == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - nMaxlen = -1; - if( nArg > 1 ){ - use_include = jx9_value_to_bool(apArg[1]); - } - /* Try to open the file in read-only mode */ - pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0); - if( pHandle == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( nArg > 3 ){ - /* Extract the offset */ - n = jx9_value_to_int64(apArg[3]); - if( n > 0 ){ - if( pStream->xSeek ){ - /* Seek to the desired offset */ - pStream->xSeek(pHandle, n, 0/*SEEK_SET*/); - } - } - if( nArg > 4 ){ - /* Maximum data to read */ - nMaxlen = jx9_value_to_int64(apArg[4]); - } - } - /* Perform the requested operation */ - nRead = 0; - for(;;){ - n = pStream->xRead(pHandle, zBuf, - (nMaxlen > 0 && (nMaxlen < sizeof(zBuf))) ? nMaxlen : sizeof(zBuf)); - if( n < 1 ){ - /* EOF or IO error, break immediately */ - break; - } - /* Append data */ - jx9_result_string(pCtx, zBuf, (int)n); - /* Increment read counter */ - nRead += n; - if( nMaxlen > 0 && nRead >= nMaxlen ){ - /* Read limit reached */ - break; - } - } - /* Close the stream */ - jx9StreamCloseHandle(pStream, pHandle); - /* Check if we have read something */ - if( jx9_context_result_buf_length(pCtx) < 1 ){ - /* Nothing read, return FALSE */ - jx9_result_bool(pCtx, 0); - } - return JX9_OK; -} -/* - * int file_put_contents(string $filename, mixed $data[, int $flags = 0[, resource $context]]) - * Write a string to a file. - * Parameters - * $filename - * Path to the file where to write the data. - * $data - * The data to write(Must be a string). - * $flags - * The value of flags can be any combination of the following - * flags, joined with the binary OR (|) operator. - * FILE_USE_INCLUDE_PATH Search for filename in the include directory. See include_path for more information. - * FILE_APPEND If file filename already exists, append the data to the file instead of overwriting it. - * LOCK_EX Acquire an exclusive lock on the file while proceeding to the writing. - * context - * A context stream resource. - * Return - * The function returns the number of bytes that were written to the file, or FALSE on failure. - */ -static int jx9Builtin_file_put_contents(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int use_include = FALSE; - const jx9_io_stream *pStream; - const char *zFile; - const char *zData; - int iOpenFlags; - void *pHandle; - int iFlags; - int nLen; - - if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the file path */ - zFile = jx9_value_to_string(apArg[0], &nLen); - /* Point to the target IO stream device */ - pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); - if( pStream == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Data to write */ - zData = jx9_value_to_string(apArg[1], &nLen); - if( nLen < 1 ){ - /* Nothing to write, return immediately */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Try to open the file in read-write mode */ - iOpenFlags = JX9_IO_OPEN_CREATE|JX9_IO_OPEN_RDWR|JX9_IO_OPEN_TRUNC; - /* Extract the flags */ - iFlags = 0; - if( nArg > 2 ){ - iFlags = jx9_value_to_int(apArg[2]); - if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/){ - use_include = TRUE; - } - if( iFlags & 0x08 /* FILE_APPEND */){ - /* If the file already exists, append the data to the file - * instead of overwriting it. - */ - iOpenFlags &= ~JX9_IO_OPEN_TRUNC; - /* Append mode */ - iOpenFlags |= JX9_IO_OPEN_APPEND; - } - } - pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, iOpenFlags, use_include, - nArg > 3 ? apArg[3] : 0, FALSE, FALSE); - if( pHandle == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( pStream->xWrite ){ - jx9_int64 n; - if( (iFlags & 0x01/* LOCK_EX */) && pStream->xLock ){ - /* Try to acquire an exclusive lock */ - pStream->xLock(pHandle, 1/* LOCK_EX */); - } - /* Perform the write operation */ - n = pStream->xWrite(pHandle, (const void *)zData, nLen); - if( n < 1 ){ - /* IO error, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* Total number of bytes written */ - jx9_result_int64(pCtx, n); - } - }else{ - /* Read-only stream */ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, - "Read-only stream(%s): Cannot perform write operation", - pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - } - /* Close the handle */ - jx9StreamCloseHandle(pStream, pHandle); - return JX9_OK; -} -/* - * array file(string $filename[, int $flags = 0[, resource $context]]) - * Reads entire file into an array. - * Parameters - * $filename - * The filename being read. - * $flags - * The optional parameter flags can be one, or more, of the following constants: - * FILE_USE_INCLUDE_PATH - * Search for the file in the include_path. - * FILE_IGNORE_NEW_LINES - * Do not add newline at the end of each array element - * FILE_SKIP_EMPTY_LINES - * Skip empty lines - * $context - * A context stream resource. - * Return - * The function returns the read data or FALSE on failure. - */ -static int jx9Builtin_file(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zFile, *zPtr, *zEnd, *zBuf; - jx9_value *pArray, *pLine; - const jx9_io_stream *pStream; - int use_include = 0; - io_private *pDev; - jx9_int64 n; - int iFlags; - int nLen; - - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the file path */ - zFile = jx9_value_to_string(apArg[0], &nLen); - /* Point to the target IO stream device */ - pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); - if( pStream == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Allocate a new IO private instance */ - pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE); - if( pDev == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Initialize the structure */ - InitIOPrivate(pCtx->pVm, pStream, pDev); - iFlags = 0; - if( nArg > 1 ){ - iFlags = jx9_value_to_int(apArg[1]); - } - if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/ ){ - use_include = TRUE; - } - /* Create the array and the working value */ - pArray = jx9_context_new_array(pCtx); - pLine = jx9_context_new_scalar(pCtx); - if( pArray == 0 || pLine == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Try to open the file in read-only mode */ - pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0); - if( pDev->pHandle == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); - jx9_result_bool(pCtx, 0); - /* Don't worry about freeing memory, everything will be released automatically - * as soon we return from this function. - */ - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - /* Try to extract a line */ - n = StreamReadLine(pDev, &zBuf, -1); - if( n < 1 ){ - /* EOF or IO error */ - break; - } - /* Reset the cursor */ - jx9_value_reset_string_cursor(pLine); - /* Remove line ending if requested by the caller */ - zPtr = zBuf; - zEnd = &zBuf[n]; - if( iFlags & 0x02 /* FILE_IGNORE_NEW_LINES */ ){ - /* Ignore trailig lines */ - while( zPtr < zEnd && (zEnd[-1] == '\n' -#ifdef __WINNT__ - || zEnd[-1] == '\r' -#endif - )){ - n--; - zEnd--; - } - } - if( iFlags & 0x04 /* FILE_SKIP_EMPTY_LINES */ ){ - /* Ignore empty lines */ - while( zPtr < zEnd && (unsigned char)zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) ){ - zPtr++; - } - if( zPtr >= zEnd ){ - /* Empty line */ - continue; - } - } - jx9_value_string(pLine, zBuf, (int)(zEnd-zBuf)); - /* Insert line */ - jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pLine); - } - /* Close the stream */ - jx9StreamCloseHandle(pStream, pDev->pHandle); - /* Release the io_private instance */ - ReleaseIOPrivate(pCtx, pDev); - /* Return the created array */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * bool copy(string $source, string $dest[, resource $context ] ) - * Makes a copy of the file source to dest. - * Parameters - * $source - * Path to the source file. - * $dest - * The destination path. If dest is a URL, the copy operation - * may fail if the wrapper does not support overwriting of existing files. - * $context - * A context stream resource. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Builtin_copy(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pSin, *pSout; - const char *zFile; - char zBuf[8192]; - void *pIn, *pOut; - jx9_int64 n; - int nLen; - if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1])){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a source and a destination path"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the source name */ - zFile = jx9_value_to_string(apArg[0], &nLen); - /* Point to the target IO stream device */ - pSin = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); - if( pSin == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Try to open the source file in a read-only mode */ - pIn = jx9StreamOpenHandle(pCtx->pVm, pSin, zFile, JX9_IO_OPEN_RDONLY, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0); - if( pIn == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening source: '%s'", zFile); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the destination name */ - zFile = jx9_value_to_string(apArg[1], &nLen); - /* Point to the target IO stream device */ - pSout = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); - if( pSout == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); - jx9_result_bool(pCtx, 0); - jx9StreamCloseHandle(pSin, pIn); - return JX9_OK; - } - if( pSout->xWrite == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pSin->zName - ); - jx9_result_bool(pCtx, 0); - jx9StreamCloseHandle(pSin, pIn); - return JX9_OK; - } - /* Try to open the destination file in a read-write mode */ - pOut = jx9StreamOpenHandle(pCtx->pVm, pSout, zFile, - JX9_IO_OPEN_CREATE|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_RDWR, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0); - if( pOut == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening destination: '%s'", zFile); - jx9_result_bool(pCtx, 0); - jx9StreamCloseHandle(pSin, pIn); - return JX9_OK; - } - /* Perform the requested operation */ - for(;;){ - /* Read from source */ - n = pSin->xRead(pIn, zBuf, sizeof(zBuf)); - if( n < 1 ){ - /* EOF or IO error, break immediately */ - break; - } - /* Write to dest */ - n = pSout->xWrite(pOut, zBuf, n); - if( n < 1 ){ - /* IO error, break immediately */ - break; - } - } - /* Close the streams */ - jx9StreamCloseHandle(pSin, pIn); - jx9StreamCloseHandle(pSout, pOut); - /* Return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_OK; -} -/* - * array fstat(resource $handle) - * Gets information about a file using an open file pointer. - * Parameters - * $handle - * The file pointer. - * Return - * Returns an array with the statistics of the file or FALSE on failure. - */ -static int jx9Builtin_fstat(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pArray, *pValue; - const jx9_io_stream *pStream; - io_private *pDev; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /* Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xStat == 0){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Create the array and the working value */ - pArray = jx9_context_new_array(pCtx); - pValue = jx9_context_new_scalar(pCtx); - if( pArray == 0 || pValue == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - pStream->xStat(pDev->pHandle, pArray, pValue); - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - /* Don't worry about freeing memory here, everything will be - * released automatically as soon we return from this function. - */ - return JX9_OK; -} -/* - * int fwrite(resource $handle, string $string[, int $length]) - * Writes the contents of string to the file stream pointed to by handle. - * Parameters - * $handle - * The file pointer. - * $string - * The string that is to be written. - * $length - * If the length argument is given, writing will stop after length bytes have been written - * or the end of string is reached, whichever comes first. - * Return - * Returns the number of bytes written, or FALSE on error. - */ -static int jx9Builtin_fwrite(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - const char *zString; - io_private *pDev; - int nLen, n; - if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /* Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xWrite == 0){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the data to write */ - zString = jx9_value_to_string(apArg[1], &nLen); - if( nArg > 2 ){ - /* Maximum data length to write */ - n = jx9_value_to_int(apArg[2]); - if( n >= 0 && n < nLen ){ - nLen = n; - } - } - if( nLen < 1 ){ - /* Nothing to write */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - n = (int)pStream->xWrite(pDev->pHandle, (const void *)zString, nLen); - if( n < 0 ){ - /* IO error, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* #Bytes written */ - jx9_result_int(pCtx, n); - } - return JX9_OK; -} -/* - * bool flock(resource $handle, int $operation) - * Portable advisory file locking. - * Parameters - * $handle - * The file pointer. - * $operation - * operation is one of the following: - * LOCK_SH to acquire a shared lock (reader). - * LOCK_EX to acquire an exclusive lock (writer). - * LOCK_UN to release a lock (shared or exclusive). - * Return - * Returns TRUE on success or FALSE on failure. - */ -static int jx9Builtin_flock(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - int nLock; - int rc; - if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xLock == 0){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Requested lock operation */ - nLock = jx9_value_to_int(apArg[1]); - /* Lock operation */ - rc = pStream->xLock(pDev->pHandle, nLock); - /* IO result */ - jx9_result_bool(pCtx, rc == JX9_OK); - return JX9_OK; -} -/* - * int fpassthru(resource $handle) - * Output all remaining data on a file pointer. - * Parameters - * $handle - * The file pointer. - * Return - * Total number of characters read from handle and passed through - * to the output on success or FALSE on failure. - */ -static int jx9Builtin_fpassthru(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - jx9_int64 n, nRead; - char zBuf[8192]; - int rc; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the requested operation */ - nRead = 0; - for(;;){ - n = StreamRead(pDev, zBuf, sizeof(zBuf)); - if( n < 1 ){ - /* Error or EOF */ - break; - } - /* Increment the read counter */ - nRead += n; - /* Output data */ - rc = jx9_context_output(pCtx, zBuf, (int)nRead /* FIXME: 64-bit issues */); - if( rc == JX9_ABORT ){ - /* Consumer callback request an operation abort */ - break; - } - } - /* Total number of bytes readen */ - jx9_result_int64(pCtx, nRead); - return JX9_OK; -} -/* CSV reader/writer private data */ -struct csv_data -{ - int delimiter; /* Delimiter. Default ', ' */ - int enclosure; /* Enclosure. Default '"'*/ - io_private *pDev; /* Open stream handle */ - int iCount; /* Counter */ -}; -/* - * The following callback is used by the fputcsv() function inorder to iterate - * throw array entries and output CSV data based on the current key and it's - * associated data. - */ -static int csv_write_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData) -{ - struct csv_data *pData = (struct csv_data *)pUserData; - const char *zData; - int nLen, c2; - sxu32 n; - /* Point to the raw data */ - zData = jx9_value_to_string(pValue, &nLen); - if( nLen < 1 ){ - /* Nothing to write */ - return JX9_OK; - } - if( pData->iCount > 0 ){ - /* Write the delimiter */ - pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->delimiter, sizeof(char)); - } - n = 1; - c2 = 0; - if( SyByteFind(zData, (sxu32)nLen, pData->delimiter, 0) == SXRET_OK || - SyByteFind(zData, (sxu32)nLen, pData->enclosure, &n) == SXRET_OK ){ - c2 = 1; - if( n == 0 ){ - c2 = 2; - } - /* Write the enclosure */ - pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char)); - if( c2 > 1 ){ - pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char)); - } - } - /* Write the data */ - if( pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)zData, (jx9_int64)nLen) < 1 ){ - SXUNUSED(pKey); /* cc warning */ - return JX9_ABORT; - } - if( c2 > 0 ){ - /* Write the enclosure */ - pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char)); - if( c2 > 1 ){ - pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char)); - } - } - pData->iCount++; - return JX9_OK; -} -/* - * int fputcsv(resource $handle, array $fields[, string $delimiter = ', '[, string $enclosure = '"' ]]) - * Format line as CSV and write to file pointer. - * Parameters - * $handle - * Open file handle. - * $fields - * An array of values. - * $delimiter - * The optional delimiter parameter sets the field delimiter (one character only). - * $enclosure - * The optional enclosure parameter sets the field enclosure (one character only). - */ -static int jx9Builtin_fputcsv(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - struct csv_data sCsv; - io_private *pDev; - char *zEol; - int eolen; - if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Missing/Invalid arguments"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 || pStream->xWrite == 0){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Set default csv separator */ - sCsv.delimiter = ','; - sCsv.enclosure = '"'; - sCsv.pDev = pDev; - sCsv.iCount = 0; - if( nArg > 2 ){ - /* User delimiter */ - const char *z; - int n; - z = jx9_value_to_string(apArg[2], &n); - if( n > 0 ){ - sCsv.delimiter = z[0]; - } - if( nArg > 3 ){ - z = jx9_value_to_string(apArg[3], &n); - if( n > 0 ){ - sCsv.enclosure = z[0]; - } - } - } - /* Iterate throw array entries and write csv data */ - jx9_array_walk(apArg[1], csv_write_callback, &sCsv); - /* Write a line ending */ -#ifdef __WINNT__ - zEol = "\r\n"; - eolen = (int)sizeof("\r\n")-1; -#else - /* Assume UNIX LF */ - zEol = "\n"; - eolen = (int)sizeof(char); -#endif - pDev->pStream->xWrite(pDev->pHandle, (const void *)zEol, eolen); - return JX9_OK; -} -/* - * fprintf, vfprintf private data. - * An instance of the following structure is passed to the formatted - * input consumer callback defined below. - */ -typedef struct fprintf_data fprintf_data; -struct fprintf_data -{ - io_private *pIO; /* IO stream */ - jx9_int64 nCount; /* Total number of bytes written */ -}; -/* - * Callback [i.e: Formatted input consumer] for the fprintf function. - */ -static int fprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData) -{ - fprintf_data *pFdata = (fprintf_data *)pUserData; - jx9_int64 n; - /* Write the formatted data */ - n = pFdata->pIO->pStream->xWrite(pFdata->pIO->pHandle, (const void *)zInput, nLen); - if( n < 1 ){ - SXUNUSED(pCtx); /* cc warning */ - /* IO error, abort immediately */ - return SXERR_ABORT; - } - /* Increment counter */ - pFdata->nCount += n; - return JX9_OK; -} -/* - * int fprintf(resource $handle, string $format[, mixed $args [, mixed $... ]]) - * Write a formatted string to a stream. - * Parameters - * $handle - * The file pointer. - * $format - * String format (see sprintf()). - * Return - * The length of the written string. - */ -static int jx9Builtin_fprintf(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - fprintf_data sFdata; - const char *zFormat; - io_private *pDev; - int nLen; - if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ - /* Missing/Invalid arguments, return zero */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments"); - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device", - jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream" - ); - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Extract the string format */ - zFormat = jx9_value_to_string(apArg[1], &nLen); - if( nLen < 1 ){ - /* Empty string, return zero */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Prepare our private data */ - sFdata.nCount = 0; - sFdata.pIO = pDev; - /* Format the string */ - jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, nArg - 1, &apArg[1], (void *)&sFdata, FALSE); - /* Return total number of bytes written */ - jx9_result_int64(pCtx, sFdata.nCount); - return JX9_OK; -} -/* - * int vfprintf(resource $handle, string $format, array $args) - * Write a formatted string to a stream. - * Parameters - * $handle - * The file pointer. - * $format - * String format (see sprintf()). - * $args - * User arguments. - * Return - * The length of the written string. - */ -static int jx9Builtin_vfprintf(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - fprintf_data sFdata; - const char *zFormat; - jx9_hashmap *pMap; - io_private *pDev; - SySet sArg; - int n, nLen; - if( nArg < 3 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) || !jx9_value_is_json_array(apArg[2]) ){ - /* Missing/Invalid arguments, return zero */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments"); - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device", - jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream" - ); - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Extract the string format */ - zFormat = jx9_value_to_string(apArg[1], &nLen); - if( nLen < 1 ){ - /* Empty string, return zero */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Point to hashmap */ - pMap = (jx9_hashmap *)apArg[2]->x.pOther; - /* Extract arguments from the hashmap */ - n = jx9HashmapValuesToSet(pMap, &sArg); - /* Prepare our private data */ - sFdata.nCount = 0; - sFdata.pIO = pDev; - /* Format the string */ - jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&sFdata, TRUE); - /* Return total number of bytes written*/ - jx9_result_int64(pCtx, sFdata.nCount); - SySetRelease(&sArg); - return JX9_OK; -} -/* - * Convert open modes (string passed to the fopen() function) [i.e: 'r', 'w+', 'a', ...] into JX9 flags. - * According to the JX9 reference manual: - * The mode parameter specifies the type of access you require to the stream. It may be any of the following - * 'r' Open for reading only; place the file pointer at the beginning of the file. - * 'r+' Open for reading and writing; place the file pointer at the beginning of the file. - * 'w' Open for writing only; place the file pointer at the beginning of the file and truncate the file - * to zero length. If the file does not exist, attempt to create it. - * 'w+' Open for reading and writing; place the file pointer at the beginning of the file and truncate - * the file to zero length. If the file does not exist, attempt to create it. - * 'a' Open for writing only; place the file pointer at the end of the file. If the file does not - * exist, attempt to create it. - * 'a+' Open for reading and writing; place the file pointer at the end of the file. If the file does - * not exist, attempt to create it. - * 'x' Create and open for writing only; place the file pointer at the beginning of the file. If the file - * already exists, - * the fopen() call will fail by returning FALSE and generating an error of level E_WARNING. If the file - * does not exist attempt to create it. This is equivalent to specifying O_EXCL|O_CREAT flags for - * the underlying open(2) system call. - * 'x+' Create and open for reading and writing; otherwise it has the same behavior as 'x'. - * 'c' Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated - * (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer - * is positioned on the beginning of the file. - * This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file - * as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can - * be used after the lock is requested). - * 'c+' Open the file for reading and writing; otherwise it has the same behavior as 'c'. - */ -static int StrModeToFlags(jx9_context *pCtx, const char *zMode, int nLen) -{ - const char *zEnd = &zMode[nLen]; - int iFlag = 0; - int c; - if( nLen < 1 ){ - /* Open in a read-only mode */ - return JX9_IO_OPEN_RDONLY; - } - c = zMode[0]; - if( c == 'r' || c == 'R' ){ - /* Read-only access */ - iFlag = JX9_IO_OPEN_RDONLY; - zMode++; /* Advance */ - if( zMode < zEnd ){ - c = zMode[0]; - if( c == '+' || c == 'w' || c == 'W' ){ - /* Read+Write access */ - iFlag = JX9_IO_OPEN_RDWR; - } - } - }else if( c == 'w' || c == 'W' ){ - /* Overwrite mode. - * If the file does not exists, try to create it - */ - iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_CREATE; - zMode++; /* Advance */ - if( zMode < zEnd ){ - c = zMode[0]; - if( c == '+' || c == 'r' || c == 'R' ){ - /* Read+Write access */ - iFlag &= ~JX9_IO_OPEN_WRONLY; - iFlag |= JX9_IO_OPEN_RDWR; - } - } - }else if( c == 'a' || c == 'A' ){ - /* Append mode (place the file pointer at the end of the file). - * Create the file if it does not exists. - */ - iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_APPEND|JX9_IO_OPEN_CREATE; - zMode++; /* Advance */ - if( zMode < zEnd ){ - c = zMode[0]; - if( c == '+' ){ - /* Read-Write access */ - iFlag &= ~JX9_IO_OPEN_WRONLY; - iFlag |= JX9_IO_OPEN_RDWR; - } - } - }else if( c == 'x' || c == 'X' ){ - /* Exclusive access. - * If the file already exists, return immediately with a failure code. - * Otherwise create a new file. - */ - iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_EXCL; - zMode++; /* Advance */ - if( zMode < zEnd ){ - c = zMode[0]; - if( c == '+' || c == 'r' || c == 'R' ){ - /* Read-Write access */ - iFlag &= ~JX9_IO_OPEN_WRONLY; - iFlag |= JX9_IO_OPEN_RDWR; - } - } - }else if( c == 'c' || c == 'C' ){ - /* Overwrite mode.Create the file if it does not exists.*/ - iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_CREATE; - zMode++; /* Advance */ - if( zMode < zEnd ){ - c = zMode[0]; - if( c == '+' ){ - /* Read-Write access */ - iFlag &= ~JX9_IO_OPEN_WRONLY; - iFlag |= JX9_IO_OPEN_RDWR; - } - } - }else{ - /* Invalid mode. Assume a read only open */ - jx9_context_throw_error(pCtx, JX9_CTX_NOTICE, "Invalid open mode, JX9 is assuming a Read-Only open"); - iFlag = JX9_IO_OPEN_RDONLY; - } - while( zMode < zEnd ){ - c = zMode[0]; - if( c == 'b' || c == 'B' ){ - iFlag &= ~JX9_IO_OPEN_TEXT; - iFlag |= JX9_IO_OPEN_BINARY; - }else if( c == 't' || c == 'T' ){ - iFlag &= ~JX9_IO_OPEN_BINARY; - iFlag |= JX9_IO_OPEN_TEXT; - } - zMode++; - } - return iFlag; -} -/* - * Initialize the IO private structure. - */ -static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut) -{ - pOut->pStream = pStream; - SyBlobInit(&pOut->sBuffer, &pVm->sAllocator); - pOut->nOfft = 0; - /* Set the magic number */ - pOut->iMagic = IO_PRIVATE_MAGIC; -} -/* - * Release the IO private structure. - */ -static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev) -{ - SyBlobRelease(&pDev->sBuffer); - pDev->iMagic = 0x2126; /* Invalid magic number so we can detetct misuse */ - /* Release the whole structure */ - jx9_context_free_chunk(pCtx, pDev); -} -/* - * Reset the IO private structure. - */ -static void ResetIOPrivate(io_private *pDev) -{ - SyBlobReset(&pDev->sBuffer); - pDev->nOfft = 0; -} -/* Forward declaration */ -static int is_jx9_stream(const jx9_io_stream *pStream); -/* - * resource fopen(string $filename, string $mode [, bool $use_include_path = false[, resource $context ]]) - * Open a file, a URL or any other IO stream. - * Parameters - * $filename - * If filename is of the form "scheme://...", it is assumed to be a URL and JX9 will search - * for a protocol handler (also known as a wrapper) for that scheme. If no scheme is given - * then a regular file is assumed. - * $mode - * The mode parameter specifies the type of access you require to the stream - * See the block comment associated with the StrModeToFlags() for the supported - * modes. - * $use_include_path - * You can use the optional second parameter and set it to - * TRUE, if you want to search for the file in the include_path, too. - * $context - * A context stream resource. - * Return - * File handle on success or FALSE on failure. - */ -static int jx9Builtin_fopen(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - const char *zUri, *zMode; - jx9_value *pResource; - io_private *pDev; - int iLen, imLen; - int iOpenFlags; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path or URL"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the URI and the desired access mode */ - zUri = jx9_value_to_string(apArg[0], &iLen); - if( nArg > 1 ){ - zMode = jx9_value_to_string(apArg[1], &imLen); - }else{ - /* Set a default read-only mode */ - zMode = "r"; - imLen = (int)sizeof(char); - } - /* Try to extract a stream */ - pStream = jx9VmGetStreamDevice(pCtx->pVm, &zUri, iLen); - if( pStream == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "No stream device is associated with the given URI(%s)", zUri); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Allocate a new IO private instance */ - pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE); - if( pDev == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pResource = 0; - if( nArg > 3 ){ - pResource = apArg[3]; - }else if( is_jx9_stream(pStream) ){ - /* TICKET 1433-80: The jx9:// stream need a jx9_value to access the underlying - * virtual machine. - */ - pResource = apArg[0]; - } - /* Initialize the structure */ - InitIOPrivate(pCtx->pVm, pStream, pDev); - /* Convert open mode to JX9 flags */ - iOpenFlags = StrModeToFlags(pCtx, zMode, imLen); - /* Try to get a handle */ - pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zUri, iOpenFlags, - nArg > 2 ? jx9_value_to_bool(apArg[2]) : FALSE, pResource, FALSE, 0); - if( pDev->pHandle == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zUri); - jx9_result_bool(pCtx, 0); - jx9_context_free_chunk(pCtx, pDev); - return JX9_OK; - } - /* All done, return the io_private instance as a resource */ - jx9_result_resource(pCtx, pDev); - return JX9_OK; -} -/* - * bool fclose(resource $handle) - * Closes an open file pointer - * Parameters - * $handle - * The file pointer. - * Return - * TRUE on success or FALSE on failure. - */ -static int jx9Builtin_fclose(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - io_private *pDev; - jx9_vm *pVm; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract our private data */ - pDev = (io_private *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid io_private instance */ - if( IO_PRIVATE_INVALID(pDev) ){ - /*Expecting an IO handle */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target IO stream device */ - pStream = pDev->pStream; - if( pStream == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, - "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", - jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" - ); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the VM that own this context */ - pVm = pCtx->pVm; - /* TICKET 1433-62: Keep the STDIN/STDOUT/STDERR handles open */ - if( pDev != pVm->pStdin && pDev != pVm->pStdout && pDev != pVm->pStderr ){ - /* Perform the requested operation */ - jx9StreamCloseHandle(pStream, pDev->pHandle); - /* Release the IO private structure */ - ReleaseIOPrivate(pCtx, pDev); - /* Invalidate the resource handle */ - jx9_value_release(apArg[0]); - } - /* Return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_OK; -} -#if !defined(JX9_DISABLE_HASH_FUNC) -/* - * MD5/SHA1 digest consumer. - */ -static int vfsHashConsumer(const void *pData, unsigned int nLen, void *pUserData) -{ - /* Append hex chunk verbatim */ - jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen); - return SXRET_OK; -} -/* - * string md5_file(string $uri[, bool $raw_output = false ]) - * Calculates the md5 hash of a given file. - * Parameters - * $uri - * Target URI (file(/path/to/something) or URL(http://www.symisc.net/)) - * $raw_output - * When TRUE, returns the digest in raw binary format with a length of 16. - * Return - * Return the MD5 digest on success or FALSE on failure. - */ -static int jx9Builtin_md5_file(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - unsigned char zDigest[16]; - int raw_output = FALSE; - const char *zFile; - MD5Context sCtx; - char zBuf[8192]; - void *pHandle; - jx9_int64 n; - int nLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the file path */ - zFile = jx9_value_to_string(apArg[0], &nLen); - /* Point to the target IO stream device */ - pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); - if( pStream == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( nArg > 1 ){ - raw_output = jx9_value_to_bool(apArg[1]); - } - /* Try to open the file in read-only mode */ - pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0); - if( pHandle == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Init the MD5 context */ - MD5Init(&sCtx); - /* Perform the requested operation */ - for(;;){ - n = pStream->xRead(pHandle, zBuf, sizeof(zBuf)); - if( n < 1 ){ - /* EOF or IO error, break immediately */ - break; - } - MD5Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n); - } - /* Close the stream */ - jx9StreamCloseHandle(pStream, pHandle); - /* Extract the digest */ - MD5Final(zDigest, &sCtx); - if( raw_output ){ - /* Output raw digest */ - jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest)); - }else{ - /* Perform a binary to hex conversion */ - SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx); - } - return JX9_OK; -} -/* - * string sha1_file(string $uri[, bool $raw_output = false ]) - * Calculates the SHA1 hash of a given file. - * Parameters - * $uri - * Target URI (file(/path/to/something) or URL(http://www.symisc.net/)) - * $raw_output - * When TRUE, returns the digest in raw binary format with a length of 20. - * Return - * Return the SHA1 digest on success or FALSE on failure. - */ -static int jx9Builtin_sha1_file(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - unsigned char zDigest[20]; - int raw_output = FALSE; - const char *zFile; - SHA1Context sCtx; - char zBuf[8192]; - void *pHandle; - jx9_int64 n; - int nLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the file path */ - zFile = jx9_value_to_string(apArg[0], &nLen); - /* Point to the target IO stream device */ - pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); - if( pStream == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( nArg > 1 ){ - raw_output = jx9_value_to_bool(apArg[1]); - } - /* Try to open the file in read-only mode */ - pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0); - if( pHandle == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Init the SHA1 context */ - SHA1Init(&sCtx); - /* Perform the requested operation */ - for(;;){ - n = pStream->xRead(pHandle, zBuf, sizeof(zBuf)); - if( n < 1 ){ - /* EOF or IO error, break immediately */ - break; - } - SHA1Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n); - } - /* Close the stream */ - jx9StreamCloseHandle(pStream, pHandle); - /* Extract the digest */ - SHA1Final(&sCtx, zDigest); - if( raw_output ){ - /* Output raw digest */ - jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest)); - }else{ - /* Perform a binary to hex conversion */ - SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx); - } - return JX9_OK; -} -#endif /* JX9_DISABLE_HASH_FUNC */ -/* - * array parse_ini_file(string $filename[, bool $process_sections = false [, int $scanner_mode = INI_SCANNER_NORMAL ]] ) - * Parse a configuration file. - * Parameters - * $filename - * The filename of the ini file being parsed. - * $process_sections - * By setting the process_sections parameter to TRUE, you get a multidimensional array - * with the section names and settings included. - * The default for process_sections is FALSE. - * $scanner_mode - * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. - * If INI_SCANNER_RAW is supplied, then option values will not be parsed. - * Return - * The settings are returned as an associative array on success. - * Otherwise is returned. - */ -static int jx9Builtin_parse_ini_file(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - const char *zFile; - SyBlob sContents; - void *pHandle; - int nLen; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the file path */ - zFile = jx9_value_to_string(apArg[0], &nLen); - /* Point to the target IO stream device */ - pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); - if( pStream == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Try to open the file in read-only mode */ - pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0); - if( pHandle == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - SyBlobInit(&sContents, &pCtx->pVm->sAllocator); - /* Read the whole file */ - jx9StreamReadWholeFile(pHandle, pStream, &sContents); - if( SyBlobLength(&sContents) < 1 ){ - /* Empty buffer, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* Process the raw INI buffer */ - jx9ParseIniString(pCtx, (const char *)SyBlobData(&sContents), SyBlobLength(&sContents), - nArg > 1 ? jx9_value_to_bool(apArg[1]) : 0); - } - /* Close the stream */ - jx9StreamCloseHandle(pStream, pHandle); - /* Release the working buffer */ - SyBlobRelease(&sContents); - return JX9_OK; -} -/* - * Section: - * ZIP archive processing. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -typedef struct zip_raw_data zip_raw_data; -struct zip_raw_data -{ - int iType; /* Where the raw data is stored */ - union raw_data{ - struct mmap_data{ - void *pMap; /* Memory mapped data */ - jx9_int64 nSize; /* Map size */ - const jx9_vfs *pVfs; /* Underlying vfs */ - }mmap; - SyBlob sBlob; /* Memory buffer */ - }raw; -}; -#define ZIP_RAW_DATA_MMAPED 1 /* Memory mapped ZIP raw data */ -#define ZIP_RAW_DATA_MEMBUF 2 /* ZIP raw data stored in a dynamically - * allocated memory chunk. - */ - /* - * mixed zip_open(string $filename) - * Opens a new zip archive for reading. - * Parameters - * $filename - * The file name of the ZIP archive to open. - * Return - * A resource handle for later use with zip_read() and zip_close() or FALSE on failure. - */ -static int jx9Builtin_zip_open(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const jx9_io_stream *pStream; - SyArchive *pArchive; - zip_raw_data *pRaw; - const char *zFile; - SyBlob *pContents; - void *pHandle; - int nLen; - sxi32 rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the file path */ - zFile = jx9_value_to_string(apArg[0], &nLen); - /* Point to the target IO stream device */ - pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); - if( pStream == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Create an in-memory archive */ - pArchive = (SyArchive *)jx9_context_alloc_chunk(pCtx, sizeof(SyArchive)+sizeof(zip_raw_data), TRUE, FALSE); - if( pArchive == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pRaw = (zip_raw_data *)&pArchive[1]; - /* Initialize the archive */ - SyArchiveInit(pArchive, &pCtx->pVm->sAllocator, 0, 0); - /* Extract the default stream */ - if( pStream == pCtx->pVm->pDefStream /* file:// stream*/){ - const jx9_vfs *pVfs; - /* Try to get a memory view of the whole file since ZIP files - * tends to be very big this days, this is a huge performance win. - */ - pVfs = jx9ExportBuiltinVfs(); - if( pVfs && pVfs->xMmap ){ - rc = pVfs->xMmap(zFile, &pRaw->raw.mmap.pMap, &pRaw->raw.mmap.nSize); - if( rc == JX9_OK ){ - /* Nice, Extract the whole archive */ - rc = SyZipExtractFromBuf(pArchive, (const char *)pRaw->raw.mmap.pMap, (sxu32)pRaw->raw.mmap.nSize); - if( rc != SXRET_OK ){ - if( pVfs->xUnmap ){ - pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize); - } - /* Release the allocated chunk */ - jx9_context_free_chunk(pCtx, pArchive); - /* Something goes wrong with this ZIP archive, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Archive successfully opened */ - pRaw->iType = ZIP_RAW_DATA_MMAPED; - pRaw->raw.mmap.pVfs = pVfs; - goto success; - } - } - /* FALL THROUGH */ - } - /* Try to open the file in read-only mode */ - pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0); - if( pHandle == 0 ){ - jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pContents = &pRaw->raw.sBlob; - SyBlobInit(pContents, &pCtx->pVm->sAllocator); - /* Read the whole file */ - jx9StreamReadWholeFile(pHandle, pStream, pContents); - /* Assume an invalid ZIP file */ - rc = SXERR_INVALID; - if( SyBlobLength(pContents) > 0 ){ - /* Extract archive entries */ - rc = SyZipExtractFromBuf(pArchive, (const char *)SyBlobData(pContents), SyBlobLength(pContents)); - } - pRaw->iType = ZIP_RAW_DATA_MEMBUF; - /* Close the stream */ - jx9StreamCloseHandle(pStream, pHandle); - if( rc != SXRET_OK ){ - /* Release the working buffer */ - SyBlobRelease(pContents); - /* Release the allocated chunk */ - jx9_context_free_chunk(pCtx, pArchive); - /* Something goes wrong with this ZIP archive, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } -success: - /* Reset the loop cursor */ - SyArchiveResetLoopCursor(pArchive); - /* Return the in-memory archive as a resource handle */ - jx9_result_resource(pCtx, pArchive); - return JX9_OK; -} -/* - * void zip_close(resource $zip) - * Close an in-memory ZIP archive. - * Parameters - * $zip - * A ZIP file previously opened with zip_open(). - * Return - * null. - */ -static int jx9Builtin_zip_close(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyArchive *pArchive; - zip_raw_data *pRaw; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); - return JX9_OK; - } - /* Point to the in-memory archive */ - pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid ZIP archive */ - if( SXARCH_INVALID(pArchive) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); - return JX9_OK; - } - /* Release the archive */ - SyArchiveRelease(pArchive); - pRaw = (zip_raw_data *)&pArchive[1]; - if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){ - SyBlobRelease(&pRaw->raw.sBlob); - }else{ - const jx9_vfs *pVfs = pRaw->raw.mmap.pVfs; - if( pVfs->xUnmap ){ - /* Unmap the memory view */ - pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize); - } - } - /* Release the memory chunk */ - jx9_context_free_chunk(pCtx, pArchive); - return JX9_OK; -} -/* - * mixed zip_read(resource $zip) - * Reads the next entry from an in-memory ZIP archive. - * Parameters - * $zip - * A ZIP file previously opened with zip_open(). - * Return - * A directory entry resource for later use with the zip_entry_... functions - * or FALSE if there are no more entries to read, or an error code if an error occurred. - */ -static int jx9Builtin_zip_read(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyArchiveEntry *pNext = 0; /* cc warning */ - SyArchive *pArchive; - sxi32 rc; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the in-memory archive */ - pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid ZIP archive */ - if( SXARCH_INVALID(pArchive) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the next entry */ - rc = SyArchiveGetNextEntry(pArchive, &pNext); - if( rc != SXRET_OK ){ - /* No more entries in the central directory, return FALSE */ - jx9_result_bool(pCtx, 0); - }else{ - /* Return as a resource handle */ - jx9_result_resource(pCtx, pNext); - /* Point to the ZIP raw data */ - pNext->pUserData = (void *)&pArchive[1]; - } - return JX9_OK; -} -/* - * bool zip_entry_open(resource $zip, resource $zip_entry[, string $mode ]) - * Open a directory entry for reading - * Parameters - * $zip - * A ZIP file previously opened with zip_open(). - * $zip_entry - * A directory entry returned by zip_read(). - * $mode - * Not used - * Return - * A directory entry resource for later use with the zip_entry_... functions - * or FALSE if there are no more entries to read, or an error code if an error occurred. - */ -static int jx9Builtin_zip_entry_open(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyArchiveEntry *pEntry; - SyArchive *pArchive; - if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_resource(apArg[1]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the in-memory archive */ - pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]); - /* Make sure we are dealing with a valid ZIP archive */ - if( SXARCH_INVALID(pArchive) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid ZIP archive entry */ - pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[1]); - if( SXARCH_ENTRY_INVALID(pEntry) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* All done. Actually this function is a no-op, return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_OK; -} -/* - * bool zip_entry_close(resource $zip_entry) - * Close a directory entry. - * Parameters - * $zip_entry - * A directory entry returned by zip_read(). - * Return - * Returns TRUE on success or FALSE on failure. - */ -static int jx9Builtin_zip_entry_close(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyArchiveEntry *pEntry; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid ZIP archive entry */ - pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); - if( SXARCH_ENTRY_INVALID(pEntry) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Reset the read cursor */ - pEntry->nReadCount = 0; - /*All done. Actually this function is a no-op, return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_OK; -} -/* - * string zip_entry_name(resource $zip_entry) - * Retrieve the name of a directory entry. - * Parameters - * $zip_entry - * A directory entry returned by zip_read(). - * Return - * The name of the directory entry. - */ -static int jx9Builtin_zip_entry_name(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyArchiveEntry *pEntry; - SyString *pName; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid ZIP archive entry */ - pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); - if( SXARCH_ENTRY_INVALID(pEntry) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Return entry name */ - pName = &pEntry->sFileName; - jx9_result_string(pCtx, pName->zString, (int)pName->nByte); - return JX9_OK; -} -/* - * int64 zip_entry_filesize(resource $zip_entry) - * Retrieve the actual file size of a directory entry. - * Parameters - * $zip_entry - * A directory entry returned by zip_read(). - * Return - * The size of the directory entry. - */ -static int jx9Builtin_zip_entry_filesize(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyArchiveEntry *pEntry; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid ZIP archive entry */ - pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); - if( SXARCH_ENTRY_INVALID(pEntry) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Return entry size */ - jx9_result_int64(pCtx, (jx9_int64)pEntry->nByte); - return JX9_OK; -} -/* - * int64 zip_entry_compressedsize(resource $zip_entry) - * Retrieve the compressed size of a directory entry. - * Parameters - * $zip_entry - * A directory entry returned by zip_read(). - * Return - * The compressed size. - */ -static int jx9Builtin_zip_entry_compressedsize(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyArchiveEntry *pEntry; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid ZIP archive entry */ - pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); - if( SXARCH_ENTRY_INVALID(pEntry) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Return entry compressed size */ - jx9_result_int64(pCtx, (jx9_int64)pEntry->nByteCompr); - return JX9_OK; -} -/* - * string zip_entry_read(resource $zip_entry[, int $length]) - * Reads from an open directory entry. - * Parameters - * $zip_entry - * A directory entry returned by zip_read(). - * $length - * The number of bytes to return. If not specified, this function - * will attempt to read 1024 bytes. - * Return - * Returns the data read, or FALSE if the end of the file is reached. - */ -static int jx9Builtin_zip_entry_read(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyArchiveEntry *pEntry; - zip_raw_data *pRaw; - const char *zData; - int iLength; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid ZIP archive entry */ - pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); - if( SXARCH_ENTRY_INVALID(pEntry) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - zData = 0; - if( pEntry->nReadCount >= pEntry->nByteCompr ){ - /* No more data to read, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Set a default read length */ - iLength = 1024; - if( nArg > 1 ){ - iLength = jx9_value_to_int(apArg[1]); - if( iLength < 1 ){ - iLength = 1024; - } - } - if( (sxu32)iLength > pEntry->nByteCompr - pEntry->nReadCount ){ - iLength = (int)(pEntry->nByteCompr - pEntry->nReadCount); - } - /* Return the entry contents */ - pRaw = (zip_raw_data *)pEntry->pUserData; - if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){ - zData = (const char *)SyBlobDataAt(&pRaw->raw.sBlob, (pEntry->nOfft+pEntry->nReadCount)); - }else{ - const char *zMap = (const char *)pRaw->raw.mmap.pMap; - /* Memory mmaped chunk */ - zData = &zMap[pEntry->nOfft+pEntry->nReadCount]; - } - /* Increment the read counter */ - pEntry->nReadCount += iLength; - /* Return the raw data */ - jx9_result_string(pCtx, zData, iLength); - return JX9_OK; -} -/* - * bool zip_entry_reset_cursor(resource $zip_entry) - * Reset the read cursor of an open directory entry. - * Parameters - * $zip_entry - * A directory entry returned by zip_read(). - * Return - * TRUE on success, FALSE on failure. - */ -static int jx9Builtin_zip_entry_reset_cursor(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyArchiveEntry *pEntry; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid ZIP archive entry */ - pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); - if( SXARCH_ENTRY_INVALID(pEntry) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Reset the cursor */ - pEntry->nReadCount = 0; - /* Return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_OK; -} -/* - * string zip_entry_compressionmethod(resource $zip_entry) - * Retrieve the compression method of a directory entry. - * Parameters - * $zip_entry - * A directory entry returned by zip_read(). - * Return - * The compression method on success or FALSE on failure. - */ -static int jx9Builtin_zip_entry_compressionmethod(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyArchiveEntry *pEntry; - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid ZIP archive entry */ - pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); - if( SXARCH_ENTRY_INVALID(pEntry) ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); - /* return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - switch(pEntry->nComprMeth){ - case 0: - /* No compression;entry is stored */ - jx9_result_string(pCtx, "stored", (int)sizeof("stored")-1); - break; - case 8: - /* Entry is deflated (Default compression algorithm) */ - jx9_result_string(pCtx, "deflate", (int)sizeof("deflate")-1); - break; - /* Exotic compression algorithms */ - case 1: - jx9_result_string(pCtx, "shrunk", (int)sizeof("shrunk")-1); - break; - case 2: - case 3: - case 4: - case 5: - /* Entry is reduced */ - jx9_result_string(pCtx, "reduced", (int)sizeof("reduced")-1); - break; - case 6: - /* Entry is imploded */ - jx9_result_string(pCtx, "implode", (int)sizeof("implode")-1); - break; - default: - jx9_result_string(pCtx, "unknown", (int)sizeof("unknown")-1); - break; - } - return JX9_OK; -} -#endif /* #ifndef JX9_DISABLE_BUILTIN_FUNC*/ -/* NULL VFS [i.e: a no-op VFS]*/ -static const jx9_vfs null_vfs = { - "null_vfs", - JX9_VFS_VERSION, - 0, /* int (*xChdir)(const char *) */ - 0, /* int (*xChroot)(const char *); */ - 0, /* int (*xGetcwd)(jx9_context *) */ - 0, /* int (*xMkdir)(const char *, int, int) */ - 0, /* int (*xRmdir)(const char *) */ - 0, /* int (*xIsdir)(const char *) */ - 0, /* int (*xRename)(const char *, const char *) */ - 0, /*int (*xRealpath)(const char *, jx9_context *)*/ - 0, /* int (*xSleep)(unsigned int) */ - 0, /* int (*xUnlink)(const char *) */ - 0, /* int (*xFileExists)(const char *) */ - 0, /*int (*xChmod)(const char *, int)*/ - 0, /*int (*xChown)(const char *, const char *)*/ - 0, /*int (*xChgrp)(const char *, const char *)*/ - 0, /* jx9_int64 (*xFreeSpace)(const char *) */ - 0, /* jx9_int64 (*xTotalSpace)(const char *) */ - 0, /* jx9_int64 (*xFileSize)(const char *) */ - 0, /* jx9_int64 (*xFileAtime)(const char *) */ - 0, /* jx9_int64 (*xFileMtime)(const char *) */ - 0, /* jx9_int64 (*xFileCtime)(const char *) */ - 0, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */ - 0, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */ - 0, /* int (*xIsfile)(const char *) */ - 0, /* int (*xIslink)(const char *) */ - 0, /* int (*xReadable)(const char *) */ - 0, /* int (*xWritable)(const char *) */ - 0, /* int (*xExecutable)(const char *) */ - 0, /* int (*xFiletype)(const char *, jx9_context *) */ - 0, /* int (*xGetenv)(const char *, jx9_context *) */ - 0, /* int (*xSetenv)(const char *, const char *) */ - 0, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */ - 0, /* int (*xMmap)(const char *, void **, jx9_int64 *) */ - 0, /* void (*xUnmap)(void *, jx9_int64); */ - 0, /* int (*xLink)(const char *, const char *, int) */ - 0, /* int (*xUmask)(int) */ - 0, /* void (*xTempDir)(jx9_context *) */ - 0, /* unsigned int (*xProcessId)(void) */ - 0, /* int (*xUid)(void) */ - 0, /* int (*xGid)(void) */ - 0, /* void (*xUsername)(jx9_context *) */ - 0 /* int (*xExec)(const char *, jx9_context *) */ -}; -#ifndef JX9_DISABLE_BUILTIN_FUNC -#ifndef JX9_DISABLE_DISK_IO -#ifdef __WINNT__ -/* - * Windows VFS implementation for the JX9 engine. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* What follows here is code that is specific to windows systems. */ -#include -/* -** Convert a UTF-8 string to microsoft unicode (UTF-16?). -** -** Space to hold the returned string is obtained from HeapAlloc(). -** Taken from the sqlite3 source tree -** status: Public Domain -*/ -static WCHAR *jx9utf8ToUnicode(const char *zFilename){ - int nChar; - WCHAR *zWideFilename; - - nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0); - zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, nChar*sizeof(zWideFilename[0])); - if( zWideFilename == 0 ){ - return 0; - } - nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar); - if( nChar==0 ){ - HeapFree(GetProcessHeap(), 0, zWideFilename); - return 0; - } - return zWideFilename; -} -/* -** Convert a UTF-8 filename into whatever form the underlying -** operating system wants filenames in.Space to hold the result -** is obtained from HeapAlloc() and must be freed by the calling -** function. -** Taken from the sqlite3 source tree -** status: Public Domain -*/ -static void *jx9convertUtf8Filename(const char *zFilename){ - void *zConverted; - zConverted = jx9utf8ToUnicode(zFilename); - return zConverted; -} -/* -** Convert microsoft unicode to UTF-8. Space to hold the returned string is -** obtained from HeapAlloc(). -** Taken from the sqlite3 source tree -** status: Public Domain -*/ -static char *jx9unicodeToUtf8(const WCHAR *zWideFilename){ - char *zFilename; - int nByte; - - nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); - zFilename = (char *)HeapAlloc(GetProcessHeap(), 0, nByte); - if( zFilename == 0 ){ - return 0; - } - nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, 0, 0); - if( nByte == 0 ){ - HeapFree(GetProcessHeap(), 0, zFilename); - return 0; - } - return zFilename; -} -/* int (*xchdir)(const char *) */ -static int WinVfs_chdir(const char *zPath) -{ - void * pConverted; - BOOL rc; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - rc = SetCurrentDirectoryW((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - return rc ? JX9_OK : -1; -} -/* int (*xGetcwd)(jx9_context *) */ -static int WinVfs_getcwd(jx9_context *pCtx) -{ - WCHAR zDir[2048]; - char *zConverted; - DWORD rc; - /* Get the current directory */ - rc = GetCurrentDirectoryW(sizeof(zDir), zDir); - if( rc < 1 ){ - return -1; - } - zConverted = jx9unicodeToUtf8(zDir); - if( zConverted == 0 ){ - return -1; - } - jx9_result_string(pCtx, zConverted, -1/*Compute length automatically*/); /* Will make it's own copy */ - HeapFree(GetProcessHeap(), 0, zConverted); - return JX9_OK; -} -/* int (*xMkdir)(const char *, int, int) */ -static int WinVfs_mkdir(const char *zPath, int mode, int recursive) -{ - void * pConverted; - BOOL rc; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - mode= 0; /* MSVC warning */ - recursive = 0; - rc = CreateDirectoryW((LPCWSTR)pConverted, 0); - HeapFree(GetProcessHeap(), 0, pConverted); - return rc ? JX9_OK : -1; -} -/* int (*xRmdir)(const char *) */ -static int WinVfs_rmdir(const char *zPath) -{ - void * pConverted; - BOOL rc; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - rc = RemoveDirectoryW((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - return rc ? JX9_OK : -1; -} -/* int (*xIsdir)(const char *) */ -static int WinVfs_isdir(const char *zPath) -{ - void * pConverted; - DWORD dwAttr; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - dwAttr = GetFileAttributesW((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - if( dwAttr == INVALID_FILE_ATTRIBUTES ){ - return -1; - } - return (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? JX9_OK : -1; -} -/* int (*xRename)(const char *, const char *) */ -static int WinVfs_Rename(const char *zOld, const char *zNew) -{ - void *pOld, *pNew; - BOOL rc = 0; - pOld = jx9convertUtf8Filename(zOld); - if( pOld == 0 ){ - return -1; - } - pNew = jx9convertUtf8Filename(zNew); - if( pNew ){ - rc = MoveFileW((LPCWSTR)pOld, (LPCWSTR)pNew); - } - HeapFree(GetProcessHeap(), 0, pOld); - if( pNew ){ - HeapFree(GetProcessHeap(), 0, pNew); - } - return rc ? JX9_OK : - 1; -} -/* int (*xRealpath)(const char *, jx9_context *) */ -static int WinVfs_Realpath(const char *zPath, jx9_context *pCtx) -{ - WCHAR zTemp[2048]; - void *pPath; - char *zReal; - DWORD n; - pPath = jx9convertUtf8Filename(zPath); - if( pPath == 0 ){ - return -1; - } - n = GetFullPathNameW((LPCWSTR)pPath, 0, 0, 0); - if( n > 0 ){ - if( n >= sizeof(zTemp) ){ - n = sizeof(zTemp) - 1; - } - GetFullPathNameW((LPCWSTR)pPath, n, zTemp, 0); - } - HeapFree(GetProcessHeap(), 0, pPath); - if( !n ){ - return -1; - } - zReal = jx9unicodeToUtf8(zTemp); - if( zReal == 0 ){ - return -1; - } - jx9_result_string(pCtx, zReal, -1); /* Will make it's own copy */ - HeapFree(GetProcessHeap(), 0, zReal); - return JX9_OK; -} -/* int (*xSleep)(unsigned int) */ -static int WinVfs_Sleep(unsigned int uSec) -{ - Sleep(uSec/1000/*uSec per Millisec */); - return JX9_OK; -} -/* int (*xUnlink)(const char *) */ -static int WinVfs_unlink(const char *zPath) -{ - void * pConverted; - BOOL rc; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - rc = DeleteFileW((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - return rc ? JX9_OK : - 1; -} -/* jx9_int64 (*xFreeSpace)(const char *) */ -static jx9_int64 WinVfs_DiskFreeSpace(const char *zPath) -{ -#ifdef _WIN32_WCE - /* GetDiskFreeSpace is not supported under WINCE */ - SXUNUSED(zPath); - return 0; -#else - DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters; - void * pConverted; - WCHAR *p; - BOOL rc; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return 0; - } - p = (WCHAR *)pConverted; - for(;*p;p++){ - if( *p == '\\' || *p == '/'){ - *p = '\0'; - break; - } - } - rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters); - if( !rc ){ - return 0; - } - return (jx9_int64)dwFreeClusters * dwSectPerClust * dwBytesPerSect; -#endif -} -/* jx9_int64 (*xTotalSpace)(const char *) */ -static jx9_int64 WinVfs_DiskTotalSpace(const char *zPath) -{ -#ifdef _WIN32_WCE - /* GetDiskFreeSpace is not supported under WINCE */ - SXUNUSED(zPath); - return 0; -#else - DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters; - void * pConverted; - WCHAR *p; - BOOL rc; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return 0; - } - p = (WCHAR *)pConverted; - for(;*p;p++){ - if( *p == '\\' || *p == '/'){ - *p = '\0'; - break; - } - } - rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters); - if( !rc ){ - return 0; - } - return (jx9_int64)dwTotalClusters * dwSectPerClust * dwBytesPerSect; -#endif -} -/* int (*xFileExists)(const char *) */ -static int WinVfs_FileExists(const char *zPath) -{ - void * pConverted; - DWORD dwAttr; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - dwAttr = GetFileAttributesW((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - if( dwAttr == INVALID_FILE_ATTRIBUTES ){ - return -1; - } - return JX9_OK; -} -/* Open a file in a read-only mode */ -static HANDLE OpenReadOnly(LPCWSTR pPath) -{ - DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; - DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE; - DWORD dwAccess = GENERIC_READ; - DWORD dwCreate = OPEN_EXISTING; - HANDLE pHandle; - pHandle = CreateFileW(pPath, dwAccess, dwShare, 0, dwCreate, dwType, 0); - if( pHandle == INVALID_HANDLE_VALUE){ - return 0; - } - return pHandle; -} -/* jx9_int64 (*xFileSize)(const char *) */ -static jx9_int64 WinVfs_FileSize(const char *zPath) -{ - DWORD dwLow, dwHigh; - void * pConverted; - jx9_int64 nSize; - HANDLE pHandle; - - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - /* Open the file in read-only mode */ - pHandle = OpenReadOnly((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - if( pHandle ){ - dwLow = GetFileSize(pHandle, &dwHigh); - nSize = dwHigh; - nSize <<= 32; - nSize += dwLow; - CloseHandle(pHandle); - }else{ - nSize = -1; - } - return nSize; -} -#define TICKS_PER_SECOND 10000000 -#define EPOCH_DIFFERENCE 11644473600LL -/* Convert Windows timestamp to UNIX timestamp */ -static jx9_int64 convertWindowsTimeToUnixTime(LPFILETIME pTime) -{ - jx9_int64 input, temp; - input = pTime->dwHighDateTime; - input <<= 32; - input += pTime->dwLowDateTime; - temp = input / TICKS_PER_SECOND; /*convert from 100ns intervals to seconds*/ - temp = temp - EPOCH_DIFFERENCE; /*subtract number of seconds between epochs*/ - return temp; -} -/* Convert UNIX timestamp to Windows timestamp */ -static void convertUnixTimeToWindowsTime(jx9_int64 nUnixtime, LPFILETIME pOut) -{ - jx9_int64 result = EPOCH_DIFFERENCE; - result += nUnixtime; - result *= 10000000LL; - pOut->dwHighDateTime = (DWORD)(nUnixtime>>32); - pOut->dwLowDateTime = (DWORD)nUnixtime; -} -/* int (*xTouch)(const char *, jx9_int64, jx9_int64) */ -static int WinVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time) -{ - FILETIME sTouch, sAccess; - void *pConverted; - void *pHandle; - BOOL rc = 0; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - pHandle = OpenReadOnly((LPCWSTR)pConverted); - if( pHandle ){ - if( touch_time < 0 ){ - GetSystemTimeAsFileTime(&sTouch); - }else{ - convertUnixTimeToWindowsTime(touch_time, &sTouch); - } - if( access_time < 0 ){ - /* Use the touch time */ - sAccess = sTouch; /* Structure assignment */ - }else{ - convertUnixTimeToWindowsTime(access_time, &sAccess); - } - rc = SetFileTime(pHandle, &sTouch, &sAccess, 0); - /* Close the handle */ - CloseHandle(pHandle); - } - HeapFree(GetProcessHeap(), 0, pConverted); - return rc ? JX9_OK : -1; -} -/* jx9_int64 (*xFileAtime)(const char *) */ -static jx9_int64 WinVfs_FileAtime(const char *zPath) -{ - BY_HANDLE_FILE_INFORMATION sInfo; - void * pConverted; - jx9_int64 atime; - HANDLE pHandle; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - /* Open the file in read-only mode */ - pHandle = OpenReadOnly((LPCWSTR)pConverted); - if( pHandle ){ - BOOL rc; - rc = GetFileInformationByHandle(pHandle, &sInfo); - if( rc ){ - atime = convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime); - }else{ - atime = -1; - } - CloseHandle(pHandle); - }else{ - atime = -1; - } - HeapFree(GetProcessHeap(), 0, pConverted); - return atime; -} -/* jx9_int64 (*xFileMtime)(const char *) */ -static jx9_int64 WinVfs_FileMtime(const char *zPath) -{ - BY_HANDLE_FILE_INFORMATION sInfo; - void * pConverted; - jx9_int64 mtime; - HANDLE pHandle; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - /* Open the file in read-only mode */ - pHandle = OpenReadOnly((LPCWSTR)pConverted); - if( pHandle ){ - BOOL rc; - rc = GetFileInformationByHandle(pHandle, &sInfo); - if( rc ){ - mtime = convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime); - }else{ - mtime = -1; - } - CloseHandle(pHandle); - }else{ - mtime = -1; - } - HeapFree(GetProcessHeap(), 0, pConverted); - return mtime; -} -/* jx9_int64 (*xFileCtime)(const char *) */ -static jx9_int64 WinVfs_FileCtime(const char *zPath) -{ - BY_HANDLE_FILE_INFORMATION sInfo; - void * pConverted; - jx9_int64 ctime; - HANDLE pHandle; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - /* Open the file in read-only mode */ - pHandle = OpenReadOnly((LPCWSTR)pConverted); - if( pHandle ){ - BOOL rc; - rc = GetFileInformationByHandle(pHandle, &sInfo); - if( rc ){ - ctime = convertWindowsTimeToUnixTime(&sInfo.ftCreationTime); - }else{ - ctime = -1; - } - CloseHandle(pHandle); - }else{ - ctime = -1; - } - HeapFree(GetProcessHeap(), 0, pConverted); - return ctime; -} -/* int (*xStat)(const char *, jx9_value *, jx9_value *) */ -/* int (*xlStat)(const char *, jx9_value *, jx9_value *) */ -static int WinVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker) -{ - BY_HANDLE_FILE_INFORMATION sInfo; - void *pConverted; - HANDLE pHandle; - BOOL rc; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - /* Open the file in read-only mode */ - pHandle = OpenReadOnly((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - if( pHandle == 0 ){ - return -1; - } - rc = GetFileInformationByHandle(pHandle, &sInfo); - CloseHandle(pHandle); - if( !rc ){ - return -1; - } - /* dev */ - jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber); - jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */ - /* ino */ - jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow)); - jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */ - /* mode */ - jx9_value_int(pWorker, 0); - jx9_array_add_strkey_elem(pArray, "mode", pWorker); - /* nlink */ - jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks); - jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */ - /* uid, gid, rdev */ - jx9_value_int(pWorker, 0); - jx9_array_add_strkey_elem(pArray, "uid", pWorker); - jx9_array_add_strkey_elem(pArray, "gid", pWorker); - jx9_array_add_strkey_elem(pArray, "rdev", pWorker); - /* size */ - jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow)); - jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */ - /* atime */ - jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime)); - jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */ - /* mtime */ - jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime)); - jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */ - /* ctime */ - jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime)); - jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */ - /* blksize, blocks */ - jx9_value_int(pWorker, 0); - jx9_array_add_strkey_elem(pArray, "blksize", pWorker); - jx9_array_add_strkey_elem(pArray, "blocks", pWorker); - return JX9_OK; -} -/* int (*xIsfile)(const char *) */ -static int WinVfs_isfile(const char *zPath) -{ - void * pConverted; - DWORD dwAttr; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - dwAttr = GetFileAttributesW((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - if( dwAttr == INVALID_FILE_ATTRIBUTES ){ - return -1; - } - return (dwAttr & (FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ? JX9_OK : -1; -} -/* int (*xIslink)(const char *) */ -static int WinVfs_islink(const char *zPath) -{ - void * pConverted; - DWORD dwAttr; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - dwAttr = GetFileAttributesW((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - if( dwAttr == INVALID_FILE_ATTRIBUTES ){ - return -1; - } - return (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) ? JX9_OK : -1; -} -/* int (*xWritable)(const char *) */ -static int WinVfs_iswritable(const char *zPath) -{ - void * pConverted; - DWORD dwAttr; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - dwAttr = GetFileAttributesW((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - if( dwAttr == INVALID_FILE_ATTRIBUTES ){ - return -1; - } - if( (dwAttr & (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL)) == 0 ){ - /* Not a regular file */ - return -1; - } - if( dwAttr & FILE_ATTRIBUTE_READONLY ){ - /* Read-only file */ - return -1; - } - /* File is writable */ - return JX9_OK; -} -/* int (*xExecutable)(const char *) */ -static int WinVfs_isexecutable(const char *zPath) -{ - void * pConverted; - DWORD dwAttr; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - dwAttr = GetFileAttributesW((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - if( dwAttr == INVALID_FILE_ATTRIBUTES ){ - return -1; - } - if( (dwAttr & FILE_ATTRIBUTE_NORMAL) == 0 ){ - /* Not a regular file */ - return -1; - } - /* File is executable */ - return JX9_OK; -} -/* int (*xFiletype)(const char *, jx9_context *) */ -static int WinVfs_Filetype(const char *zPath, jx9_context *pCtx) -{ - void * pConverted; - DWORD dwAttr; - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - /* Expand 'unknown' */ - jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); - return -1; - } - dwAttr = GetFileAttributesW((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - if( dwAttr == INVALID_FILE_ATTRIBUTES ){ - /* Expand 'unknown' */ - jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); - return -1; - } - if(dwAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE) ){ - jx9_result_string(pCtx, "file", sizeof("file")-1); - }else if(dwAttr & FILE_ATTRIBUTE_DIRECTORY){ - jx9_result_string(pCtx, "dir", sizeof("dir")-1); - }else if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT){ - jx9_result_string(pCtx, "link", sizeof("link")-1); - }else if(dwAttr & (FILE_ATTRIBUTE_DEVICE)){ - jx9_result_string(pCtx, "block", sizeof("block")-1); - }else{ - jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); - } - return JX9_OK; -} -/* int (*xGetenv)(const char *, jx9_context *) */ -static int WinVfs_Getenv(const char *zVar, jx9_context *pCtx) -{ - char zValue[1024]; - DWORD n; - /* - * According to MSDN - * If lpBuffer is not large enough to hold the data, the return - * value is the buffer size, in characters, required to hold the - * string and its terminating null character and the contents - * of lpBuffer are undefined. - */ - n = sizeof(zValue); - SyMemcpy("Undefined", zValue, sizeof("Undefined")-1); - /* Extract the environment value */ - n = GetEnvironmentVariableA(zVar, zValue, sizeof(zValue)); - if( !n ){ - /* No such variable*/ - return -1; - } - jx9_result_string(pCtx, zValue, (int)n); - return JX9_OK; -} -/* int (*xSetenv)(const char *, const char *) */ -static int WinVfs_Setenv(const char *zName, const char *zValue) -{ - BOOL rc; - rc = SetEnvironmentVariableA(zName, zValue); - return rc ? JX9_OK : -1; -} -/* int (*xMmap)(const char *, void **, jx9_int64 *) */ -static int WinVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize) -{ - DWORD dwSizeLow, dwSizeHigh; - HANDLE pHandle, pMapHandle; - void *pConverted, *pView; - - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - pHandle = OpenReadOnly((LPCWSTR)pConverted); - HeapFree(GetProcessHeap(), 0, pConverted); - if( pHandle == 0 ){ - return -1; - } - /* Get the file size */ - dwSizeLow = GetFileSize(pHandle, &dwSizeHigh); - /* Create the mapping */ - pMapHandle = CreateFileMappingW(pHandle, 0, PAGE_READONLY, dwSizeHigh, dwSizeLow, 0); - if( pMapHandle == 0 ){ - CloseHandle(pHandle); - return -1; - } - *pSize = ((jx9_int64)dwSizeHigh << 32) | dwSizeLow; - /* Obtain the view */ - pView = MapViewOfFile(pMapHandle, FILE_MAP_READ, 0, 0, (SIZE_T)(*pSize)); - if( pView ){ - /* Let the upper layer point to the view */ - *ppMap = pView; - } - /* Close the handle - * According to MSDN it's OK the close the HANDLES. - */ - CloseHandle(pMapHandle); - CloseHandle(pHandle); - return pView ? JX9_OK : -1; -} -/* void (*xUnmap)(void *, jx9_int64) */ -static void WinVfs_Unmap(void *pView, jx9_int64 nSize) -{ - nSize = 0; /* Compiler warning */ - UnmapViewOfFile(pView); -} -/* void (*xTempDir)(jx9_context *) */ -static void WinVfs_TempDir(jx9_context *pCtx) -{ - CHAR zTemp[1024]; - DWORD n; - n = GetTempPathA(sizeof(zTemp), zTemp); - if( n < 1 ){ - /* Assume the default windows temp directory */ - jx9_result_string(pCtx, "C:\\Windows\\Temp", -1/*Compute length automatically*/); - }else{ - jx9_result_string(pCtx, zTemp, (int)n); - } -} -/* unsigned int (*xProcessId)(void) */ -static unsigned int WinVfs_ProcessId(void) -{ - DWORD nID = 0; -#ifndef __MINGW32__ - nID = GetProcessId(GetCurrentProcess()); -#endif /* __MINGW32__ */ - return (unsigned int)nID; -} - -/* Export the windows vfs */ -static const jx9_vfs sWinVfs = { - "Windows_vfs", - JX9_VFS_VERSION, - WinVfs_chdir, /* int (*xChdir)(const char *) */ - 0, /* int (*xChroot)(const char *); */ - WinVfs_getcwd, /* int (*xGetcwd)(jx9_context *) */ - WinVfs_mkdir, /* int (*xMkdir)(const char *, int, int) */ - WinVfs_rmdir, /* int (*xRmdir)(const char *) */ - WinVfs_isdir, /* int (*xIsdir)(const char *) */ - WinVfs_Rename, /* int (*xRename)(const char *, const char *) */ - WinVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/ - WinVfs_Sleep, /* int (*xSleep)(unsigned int) */ - WinVfs_unlink, /* int (*xUnlink)(const char *) */ - WinVfs_FileExists, /* int (*xFileExists)(const char *) */ - 0, /*int (*xChmod)(const char *, int)*/ - 0, /*int (*xChown)(const char *, const char *)*/ - 0, /*int (*xChgrp)(const char *, const char *)*/ - WinVfs_DiskFreeSpace, /* jx9_int64 (*xFreeSpace)(const char *) */ - WinVfs_DiskTotalSpace, /* jx9_int64 (*xTotalSpace)(const char *) */ - WinVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */ - WinVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */ - WinVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */ - WinVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */ - WinVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */ - WinVfs_Stat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */ - WinVfs_isfile, /* int (*xIsfile)(const char *) */ - WinVfs_islink, /* int (*xIslink)(const char *) */ - WinVfs_isfile, /* int (*xReadable)(const char *) */ - WinVfs_iswritable, /* int (*xWritable)(const char *) */ - WinVfs_isexecutable, /* int (*xExecutable)(const char *) */ - WinVfs_Filetype, /* int (*xFiletype)(const char *, jx9_context *) */ - WinVfs_Getenv, /* int (*xGetenv)(const char *, jx9_context *) */ - WinVfs_Setenv, /* int (*xSetenv)(const char *, const char *) */ - WinVfs_Touch, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */ - WinVfs_Mmap, /* int (*xMmap)(const char *, void **, jx9_int64 *) */ - WinVfs_Unmap, /* void (*xUnmap)(void *, jx9_int64); */ - 0, /* int (*xLink)(const char *, const char *, int) */ - 0, /* int (*xUmask)(int) */ - WinVfs_TempDir, /* void (*xTempDir)(jx9_context *) */ - WinVfs_ProcessId, /* unsigned int (*xProcessId)(void) */ - 0, /* int (*xUid)(void) */ - 0, /* int (*xGid)(void) */ - 0, /* void (*xUsername)(jx9_context *) */ - 0 /* int (*xExec)(const char *, jx9_context *) */ -}; -/* Windows file IO */ -#ifndef INVALID_SET_FILE_POINTER -# define INVALID_SET_FILE_POINTER ((DWORD)-1) -#endif -/* int (*xOpen)(const char *, int, jx9_value *, void **) */ -static int WinFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle) -{ - DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; - DWORD dwAccess = GENERIC_READ; - DWORD dwShare, dwCreate; - void *pConverted; - HANDLE pHandle; - - pConverted = jx9convertUtf8Filename(zPath); - if( pConverted == 0 ){ - return -1; - } - /* Set the desired flags according to the open mode */ - if( iOpenMode & JX9_IO_OPEN_CREATE ){ - /* Open existing file, or create if it doesn't exist */ - dwCreate = OPEN_ALWAYS; - if( iOpenMode & JX9_IO_OPEN_TRUNC ){ - /* If the specified file exists and is writable, the function overwrites the file */ - dwCreate = CREATE_ALWAYS; - } - }else if( iOpenMode & JX9_IO_OPEN_EXCL ){ - /* Creates a new file, only if it does not already exist. - * If the file exists, it fails. - */ - dwCreate = CREATE_NEW; - }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){ - /* Opens a file and truncates it so that its size is zero bytes - * The file must exist. - */ - dwCreate = TRUNCATE_EXISTING; - }else{ - /* Opens a file, only if it exists. */ - dwCreate = OPEN_EXISTING; - } - if( iOpenMode & JX9_IO_OPEN_RDWR ){ - /* Read+Write access */ - dwAccess |= GENERIC_WRITE; - }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){ - /* Write only access */ - dwAccess = GENERIC_WRITE; - } - if( iOpenMode & JX9_IO_OPEN_APPEND ){ - /* Append mode */ - dwAccess = FILE_APPEND_DATA; - } - if( iOpenMode & JX9_IO_OPEN_TEMP ){ - /* File is temporary */ - dwType = FILE_ATTRIBUTE_TEMPORARY; - } - dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE; - pHandle = CreateFileW((LPCWSTR)pConverted, dwAccess, dwShare, 0, dwCreate, dwType, 0); - HeapFree(GetProcessHeap(), 0, pConverted); - if( pHandle == INVALID_HANDLE_VALUE){ - SXUNUSED(pResource); /* MSVC warning */ - return -1; - } - /* Make the handle accessible to the upper layer */ - *ppHandle = (void *)pHandle; - return JX9_OK; -} -/* An instance of the following structure is used to record state information - * while iterating throw directory entries. - */ -typedef struct WinDir_Info WinDir_Info; -struct WinDir_Info -{ - HANDLE pDirHandle; - void *pPath; - WIN32_FIND_DATAW sInfo; - int rc; -}; -/* int (*xOpenDir)(const char *, jx9_value *, void **) */ -static int WinDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle) -{ - WinDir_Info *pDirInfo; - void *pConverted; - char *zPrep; - sxu32 n; - /* Prepare the path */ - n = SyStrlen(zPath); - zPrep = (char *)HeapAlloc(GetProcessHeap(), 0, n+sizeof("\\*")+4); - if( zPrep == 0 ){ - return -1; - } - SyMemcpy((const void *)zPath, zPrep, n); - zPrep[n] = '\\'; - zPrep[n+1] = '*'; - zPrep[n+2] = 0; - pConverted = jx9convertUtf8Filename(zPrep); - HeapFree(GetProcessHeap(), 0, zPrep); - if( pConverted == 0 ){ - return -1; - } - /* Allocate a new instance */ - pDirInfo = (WinDir_Info *)HeapAlloc(GetProcessHeap(), 0, sizeof(WinDir_Info)); - if( pDirInfo == 0 ){ - pResource = 0; /* Compiler warning */ - return -1; - } - pDirInfo->rc = SXRET_OK; - pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pConverted, &pDirInfo->sInfo); - if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){ - /* Cannot open directory */ - HeapFree(GetProcessHeap(), 0, pConverted); - HeapFree(GetProcessHeap(), 0, pDirInfo); - return -1; - } - /* Save the path */ - pDirInfo->pPath = pConverted; - /* Save our structure */ - *ppHandle = pDirInfo; - return JX9_OK; -} -/* void (*xCloseDir)(void *) */ -static void WinDir_Close(void *pUserData) -{ - WinDir_Info *pDirInfo = (WinDir_Info *)pUserData; - if( pDirInfo->pDirHandle != INVALID_HANDLE_VALUE ){ - FindClose(pDirInfo->pDirHandle); - } - HeapFree(GetProcessHeap(), 0, pDirInfo->pPath); - HeapFree(GetProcessHeap(), 0, pDirInfo); -} -/* void (*xClose)(void *); */ -static void WinFile_Close(void *pUserData) -{ - HANDLE pHandle = (HANDLE)pUserData; - CloseHandle(pHandle); -} -/* int (*xReadDir)(void *, jx9_context *) */ -static int WinDir_Read(void *pUserData, jx9_context *pCtx) -{ - WinDir_Info *pDirInfo = (WinDir_Info *)pUserData; - LPWIN32_FIND_DATAW pData; - char *zName; - BOOL rc; - sxu32 n; - if( pDirInfo->rc != SXRET_OK ){ - /* No more entry to process */ - return -1; - } - pData = &pDirInfo->sInfo; - for(;;){ - zName = jx9unicodeToUtf8(pData->cFileName); - if( zName == 0 ){ - /* Out of memory */ - return -1; - } - n = SyStrlen(zName); - /* Ignore '.' && '..' */ - if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){ - break; - } - HeapFree(GetProcessHeap(), 0, zName); - rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo); - if( !rc ){ - return -1; - } - } - /* Return the current file name */ - jx9_result_string(pCtx, zName, -1); - HeapFree(GetProcessHeap(), 0, zName); - /* Point to the next entry */ - rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo); - if( !rc ){ - pDirInfo->rc = SXERR_EOF; - } - return JX9_OK; -} -/* void (*xRewindDir)(void *) */ -static void WinDir_RewindDir(void *pUserData) -{ - WinDir_Info *pDirInfo = (WinDir_Info *)pUserData; - FindClose(pDirInfo->pDirHandle); - pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pDirInfo->pPath, &pDirInfo->sInfo); - if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){ - pDirInfo->rc = SXERR_EOF; - }else{ - pDirInfo->rc = SXRET_OK; - } -} -/* jx9_int64 (*xRead)(void *, void *, jx9_int64); */ -static jx9_int64 WinFile_Read(void *pOS, void *pBuffer, jx9_int64 nDatatoRead) -{ - HANDLE pHandle = (HANDLE)pOS; - DWORD nRd; - BOOL rc; - rc = ReadFile(pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0); - if( !rc ){ - /* EOF or IO error */ - return -1; - } - return (jx9_int64)nRd; -} -/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */ -static jx9_int64 WinFile_Write(void *pOS, const void *pBuffer, jx9_int64 nWrite) -{ - const char *zData = (const char *)pBuffer; - HANDLE pHandle = (HANDLE)pOS; - jx9_int64 nCount; - DWORD nWr; - BOOL rc; - nWr = 0; - nCount = 0; - for(;;){ - if( nWrite < 1 ){ - break; - } - rc = WriteFile(pHandle, zData, (DWORD)nWrite, &nWr, 0); - if( !rc ){ - /* IO error */ - break; - } - nWrite -= nWr; - nCount += nWr; - zData += nWr; - } - if( nWrite > 0 ){ - return -1; - } - return nCount; -} -/* int (*xSeek)(void *, jx9_int64, int) */ -static int WinFile_Seek(void *pUserData, jx9_int64 iOfft, int whence) -{ - HANDLE pHandle = (HANDLE)pUserData; - DWORD dwMove, dwNew; - LONG nHighOfft; - switch(whence){ - case 1:/*SEEK_CUR*/ - dwMove = FILE_CURRENT; - break; - case 2: /* SEEK_END */ - dwMove = FILE_END; - break; - case 0: /* SEEK_SET */ - default: - dwMove = FILE_BEGIN; - break; - } - nHighOfft = (LONG)(iOfft >> 32); - dwNew = SetFilePointer(pHandle, (LONG)iOfft, &nHighOfft, dwMove); - if( dwNew == INVALID_SET_FILE_POINTER ){ - return -1; - } - return JX9_OK; -} -/* int (*xLock)(void *, int) */ -static int WinFile_Lock(void *pUserData, int lock_type) -{ - HANDLE pHandle = (HANDLE)pUserData; - static DWORD dwLo = 0, dwHi = 0; /* xx: MT-SAFE */ - OVERLAPPED sDummy; - BOOL rc; - SyZero(&sDummy, sizeof(sDummy)); - /* Get the file size */ - if( lock_type < 1 ){ - /* Unlock the file */ - rc = UnlockFileEx(pHandle, 0, dwLo, dwHi, &sDummy); - }else{ - DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; /* Shared non-blocking lock by default*/ - /* Lock the file */ - if( lock_type == 1 /* LOCK_EXCL */ ){ - dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; - } - dwLo = GetFileSize(pHandle, &dwHi); - rc = LockFileEx(pHandle, dwFlags, 0, dwLo, dwHi, &sDummy); - } - return rc ? JX9_OK : -1 /* Lock error */; -} -/* jx9_int64 (*xTell)(void *) */ -static jx9_int64 WinFile_Tell(void *pUserData) -{ - HANDLE pHandle = (HANDLE)pUserData; - DWORD dwNew; - dwNew = SetFilePointer(pHandle, 0, 0, FILE_CURRENT/* SEEK_CUR */); - if( dwNew == INVALID_SET_FILE_POINTER ){ - return -1; - } - return (jx9_int64)dwNew; -} -/* int (*xTrunc)(void *, jx9_int64) */ -static int WinFile_Trunc(void *pUserData, jx9_int64 nOfft) -{ - HANDLE pHandle = (HANDLE)pUserData; - LONG HighOfft; - DWORD dwNew; - BOOL rc; - HighOfft = (LONG)(nOfft >> 32); - dwNew = SetFilePointer(pHandle, (LONG)nOfft, &HighOfft, FILE_BEGIN); - if( dwNew == INVALID_SET_FILE_POINTER ){ - return -1; - } - rc = SetEndOfFile(pHandle); - return rc ? JX9_OK : -1; -} -/* int (*xSync)(void *); */ -static int WinFile_Sync(void *pUserData) -{ - HANDLE pHandle = (HANDLE)pUserData; - BOOL rc; - rc = FlushFileBuffers(pHandle); - return rc ? JX9_OK : - 1; -} -/* int (*xStat)(void *, jx9_value *, jx9_value *) */ -static int WinFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker) -{ - BY_HANDLE_FILE_INFORMATION sInfo; - HANDLE pHandle = (HANDLE)pUserData; - BOOL rc; - rc = GetFileInformationByHandle(pHandle, &sInfo); - if( !rc ){ - return -1; - } - /* dev */ - jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber); - jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */ - /* ino */ - jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow)); - jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */ - /* mode */ - jx9_value_int(pWorker, 0); - jx9_array_add_strkey_elem(pArray, "mode", pWorker); - /* nlink */ - jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks); - jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */ - /* uid, gid, rdev */ - jx9_value_int(pWorker, 0); - jx9_array_add_strkey_elem(pArray, "uid", pWorker); - jx9_array_add_strkey_elem(pArray, "gid", pWorker); - jx9_array_add_strkey_elem(pArray, "rdev", pWorker); - /* size */ - jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow)); - jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */ - /* atime */ - jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime)); - jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */ - /* mtime */ - jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime)); - jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */ - /* ctime */ - jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime)); - jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */ - /* blksize, blocks */ - jx9_value_int(pWorker, 0); - jx9_array_add_strkey_elem(pArray, "blksize", pWorker); - jx9_array_add_strkey_elem(pArray, "blocks", pWorker); - return JX9_OK; -} -/* Export the file:// stream */ -static const jx9_io_stream sWinFileStream = { - "file", /* Stream name */ - JX9_IO_STREAM_VERSION, - WinFile_Open, /* xOpen */ - WinDir_Open, /* xOpenDir */ - WinFile_Close, /* xClose */ - WinDir_Close, /* xCloseDir */ - WinFile_Read, /* xRead */ - WinDir_Read, /* xReadDir */ - WinFile_Write, /* xWrite */ - WinFile_Seek, /* xSeek */ - WinFile_Lock, /* xLock */ - WinDir_RewindDir, /* xRewindDir */ - WinFile_Tell, /* xTell */ - WinFile_Trunc, /* xTrunc */ - WinFile_Sync, /* xSeek */ - WinFile_Stat /* xStat */ -}; -#elif defined(__UNIXES__) -/* - * UNIX VFS implementation for the JX9 engine. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -/* int (*xchdir)(const char *) */ -static int UnixVfs_chdir(const char *zPath) -{ - int rc; - rc = chdir(zPath); - return rc == 0 ? JX9_OK : -1; -} -/* int (*xGetcwd)(jx9_context *) */ -static int UnixVfs_getcwd(jx9_context *pCtx) -{ - char zBuf[4096]; - char *zDir; - /* Get the current directory */ - zDir = getcwd(zBuf, sizeof(zBuf)); - if( zDir == 0 ){ - return -1; - } - jx9_result_string(pCtx, zDir, -1/*Compute length automatically*/); - return JX9_OK; -} -/* int (*xMkdir)(const char *, int, int) */ -static int UnixVfs_mkdir(const char *zPath, int mode, int recursive) -{ - int rc; - rc = mkdir(zPath, mode); - recursive = 0; /* cc warning */ - return rc == 0 ? JX9_OK : -1; -} -/* int (*xRmdir)(const char *) */ -static int UnixVfs_rmdir(const char *zPath) -{ - int rc; - rc = rmdir(zPath); - return rc == 0 ? JX9_OK : -1; -} -/* int (*xIsdir)(const char *) */ -static int UnixVfs_isdir(const char *zPath) -{ - struct stat st; - int rc; - rc = stat(zPath, &st); - if( rc != 0 ){ - return -1; - } - rc = S_ISDIR(st.st_mode); - return rc ? JX9_OK : -1 ; -} -/* int (*xRename)(const char *, const char *) */ -static int UnixVfs_Rename(const char *zOld, const char *zNew) -{ - int rc; - rc = rename(zOld, zNew); - return rc == 0 ? JX9_OK : -1; -} -/* int (*xRealpath)(const char *, jx9_context *) */ -static int UnixVfs_Realpath(const char *zPath, jx9_context *pCtx) -{ -#ifndef JX9_UNIX_OLD_LIBC - char *zReal; - zReal = realpath(zPath, 0); - if( zReal == 0 ){ - return -1; - } - jx9_result_string(pCtx, zReal, -1/*Compute length automatically*/); - /* Release the allocated buffer */ - free(zReal); - return JX9_OK; -#else - zPath = 0; /* cc warning */ - pCtx = 0; - return -1; -#endif -} -/* int (*xSleep)(unsigned int) */ -static int UnixVfs_Sleep(unsigned int uSec) -{ - usleep(uSec); - return JX9_OK; -} -/* int (*xUnlink)(const char *) */ -static int UnixVfs_unlink(const char *zPath) -{ - int rc; - rc = unlink(zPath); - return rc == 0 ? JX9_OK : -1 ; -} -/* int (*xFileExists)(const char *) */ -static int UnixVfs_FileExists(const char *zPath) -{ - int rc; - rc = access(zPath, F_OK); - return rc == 0 ? JX9_OK : -1; -} -/* jx9_int64 (*xFileSize)(const char *) */ -static jx9_int64 UnixVfs_FileSize(const char *zPath) -{ - struct stat st; - int rc; - rc = stat(zPath, &st); - if( rc != 0 ){ - return -1; - } - return (jx9_int64)st.st_size; -} -/* int (*xTouch)(const char *, jx9_int64, jx9_int64) */ -static int UnixVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time) -{ - struct utimbuf ut; - int rc; - ut.actime = (time_t)access_time; - ut.modtime = (time_t)touch_time; - rc = utime(zPath, &ut); - if( rc != 0 ){ - return -1; - } - return JX9_OK; -} -/* jx9_int64 (*xFileAtime)(const char *) */ -static jx9_int64 UnixVfs_FileAtime(const char *zPath) -{ - struct stat st; - int rc; - rc = stat(zPath, &st); - if( rc != 0 ){ - return -1; - } - return (jx9_int64)st.st_atime; -} -/* jx9_int64 (*xFileMtime)(const char *) */ -static jx9_int64 UnixVfs_FileMtime(const char *zPath) -{ - struct stat st; - int rc; - rc = stat(zPath, &st); - if( rc != 0 ){ - return -1; - } - return (jx9_int64)st.st_mtime; -} -/* jx9_int64 (*xFileCtime)(const char *) */ -static jx9_int64 UnixVfs_FileCtime(const char *zPath) -{ - struct stat st; - int rc; - rc = stat(zPath, &st); - if( rc != 0 ){ - return -1; - } - return (jx9_int64)st.st_ctime; -} -/* int (*xStat)(const char *, jx9_value *, jx9_value *) */ -static int UnixVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker) -{ - struct stat st; - int rc; - rc = stat(zPath, &st); - if( rc != 0 ){ - return -1; - } - /* dev */ - jx9_value_int64(pWorker, (jx9_int64)st.st_dev); - jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */ - /* ino */ - jx9_value_int64(pWorker, (jx9_int64)st.st_ino); - jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */ - /* mode */ - jx9_value_int(pWorker, (int)st.st_mode); - jx9_array_add_strkey_elem(pArray, "mode", pWorker); - /* nlink */ - jx9_value_int(pWorker, (int)st.st_nlink); - jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */ - /* uid, gid, rdev */ - jx9_value_int(pWorker, (int)st.st_uid); - jx9_array_add_strkey_elem(pArray, "uid", pWorker); - jx9_value_int(pWorker, (int)st.st_gid); - jx9_array_add_strkey_elem(pArray, "gid", pWorker); - jx9_value_int(pWorker, (int)st.st_rdev); - jx9_array_add_strkey_elem(pArray, "rdev", pWorker); - /* size */ - jx9_value_int64(pWorker, (jx9_int64)st.st_size); - jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */ - /* atime */ - jx9_value_int64(pWorker, (jx9_int64)st.st_atime); - jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */ - /* mtime */ - jx9_value_int64(pWorker, (jx9_int64)st.st_mtime); - jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */ - /* ctime */ - jx9_value_int64(pWorker, (jx9_int64)st.st_ctime); - jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */ - /* blksize, blocks */ - jx9_value_int(pWorker, (int)st.st_blksize); - jx9_array_add_strkey_elem(pArray, "blksize", pWorker); - jx9_value_int(pWorker, (int)st.st_blocks); - jx9_array_add_strkey_elem(pArray, "blocks", pWorker); - return JX9_OK; -} -/* int (*xlStat)(const char *, jx9_value *, jx9_value *) */ -static int UnixVfs_lStat(const char *zPath, jx9_value *pArray, jx9_value *pWorker) -{ - struct stat st; - int rc; - rc = lstat(zPath, &st); - if( rc != 0 ){ - return -1; - } - /* dev */ - jx9_value_int64(pWorker, (jx9_int64)st.st_dev); - jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */ - /* ino */ - jx9_value_int64(pWorker, (jx9_int64)st.st_ino); - jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */ - /* mode */ - jx9_value_int(pWorker, (int)st.st_mode); - jx9_array_add_strkey_elem(pArray, "mode", pWorker); - /* nlink */ - jx9_value_int(pWorker, (int)st.st_nlink); - jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */ - /* uid, gid, rdev */ - jx9_value_int(pWorker, (int)st.st_uid); - jx9_array_add_strkey_elem(pArray, "uid", pWorker); - jx9_value_int(pWorker, (int)st.st_gid); - jx9_array_add_strkey_elem(pArray, "gid", pWorker); - jx9_value_int(pWorker, (int)st.st_rdev); - jx9_array_add_strkey_elem(pArray, "rdev", pWorker); - /* size */ - jx9_value_int64(pWorker, (jx9_int64)st.st_size); - jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */ - /* atime */ - jx9_value_int64(pWorker, (jx9_int64)st.st_atime); - jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */ - /* mtime */ - jx9_value_int64(pWorker, (jx9_int64)st.st_mtime); - jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */ - /* ctime */ - jx9_value_int64(pWorker, (jx9_int64)st.st_ctime); - jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */ - /* blksize, blocks */ - jx9_value_int(pWorker, (int)st.st_blksize); - jx9_array_add_strkey_elem(pArray, "blksize", pWorker); - jx9_value_int(pWorker, (int)st.st_blocks); - jx9_array_add_strkey_elem(pArray, "blocks", pWorker); - return JX9_OK; -} -/* int (*xChmod)(const char *, int) */ -static int UnixVfs_Chmod(const char *zPath, int mode) -{ - int rc; - rc = chmod(zPath, (mode_t)mode); - return rc == 0 ? JX9_OK : - 1; -} -/* int (*xChown)(const char *, const char *) */ -static int UnixVfs_Chown(const char *zPath, const char *zUser) -{ -#ifndef JX9_UNIX_STATIC_BUILD - struct passwd *pwd; - uid_t uid; - int rc; - pwd = getpwnam(zUser); /* Try getting UID for username */ - if (pwd == 0) { - return -1; - } - uid = pwd->pw_uid; - rc = chown(zPath, uid, -1); - return rc == 0 ? JX9_OK : -1; -#else - SXUNUSED(zPath); - SXUNUSED(zUser); - return -1; -#endif /* JX9_UNIX_STATIC_BUILD */ -} -/* int (*xChgrp)(const char *, const char *) */ -static int UnixVfs_Chgrp(const char *zPath, const char *zGroup) -{ -#ifndef JX9_UNIX_STATIC_BUILD - struct group *group; - gid_t gid; - int rc; - group = getgrnam(zGroup); - if (group == 0) { - return -1; - } - gid = group->gr_gid; - rc = chown(zPath, -1, gid); - return rc == 0 ? JX9_OK : -1; -#else - SXUNUSED(zPath); - SXUNUSED(zGroup); - return -1; -#endif /* JX9_UNIX_STATIC_BUILD */ -} -/* int (*xIsfile)(const char *) */ -static int UnixVfs_isfile(const char *zPath) -{ - struct stat st; - int rc; - rc = stat(zPath, &st); - if( rc != 0 ){ - return -1; - } - rc = S_ISREG(st.st_mode); - return rc ? JX9_OK : -1 ; -} -/* int (*xIslink)(const char *) */ -static int UnixVfs_islink(const char *zPath) -{ - struct stat st; - int rc; - rc = stat(zPath, &st); - if( rc != 0 ){ - return -1; - } - rc = S_ISLNK(st.st_mode); - return rc ? JX9_OK : -1 ; -} -/* int (*xReadable)(const char *) */ -static int UnixVfs_isreadable(const char *zPath) -{ - int rc; - rc = access(zPath, R_OK); - return rc == 0 ? JX9_OK : -1; -} -/* int (*xWritable)(const char *) */ -static int UnixVfs_iswritable(const char *zPath) -{ - int rc; - rc = access(zPath, W_OK); - return rc == 0 ? JX9_OK : -1; -} -/* int (*xExecutable)(const char *) */ -static int UnixVfs_isexecutable(const char *zPath) -{ - int rc; - rc = access(zPath, X_OK); - return rc == 0 ? JX9_OK : -1; -} -/* int (*xFiletype)(const char *, jx9_context *) */ -static int UnixVfs_Filetype(const char *zPath, jx9_context *pCtx) -{ - struct stat st; - int rc; - rc = stat(zPath, &st); - if( rc != 0 ){ - /* Expand 'unknown' */ - jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); - return -1; - } - if(S_ISREG(st.st_mode) ){ - jx9_result_string(pCtx, "file", sizeof("file")-1); - }else if(S_ISDIR(st.st_mode)){ - jx9_result_string(pCtx, "dir", sizeof("dir")-1); - }else if(S_ISLNK(st.st_mode)){ - jx9_result_string(pCtx, "link", sizeof("link")-1); - }else if(S_ISBLK(st.st_mode)){ - jx9_result_string(pCtx, "block", sizeof("block")-1); - }else if(S_ISSOCK(st.st_mode)){ - jx9_result_string(pCtx, "socket", sizeof("socket")-1); - }else if(S_ISFIFO(st.st_mode)){ - jx9_result_string(pCtx, "fifo", sizeof("fifo")-1); - }else{ - jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); - } - return JX9_OK; -} -/* int (*xGetenv)(const char *, jx9_context *) */ -static int UnixVfs_Getenv(const char *zVar, jx9_context *pCtx) -{ - char *zEnv; - zEnv = getenv(zVar); - if( zEnv == 0 ){ - return -1; - } - jx9_result_string(pCtx, zEnv, -1/*Compute length automatically*/); - return JX9_OK; -} -/* int (*xSetenv)(const char *, const char *) */ -static int UnixVfs_Setenv(const char *zName, const char *zValue) -{ - int rc; - rc = setenv(zName, zValue, 1); - return rc == 0 ? JX9_OK : -1; -} -/* int (*xMmap)(const char *, void **, jx9_int64 *) */ -static int UnixVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize) -{ - struct stat st; - void *pMap; - int fd; - int rc; - /* Open the file in a read-only mode */ - fd = open(zPath, O_RDONLY); - if( fd < 0 ){ - return -1; - } - /* stat the handle */ - fstat(fd, &st); - /* Obtain a memory view of the whole file */ - pMap = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0); - rc = JX9_OK; - if( pMap == MAP_FAILED ){ - rc = -1; - }else{ - /* Point to the memory view */ - *ppMap = pMap; - *pSize = (jx9_int64)st.st_size; - } - close(fd); - return rc; -} -/* void (*xUnmap)(void *, jx9_int64) */ -static void UnixVfs_Unmap(void *pView, jx9_int64 nSize) -{ - munmap(pView, (size_t)nSize); -} -/* void (*xTempDir)(jx9_context *) */ -static void UnixVfs_TempDir(jx9_context *pCtx) -{ - static const char *azDirs[] = { - "/var/tmp", - "/usr/tmp", - "/usr/local/tmp" - }; - unsigned int i; - struct stat buf; - const char *zDir; - zDir = getenv("TMPDIR"); - if( zDir && zDir[0] != 0 && !access(zDir, 07) ){ - jx9_result_string(pCtx, zDir, -1); - return; - } - for(i=0; ipw_name, -1); -#else - jx9_result_string(pCtx, "Unknown", -1); -#endif /* JX9_UNIX_STATIC_BUILD */ - return; -} -/* int (*xLink)(const char *, const char *, int) */ -static int UnixVfs_link(const char *zSrc, const char *zTarget, int is_sym) -{ - int rc; - if( is_sym ){ - /* Symbolic link */ - rc = symlink(zSrc, zTarget); - }else{ - /* Hard link */ - rc = link(zSrc, zTarget); - } - return rc == 0 ? JX9_OK : -1; -} -/* int (*xChroot)(const char *) */ -static int UnixVfs_chroot(const char *zRootDir) -{ - int rc; - rc = chroot(zRootDir); - return rc == 0 ? JX9_OK : -1; -} -/* Export the UNIX vfs */ -static const jx9_vfs sUnixVfs = { - "Unix_vfs", - JX9_VFS_VERSION, - UnixVfs_chdir, /* int (*xChdir)(const char *) */ - UnixVfs_chroot, /* int (*xChroot)(const char *); */ - UnixVfs_getcwd, /* int (*xGetcwd)(jx9_context *) */ - UnixVfs_mkdir, /* int (*xMkdir)(const char *, int, int) */ - UnixVfs_rmdir, /* int (*xRmdir)(const char *) */ - UnixVfs_isdir, /* int (*xIsdir)(const char *) */ - UnixVfs_Rename, /* int (*xRename)(const char *, const char *) */ - UnixVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/ - UnixVfs_Sleep, /* int (*xSleep)(unsigned int) */ - UnixVfs_unlink, /* int (*xUnlink)(const char *) */ - UnixVfs_FileExists, /* int (*xFileExists)(const char *) */ - UnixVfs_Chmod, /*int (*xChmod)(const char *, int)*/ - UnixVfs_Chown, /*int (*xChown)(const char *, const char *)*/ - UnixVfs_Chgrp, /*int (*xChgrp)(const char *, const char *)*/ - 0, /* jx9_int64 (*xFreeSpace)(const char *) */ - 0, /* jx9_int64 (*xTotalSpace)(const char *) */ - UnixVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */ - UnixVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */ - UnixVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */ - UnixVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */ - UnixVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */ - UnixVfs_lStat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */ - UnixVfs_isfile, /* int (*xIsfile)(const char *) */ - UnixVfs_islink, /* int (*xIslink)(const char *) */ - UnixVfs_isreadable, /* int (*xReadable)(const char *) */ - UnixVfs_iswritable, /* int (*xWritable)(const char *) */ - UnixVfs_isexecutable, /* int (*xExecutable)(const char *) */ - UnixVfs_Filetype, /* int (*xFiletype)(const char *, jx9_context *) */ - UnixVfs_Getenv, /* int (*xGetenv)(const char *, jx9_context *) */ - UnixVfs_Setenv, /* int (*xSetenv)(const char *, const char *) */ - UnixVfs_Touch, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */ - UnixVfs_Mmap, /* int (*xMmap)(const char *, void **, jx9_int64 *) */ - UnixVfs_Unmap, /* void (*xUnmap)(void *, jx9_int64); */ - UnixVfs_link, /* int (*xLink)(const char *, const char *, int) */ - UnixVfs_Umask, /* int (*xUmask)(int) */ - UnixVfs_TempDir, /* void (*xTempDir)(jx9_context *) */ - UnixVfs_ProcessId, /* unsigned int (*xProcessId)(void) */ - UnixVfs_uid, /* int (*xUid)(void) */ - UnixVfs_gid, /* int (*xGid)(void) */ - UnixVfs_Username, /* void (*xUsername)(jx9_context *) */ - 0 /* int (*xExec)(const char *, jx9_context *) */ -}; -/* UNIX File IO */ -#define JX9_UNIX_OPEN_MODE 0640 /* Default open mode */ -/* int (*xOpen)(const char *, int, jx9_value *, void **) */ -static int UnixFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle) -{ - int iOpen = O_RDONLY; - int fd; - /* Set the desired flags according to the open mode */ - if( iOpenMode & JX9_IO_OPEN_CREATE ){ - /* Open existing file, or create if it doesn't exist */ - iOpen = O_CREAT; - if( iOpenMode & JX9_IO_OPEN_TRUNC ){ - /* If the specified file exists and is writable, the function overwrites the file */ - iOpen |= O_TRUNC; - SXUNUSED(pResource); /* cc warning */ - } - }else if( iOpenMode & JX9_IO_OPEN_EXCL ){ - /* Creates a new file, only if it does not already exist. - * If the file exists, it fails. - */ - iOpen = O_CREAT|O_EXCL; - }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){ - /* Opens a file and truncates it so that its size is zero bytes - * The file must exist. - */ - iOpen = O_RDWR|O_TRUNC; - } - if( iOpenMode & JX9_IO_OPEN_RDWR ){ - /* Read+Write access */ - iOpen &= ~O_RDONLY; - iOpen |= O_RDWR; - }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){ - /* Write only access */ - iOpen &= ~O_RDONLY; - iOpen |= O_WRONLY; - } - if( iOpenMode & JX9_IO_OPEN_APPEND ){ - /* Append mode */ - iOpen |= O_APPEND; - } -#ifdef O_TEMP - if( iOpenMode & JX9_IO_OPEN_TEMP ){ - /* File is temporary */ - iOpen |= O_TEMP; - } -#endif - /* Open the file now */ - fd = open(zPath, iOpen, JX9_UNIX_OPEN_MODE); - if( fd < 0 ){ - /* IO error */ - return -1; - } - /* Save the handle */ - *ppHandle = SX_INT_TO_PTR(fd); - return JX9_OK; -} -/* int (*xOpenDir)(const char *, jx9_value *, void **) */ -static int UnixDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle) -{ - DIR *pDir; - /* Open the target directory */ - pDir = opendir(zPath); - if( pDir == 0 ){ - pResource = 0; /* Compiler warning */ - return -1; - } - /* Save our structure */ - *ppHandle = pDir; - return JX9_OK; -} -/* void (*xCloseDir)(void *) */ -static void UnixDir_Close(void *pUserData) -{ - closedir((DIR *)pUserData); -} -/* void (*xClose)(void *); */ -static void UnixFile_Close(void *pUserData) -{ - close(SX_PTR_TO_INT(pUserData)); -} -/* int (*xReadDir)(void *, jx9_context *) */ -static int UnixDir_Read(void *pUserData, jx9_context *pCtx) -{ - DIR *pDir = (DIR *)pUserData; - struct dirent *pEntry; - char *zName = 0; /* cc warning */ - sxu32 n = 0; - for(;;){ - pEntry = readdir(pDir); - if( pEntry == 0 ){ - /* No more entries to process */ - return -1; - } - zName = pEntry->d_name; - n = SyStrlen(zName); - /* Ignore '.' && '..' */ - if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){ - break; - } - /* Next entry */ - } - /* Return the current file name */ - jx9_result_string(pCtx, zName, (int)n); - return JX9_OK; -} -/* void (*xRewindDir)(void *) */ -static void UnixDir_Rewind(void *pUserData) -{ - rewinddir((DIR *)pUserData); -} -/* jx9_int64 (*xRead)(void *, void *, jx9_int64); */ -static jx9_int64 UnixFile_Read(void *pUserData, void *pBuffer, jx9_int64 nDatatoRead) -{ - ssize_t nRd; - nRd = read(SX_PTR_TO_INT(pUserData), pBuffer, (size_t)nDatatoRead); - if( nRd < 1 ){ - /* EOF or IO error */ - return -1; - } - return (jx9_int64)nRd; -} -/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */ -static jx9_int64 UnixFile_Write(void *pUserData, const void *pBuffer, jx9_int64 nWrite) -{ - const char *zData = (const char *)pBuffer; - int fd = SX_PTR_TO_INT(pUserData); - jx9_int64 nCount; - ssize_t nWr; - nCount = 0; - for(;;){ - if( nWrite < 1 ){ - break; - } - nWr = write(fd, zData, (size_t)nWrite); - if( nWr < 1 ){ - /* IO error */ - break; - } - nWrite -= nWr; - nCount += nWr; - zData += nWr; - } - if( nWrite > 0 ){ - return -1; - } - return nCount; -} -/* int (*xSeek)(void *, jx9_int64, int) */ -static int UnixFile_Seek(void *pUserData, jx9_int64 iOfft, int whence) -{ - off_t iNew; - switch(whence){ - case 1:/*SEEK_CUR*/ - whence = SEEK_CUR; - break; - case 2: /* SEEK_END */ - whence = SEEK_END; - break; - case 0: /* SEEK_SET */ - default: - whence = SEEK_SET; - break; - } - iNew = lseek(SX_PTR_TO_INT(pUserData), (off_t)iOfft, whence); - if( iNew < 0 ){ - return -1; - } - return JX9_OK; -} -/* int (*xLock)(void *, int) */ -static int UnixFile_Lock(void *pUserData, int lock_type) -{ - int fd = SX_PTR_TO_INT(pUserData); - int rc = JX9_OK; /* cc warning */ - if( lock_type < 0 ){ - /* Unlock the file */ - rc = flock(fd, LOCK_UN); - }else{ - if( lock_type == 1 ){ - /* Exculsive lock */ - rc = flock(fd, LOCK_EX); - }else{ - /* Shared lock */ - rc = flock(fd, LOCK_SH); - } - } - return !rc ? JX9_OK : -1; -} -/* jx9_int64 (*xTell)(void *) */ -static jx9_int64 UnixFile_Tell(void *pUserData) -{ - off_t iNew; - iNew = lseek(SX_PTR_TO_INT(pUserData), 0, SEEK_CUR); - return (jx9_int64)iNew; -} -/* int (*xTrunc)(void *, jx9_int64) */ -static int UnixFile_Trunc(void *pUserData, jx9_int64 nOfft) -{ - int rc; - rc = ftruncate(SX_PTR_TO_INT(pUserData), (off_t)nOfft); - if( rc != 0 ){ - return -1; - } - return JX9_OK; -} -/* int (*xSync)(void *); */ -static int UnixFile_Sync(void *pUserData) -{ - int rc; - rc = fsync(SX_PTR_TO_INT(pUserData)); - return rc == 0 ? JX9_OK : - 1; -} -/* int (*xStat)(void *, jx9_value *, jx9_value *) */ -static int UnixFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker) -{ - struct stat st; - int rc; - rc = fstat(SX_PTR_TO_INT(pUserData), &st); - if( rc != 0 ){ - return -1; - } - /* dev */ - jx9_value_int64(pWorker, (jx9_int64)st.st_dev); - jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */ - /* ino */ - jx9_value_int64(pWorker, (jx9_int64)st.st_ino); - jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */ - /* mode */ - jx9_value_int(pWorker, (int)st.st_mode); - jx9_array_add_strkey_elem(pArray, "mode", pWorker); - /* nlink */ - jx9_value_int(pWorker, (int)st.st_nlink); - jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */ - /* uid, gid, rdev */ - jx9_value_int(pWorker, (int)st.st_uid); - jx9_array_add_strkey_elem(pArray, "uid", pWorker); - jx9_value_int(pWorker, (int)st.st_gid); - jx9_array_add_strkey_elem(pArray, "gid", pWorker); - jx9_value_int(pWorker, (int)st.st_rdev); - jx9_array_add_strkey_elem(pArray, "rdev", pWorker); - /* size */ - jx9_value_int64(pWorker, (jx9_int64)st.st_size); - jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */ - /* atime */ - jx9_value_int64(pWorker, (jx9_int64)st.st_atime); - jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */ - /* mtime */ - jx9_value_int64(pWorker, (jx9_int64)st.st_mtime); - jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */ - /* ctime */ - jx9_value_int64(pWorker, (jx9_int64)st.st_ctime); - jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */ - /* blksize, blocks */ - jx9_value_int(pWorker, (int)st.st_blksize); - jx9_array_add_strkey_elem(pArray, "blksize", pWorker); - jx9_value_int(pWorker, (int)st.st_blocks); - jx9_array_add_strkey_elem(pArray, "blocks", pWorker); - return JX9_OK; -} -/* Export the file:// stream */ -static const jx9_io_stream sUnixFileStream = { - "file", /* Stream name */ - JX9_IO_STREAM_VERSION, - UnixFile_Open, /* xOpen */ - UnixDir_Open, /* xOpenDir */ - UnixFile_Close, /* xClose */ - UnixDir_Close, /* xCloseDir */ - UnixFile_Read, /* xRead */ - UnixDir_Read, /* xReadDir */ - UnixFile_Write, /* xWrite */ - UnixFile_Seek, /* xSeek */ - UnixFile_Lock, /* xLock */ - UnixDir_Rewind, /* xRewindDir */ - UnixFile_Tell, /* xTell */ - UnixFile_Trunc, /* xTrunc */ - UnixFile_Sync, /* xSeek */ - UnixFile_Stat /* xStat */ -}; -#endif /* __WINNT__/__UNIXES__ */ -#endif /* JX9_DISABLE_DISK_IO */ -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -/* - * Export the builtin vfs. - * Return a pointer to the builtin vfs if available. - * Otherwise return the null_vfs [i.e: a no-op vfs] instead. - * Note: - * The built-in vfs is always available for Windows/UNIX systems. - * Note: - * If the engine is compiled with the JX9_DISABLE_DISK_IO/JX9_DISABLE_BUILTIN_FUNC - * directives defined then this function return the null_vfs instead. - */ -JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void) -{ -#ifndef JX9_DISABLE_BUILTIN_FUNC -#ifdef JX9_DISABLE_DISK_IO - return &null_vfs; -#else -#ifdef __WINNT__ - return &sWinVfs; -#elif defined(__UNIXES__) - return &sUnixVfs; -#else - return &null_vfs; -#endif /* __WINNT__/__UNIXES__ */ -#endif /*JX9_DISABLE_DISK_IO*/ -#else - return &null_vfs; -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -#ifndef JX9_DISABLE_DISK_IO -/* - * The following defines are mostly used by the UNIX built and have - * no particular meaning on windows. - */ -#ifndef STDIN_FILENO -#define STDIN_FILENO 0 -#endif -#ifndef STDOUT_FILENO -#define STDOUT_FILENO 1 -#endif -#ifndef STDERR_FILENO -#define STDERR_FILENO 2 -#endif -/* - * jx9:// Accessing various I/O streams - * According to the JX9 langage reference manual - * JX9 provides a number of miscellaneous I/O streams that allow access to JX9's own input - * and output streams, the standard input, output and error file descriptors. - * jx9://stdin, jx9://stdout and jx9://stderr: - * Allow direct access to the corresponding input or output stream of the JX9 process. - * The stream references a duplicate file descriptor, so if you open jx9://stdin and later - * close it, you close only your copy of the descriptor-the actual stream referenced by STDIN is unaffected. - * jx9://stdin is read-only, whereas jx9://stdout and jx9://stderr are write-only. - * jx9://output - * jx9://output is a write-only stream that allows you to write to the output buffer - * mechanism in the same way as print and print. - */ -typedef struct jx9_stream_data jx9_stream_data; -/* Supported IO streams */ -#define JX9_IO_STREAM_STDIN 1 /* jx9://stdin */ -#define JX9_IO_STREAM_STDOUT 2 /* jx9://stdout */ -#define JX9_IO_STREAM_STDERR 3 /* jx9://stderr */ -#define JX9_IO_STREAM_OUTPUT 4 /* jx9://output */ - /* The following structure is the private data associated with the jx9:// stream */ -struct jx9_stream_data -{ - jx9_vm *pVm; /* VM that own this instance */ - int iType; /* Stream type */ - union{ - void *pHandle; /* Stream handle */ - jx9_output_consumer sConsumer; /* VM output consumer */ - }x; -}; -/* - * Allocate a new instance of the jx9_stream_data structure. - */ -static jx9_stream_data * JX9StreamDataInit(jx9_vm *pVm, int iType) -{ - jx9_stream_data *pData; - if( pVm == 0 ){ - return 0; - } - /* Allocate a new instance */ - pData = (jx9_stream_data *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(jx9_stream_data)); - if( pData == 0 ){ - return 0; - } - /* Zero the structure */ - SyZero(pData, sizeof(jx9_stream_data)); - /* Initialize fields */ - pData->iType = iType; - if( iType == JX9_IO_STREAM_OUTPUT ){ - /* Point to the default VM consumer routine. */ - pData->x.sConsumer = pVm->sVmConsumer; - }else{ -#ifdef __WINNT__ - DWORD nChannel; - switch(iType){ - case JX9_IO_STREAM_STDOUT: nChannel = STD_OUTPUT_HANDLE; break; - case JX9_IO_STREAM_STDERR: nChannel = STD_ERROR_HANDLE; break; - default: - nChannel = STD_INPUT_HANDLE; - break; - } - pData->x.pHandle = GetStdHandle(nChannel); -#else - /* Assume an UNIX system */ - int ifd = STDIN_FILENO; - switch(iType){ - case JX9_IO_STREAM_STDOUT: ifd = STDOUT_FILENO; break; - case JX9_IO_STREAM_STDERR: ifd = STDERR_FILENO; break; - default: - break; - } - pData->x.pHandle = SX_INT_TO_PTR(ifd); -#endif - } - pData->pVm = pVm; - return pData; -} -/* - * Implementation of the jx9:// IO streams routines - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* int (*xOpen)(const char *, int, jx9_value *, void **) */ -static int JX9StreamData_Open(const char *zName, int iMode, jx9_value *pResource, void ** ppHandle) -{ - jx9_stream_data *pData; - SyString sStream; - SyStringInitFromBuf(&sStream, zName, SyStrlen(zName)); - /* Trim leading and trailing white spaces */ - SyStringFullTrim(&sStream); - /* Stream to open */ - if( SyStrnicmp(sStream.zString, "stdin", sizeof("stdin")-1) == 0 ){ - iMode = JX9_IO_STREAM_STDIN; - }else if( SyStrnicmp(sStream.zString, "output", sizeof("output")-1) == 0 ){ - iMode = JX9_IO_STREAM_OUTPUT; - }else if( SyStrnicmp(sStream.zString, "stdout", sizeof("stdout")-1) == 0 ){ - iMode = JX9_IO_STREAM_STDOUT; - }else if( SyStrnicmp(sStream.zString, "stderr", sizeof("stderr")-1) == 0 ){ - iMode = JX9_IO_STREAM_STDERR; - }else{ - /* unknown stream name */ - return -1; - } - /* Create our handle */ - pData = JX9StreamDataInit(pResource?pResource->pVm:0, iMode); - if( pData == 0 ){ - return -1; - } - /* Make the handle public */ - *ppHandle = (void *)pData; - return JX9_OK; -} -/* jx9_int64 (*xRead)(void *, void *, jx9_int64) */ -static jx9_int64 JX9StreamData_Read(void *pHandle, void *pBuffer, jx9_int64 nDatatoRead) -{ - jx9_stream_data *pData = (jx9_stream_data *)pHandle; - if( pData == 0 ){ - return -1; - } - if( pData->iType != JX9_IO_STREAM_STDIN ){ - /* Forbidden */ - return -1; - } -#ifdef __WINNT__ - { - DWORD nRd; - BOOL rc; - rc = ReadFile(pData->x.pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0); - if( !rc ){ - /* IO error */ - return -1; - } - return (jx9_int64)nRd; - } -#elif defined(__UNIXES__) - { - ssize_t nRd; - int fd; - fd = SX_PTR_TO_INT(pData->x.pHandle); - nRd = read(fd, pBuffer, (size_t)nDatatoRead); - if( nRd < 1 ){ - return -1; - } - return (jx9_int64)nRd; - } -#else - return -1; -#endif -} -/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64) */ -static jx9_int64 JX9StreamData_Write(void *pHandle, const void *pBuf, jx9_int64 nWrite) -{ - jx9_stream_data *pData = (jx9_stream_data *)pHandle; - if( pData == 0 ){ - return -1; - } - if( pData->iType == JX9_IO_STREAM_STDIN ){ - /* Forbidden */ - return -1; - }else if( pData->iType == JX9_IO_STREAM_OUTPUT ){ - jx9_output_consumer *pCons = &pData->x.sConsumer; - int rc; - /* Call the vm output consumer */ - rc = pCons->xConsumer(pBuf, (unsigned int)nWrite, pCons->pUserData); - if( rc == JX9_ABORT ){ - return -1; - } - return nWrite; - } -#ifdef __WINNT__ - { - DWORD nWr; - BOOL rc; - rc = WriteFile(pData->x.pHandle, pBuf, (DWORD)nWrite, &nWr, 0); - if( !rc ){ - /* IO error */ - return -1; - } - return (jx9_int64)nWr; - } -#elif defined(__UNIXES__) - { - ssize_t nWr; - int fd; - fd = SX_PTR_TO_INT(pData->x.pHandle); - nWr = write(fd, pBuf, (size_t)nWrite); - if( nWr < 1 ){ - return -1; - } - return (jx9_int64)nWr; - } -#else - return -1; -#endif -} -/* void (*xClose)(void *) */ -static void JX9StreamData_Close(void *pHandle) -{ - jx9_stream_data *pData = (jx9_stream_data *)pHandle; - jx9_vm *pVm; - if( pData == 0 ){ - return; - } - pVm = pData->pVm; - /* Free the instance */ - SyMemBackendFree(&pVm->sAllocator, pData); -} -/* Export the jx9:// stream */ -static const jx9_io_stream sjx9Stream = { - "jx9", - JX9_IO_STREAM_VERSION, - JX9StreamData_Open, /* xOpen */ - 0, /* xOpenDir */ - JX9StreamData_Close, /* xClose */ - 0, /* xCloseDir */ - JX9StreamData_Read, /* xRead */ - 0, /* xReadDir */ - JX9StreamData_Write, /* xWrite */ - 0, /* xSeek */ - 0, /* xLock */ - 0, /* xRewindDir */ - 0, /* xTell */ - 0, /* xTrunc */ - 0, /* xSeek */ - 0 /* xStat */ -}; -#endif /* JX9_DISABLE_DISK_IO */ -/* - * Return TRUE if we are dealing with the jx9:// stream. - * FALSE otherwise. - */ -static int is_jx9_stream(const jx9_io_stream *pStream) -{ -#ifndef JX9_DISABLE_DISK_IO - return pStream == &sjx9Stream; -#else - SXUNUSED(pStream); /* cc warning */ - return 0; -#endif /* JX9_DISABLE_DISK_IO */ -} - -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -/* - * Export the IO routines defined above and the built-in IO streams - * [i.e: file://, jx9://]. - * Note: - * If the engine is compiled with the JX9_DISABLE_BUILTIN_FUNC directive - * defined then this function is a no-op. - */ -JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm) -{ -#ifndef JX9_DISABLE_BUILTIN_FUNC - /* VFS functions */ - static const jx9_builtin_func aVfsFunc[] = { - {"chdir", jx9Vfs_chdir }, - {"chroot", jx9Vfs_chroot }, - {"getcwd", jx9Vfs_getcwd }, - {"rmdir", jx9Vfs_rmdir }, - {"is_dir", jx9Vfs_is_dir }, - {"mkdir", jx9Vfs_mkdir }, - {"rename", jx9Vfs_rename }, - {"realpath", jx9Vfs_realpath}, - {"sleep", jx9Vfs_sleep }, - {"usleep", jx9Vfs_usleep }, - {"unlink", jx9Vfs_unlink }, - {"delete", jx9Vfs_unlink }, - {"chmod", jx9Vfs_chmod }, - {"chown", jx9Vfs_chown }, - {"chgrp", jx9Vfs_chgrp }, - {"disk_free_space", jx9Vfs_disk_free_space }, - {"disk_total_space", jx9Vfs_disk_total_space}, - {"file_exists", jx9Vfs_file_exists }, - {"filesize", jx9Vfs_file_size }, - {"fileatime", jx9Vfs_file_atime }, - {"filemtime", jx9Vfs_file_mtime }, - {"filectime", jx9Vfs_file_ctime }, - {"is_file", jx9Vfs_is_file }, - {"is_link", jx9Vfs_is_link }, - {"is_readable", jx9Vfs_is_readable }, - {"is_writable", jx9Vfs_is_writable }, - {"is_executable", jx9Vfs_is_executable}, - {"filetype", jx9Vfs_filetype }, - {"stat", jx9Vfs_stat }, - {"lstat", jx9Vfs_lstat }, - {"getenv", jx9Vfs_getenv }, - {"setenv", jx9Vfs_putenv }, - {"putenv", jx9Vfs_putenv }, - {"touch", jx9Vfs_touch }, - {"link", jx9Vfs_link }, - {"symlink", jx9Vfs_symlink }, - {"umask", jx9Vfs_umask }, - {"sys_get_temp_dir", jx9Vfs_sys_get_temp_dir }, - {"get_current_user", jx9Vfs_get_current_user }, - {"getpid", jx9Vfs_getmypid }, - {"getuid", jx9Vfs_getmyuid }, - {"getgid", jx9Vfs_getmygid }, - {"uname", jx9Vfs_uname}, - /* Path processing */ - {"dirname", jx9Builtin_dirname }, - {"basename", jx9Builtin_basename }, - {"pathinfo", jx9Builtin_pathinfo }, - {"strglob", jx9Builtin_strglob }, - {"fnmatch", jx9Builtin_fnmatch }, - /* ZIP processing */ - {"zip_open", jx9Builtin_zip_open }, - {"zip_close", jx9Builtin_zip_close}, - {"zip_read", jx9Builtin_zip_read }, - {"zip_entry_open", jx9Builtin_zip_entry_open }, - {"zip_entry_close", jx9Builtin_zip_entry_close}, - {"zip_entry_name", jx9Builtin_zip_entry_name }, - {"zip_entry_filesize", jx9Builtin_zip_entry_filesize }, - {"zip_entry_compressedsize", jx9Builtin_zip_entry_compressedsize }, - {"zip_entry_read", jx9Builtin_zip_entry_read }, - {"zip_entry_reset_cursor", jx9Builtin_zip_entry_reset_cursor}, - {"zip_entry_compressionmethod", jx9Builtin_zip_entry_compressionmethod} - }; - /* IO stream functions */ - static const jx9_builtin_func aIOFunc[] = { - {"ftruncate", jx9Builtin_ftruncate }, - {"fseek", jx9Builtin_fseek }, - {"ftell", jx9Builtin_ftell }, - {"rewind", jx9Builtin_rewind }, - {"fflush", jx9Builtin_fflush }, - {"feof", jx9Builtin_feof }, - {"fgetc", jx9Builtin_fgetc }, - {"fgets", jx9Builtin_fgets }, - {"fread", jx9Builtin_fread }, - {"fgetcsv", jx9Builtin_fgetcsv}, - {"fgetss", jx9Builtin_fgetss }, - {"readdir", jx9Builtin_readdir}, - {"rewinddir", jx9Builtin_rewinddir }, - {"closedir", jx9Builtin_closedir}, - {"opendir", jx9Builtin_opendir }, - {"readfile", jx9Builtin_readfile}, - {"file_get_contents", jx9Builtin_file_get_contents}, - {"file_put_contents", jx9Builtin_file_put_contents}, - {"file", jx9Builtin_file }, - {"copy", jx9Builtin_copy }, - {"fstat", jx9Builtin_fstat }, - {"fwrite", jx9Builtin_fwrite }, - {"fputs", jx9Builtin_fwrite }, - {"flock", jx9Builtin_flock }, - {"fclose", jx9Builtin_fclose }, - {"fopen", jx9Builtin_fopen }, - {"fpassthru", jx9Builtin_fpassthru }, - {"fputcsv", jx9Builtin_fputcsv }, - {"fprintf", jx9Builtin_fprintf }, -#if !defined(JX9_DISABLE_HASH_FUNC) - {"md5_file", jx9Builtin_md5_file}, - {"sha1_file", jx9Builtin_sha1_file}, -#endif /* JX9_DISABLE_HASH_FUNC */ - {"parse_ini_file", jx9Builtin_parse_ini_file}, - {"vfprintf", jx9Builtin_vfprintf} - }; - const jx9_io_stream *pFileStream = 0; - sxu32 n = 0; - /* Register the functions defined above */ - for( n = 0 ; n < SX_ARRAYSIZE(aVfsFunc) ; ++n ){ - jx9_create_function(&(*pVm), aVfsFunc[n].zName, aVfsFunc[n].xFunc, (void *)pVm->pEngine->pVfs); - } - for( n = 0 ; n < SX_ARRAYSIZE(aIOFunc) ; ++n ){ - jx9_create_function(&(*pVm), aIOFunc[n].zName, aIOFunc[n].xFunc, pVm); - } -#ifndef JX9_DISABLE_DISK_IO - /* Register the file stream if available */ -#ifdef __WINNT__ - pFileStream = &sWinFileStream; -#elif defined(__UNIXES__) - pFileStream = &sUnixFileStream; -#endif - /* Install the jx9:// stream */ - jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, &sjx9Stream); -#endif /* JX9_DISABLE_DISK_IO */ - if( pFileStream ){ - /* Install the file:// stream */ - jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, pFileStream); - } -#else - SXUNUSED(pVm); /* cc warning */ -#endif /* JX9_DISABLE_BUILTIN_FUNC */ - return SXRET_OK; -} -/* - * Export the STDIN handle. - */ -JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm) -{ -#ifndef JX9_DISABLE_BUILTIN_FUNC -#ifndef JX9_DISABLE_DISK_IO - if( pVm->pStdin == 0 ){ - io_private *pIn; - /* Allocate an IO private instance */ - pIn = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private)); - if( pIn == 0 ){ - return 0; - } - InitIOPrivate(pVm, &sjx9Stream, pIn); - /* Initialize the handle */ - pIn->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDIN); - /* Install the STDIN stream */ - pVm->pStdin = pIn; - return pIn; - }else{ - /* NULL or STDIN */ - return pVm->pStdin; - } -#else - return 0; -#endif -#else - SXUNUSED(pVm); /* cc warning */ - return 0; -#endif -} -/* - * Export the STDOUT handle. - */ -JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm) -{ -#ifndef JX9_DISABLE_BUILTIN_FUNC -#ifndef JX9_DISABLE_DISK_IO - if( pVm->pStdout == 0 ){ - io_private *pOut; - /* Allocate an IO private instance */ - pOut = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private)); - if( pOut == 0 ){ - return 0; - } - InitIOPrivate(pVm, &sjx9Stream, pOut); - /* Initialize the handle */ - pOut->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDOUT); - /* Install the STDOUT stream */ - pVm->pStdout = pOut; - return pOut; - }else{ - /* NULL or STDOUT */ - return pVm->pStdout; - } -#else - return 0; -#endif -#else - SXUNUSED(pVm); /* cc warning */ - return 0; -#endif -} -/* - * Export the STDERR handle. - */ -JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm) -{ -#ifndef JX9_DISABLE_BUILTIN_FUNC -#ifndef JX9_DISABLE_DISK_IO - if( pVm->pStderr == 0 ){ - io_private *pErr; - /* Allocate an IO private instance */ - pErr = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private)); - if( pErr == 0 ){ - return 0; - } - InitIOPrivate(pVm, &sjx9Stream, pErr); - /* Initialize the handle */ - pErr->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDERR); - /* Install the STDERR stream */ - pVm->pStderr = pErr; - return pErr; - }else{ - /* NULL or STDERR */ - return pVm->pStderr; - } -#else - return 0; -#endif -#else - SXUNUSED(pVm); /* cc warning */ - return 0; -#endif -} - -/* - * ---------------------------------------------------------- - * File: jx9_vm.c - * MD5: beca4be65a9a49c932c356d7680034c9 - * ---------------------------------------------------------- - */ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://jx9.symisc.net/ - */ - /* $SymiscID: jx9_vm.c v1.0 FreeBSD 2012-12-09 00:19 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* - * The code in this file implements execution method of the JX9 Virtual Machine. - * The JX9 compiler (implemented in 'compiler.c' and 'parse.c') generates a bytecode program - * which is then executed by the virtual machine implemented here to do the work of the JX9 - * statements. - * JX9 bytecode programs are similar in form to assembly language. The program consists - * of a linear sequence of operations .Each operation has an opcode and 3 operands. - * Operands P1 and P2 are integers where the first is signed while the second is unsigned. - * Operand P3 is an arbitrary pointer specific to each instruction. The P2 operand is usually - * the jump destination used by the OP_JMP, OP_JZ, OP_JNZ, ... instructions. - * Opcodes will typically ignore one or more operands. Many opcodes ignore all three operands. - * Computation results are stored on a stack. Each entry on the stack is of type jx9_value. - * JX9 uses the jx9_value object to represent all values that can be stored in a JX9 variable. - * Since JX9 uses dynamic typing for the values it stores. Values stored in jx9_value objects - * can be integers, floating point values, strings, arrays, object instances (object in the JX9 jargon) - * and so on. - * Internally, the JX9 virtual machine manipulates nearly all values as jx9_values structures. - * Each jx9_value may cache multiple representations(string, integer etc.) of the same value. - * An implicit conversion from one type to the other occurs as necessary. - * Most of the code in this file is taken up by the [VmByteCodeExec()] function which does - * the work of interpreting a JX9 bytecode program. But other routines are also provided - * to help in building up a program instruction by instruction. - */ -/* - * Each active virtual machine frame is represented by an instance - * of the following structure. - * VM Frame hold local variables and other stuff related to function call. - */ -struct VmFrame -{ - VmFrame *pParent; /* Parent frame or NULL if global scope */ - void *pUserData; /* Upper layer private data associated with this frame */ - SySet sLocal; /* Local variables container (VmSlot instance) */ - jx9_vm *pVm; /* VM that own this frame */ - SyHash hVar; /* Variable hashtable for fast lookup */ - SySet sArg; /* Function arguments container */ - sxi32 iFlags; /* Frame configuration flags (See below)*/ - sxu32 iExceptionJump; /* Exception jump destination */ -}; -/* - * When a user defined variable is garbage collected, memory object index - * is stored in an instance of the following structure and put in the free object - * table so that it can be reused again without allocating a new memory object. - */ -typedef struct VmSlot VmSlot; -struct VmSlot -{ - sxu32 nIdx; /* Index in pVm->aMemObj[] */ - void *pUserData; /* Upper-layer private data */ -}; -/* - * Each parsed URI is recorded and stored in an instance of the following structure. - * This structure and it's related routines are taken verbatim from the xHT project - * [A modern embeddable HTTP engine implementing all the RFC2616 methods] - * the xHT project is developed internally by Symisc Systems. - */ -typedef struct SyhttpUri SyhttpUri; -struct SyhttpUri -{ - SyString sHost; /* Hostname or IP address */ - SyString sPort; /* Port number */ - SyString sPath; /* Mandatory resource path passed verbatim (Not decoded) */ - SyString sQuery; /* Query part */ - SyString sFragment; /* Fragment part */ - SyString sScheme; /* Scheme */ - SyString sUser; /* Username */ - SyString sPass; /* Password */ - SyString sRaw; /* Raw URI */ -}; -/* - * An instance of the following structure is used to record all MIME headers seen - * during a HTTP interaction. - * This structure and it's related routines are taken verbatim from the xHT project - * [A modern embeddable HTTP engine implementing all the RFC2616 methods] - * the xHT project is developed internally by Symisc Systems. - */ -typedef struct SyhttpHeader SyhttpHeader; -struct SyhttpHeader -{ - SyString sName; /* Header name [i.e:"Content-Type", "Host", "User-Agent"]. NOT NUL TERMINATED */ - SyString sValue; /* Header values [i.e: "text/html"]. NOT NUL TERMINATED */ -}; -/* - * Supported HTTP methods. - */ -#define HTTP_METHOD_GET 1 /* GET */ -#define HTTP_METHOD_HEAD 2 /* HEAD */ -#define HTTP_METHOD_POST 3 /* POST */ -#define HTTP_METHOD_PUT 4 /* PUT */ -#define HTTP_METHOD_OTHR 5 /* Other HTTP methods [i.e: DELETE, TRACE, OPTIONS...]*/ -/* - * Supported HTTP protocol version. - */ -#define HTTP_PROTO_10 1 /* HTTP/1.0 */ -#define HTTP_PROTO_11 2 /* HTTP/1.1 */ -/* - * Register a constant and it's associated expansion callback so that - * it can be expanded from the target JX9 program. - * The constant expansion mechanism under JX9 is extremely powerful yet - * simple and work as follows: - * Each registered constant have a C procedure associated with it. - * This procedure known as the constant expansion callback is responsible - * of expanding the invoked constant to the desired value, for example: - * The C procedure associated with the "__PI__" constant expands to 3.14 (the value of PI). - * The "__OS__" constant procedure expands to the name of the host Operating Systems - * (Windows, Linux, ...) and so on. - * Please refer to the official documentation for additional information. - */ -JX9_PRIVATE sxi32 jx9VmRegisterConstant( - jx9_vm *pVm, /* Target VM */ - const SyString *pName, /* Constant name */ - ProcConstant xExpand, /* Constant expansion callback */ - void *pUserData /* Last argument to xExpand() */ - ) -{ - jx9_constant *pCons; - SyHashEntry *pEntry; - char *zDupName; - sxi32 rc; - pEntry = SyHashGet(&pVm->hConstant, (const void *)pName->zString, pName->nByte); - if( pEntry ){ - /* Overwrite the old definition and return immediately */ - pCons = (jx9_constant *)pEntry->pUserData; - pCons->xExpand = xExpand; - pCons->pUserData = pUserData; - return SXRET_OK; - } - /* Allocate a new constant instance */ - pCons = (jx9_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_constant)); - if( pCons == 0 ){ - return 0; - } - /* Duplicate constant name */ - zDupName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte); - if( zDupName == 0 ){ - SyMemBackendPoolFree(&pVm->sAllocator, pCons); - return 0; - } - /* Install the constant */ - SyStringInitFromBuf(&pCons->sName, zDupName, pName->nByte); - pCons->xExpand = xExpand; - pCons->pUserData = pUserData; - rc = SyHashInsert(&pVm->hConstant, (const void *)zDupName, SyStringLength(&pCons->sName), pCons); - if( rc != SXRET_OK ){ - SyMemBackendFree(&pVm->sAllocator, zDupName); - SyMemBackendPoolFree(&pVm->sAllocator, pCons); - return rc; - } - /* All done, constant can be invoked from JX9 code */ - return SXRET_OK; -} -/* - * Allocate a new foreign function instance. - * This function return SXRET_OK on success. Any other - * return value indicates failure. - * Please refer to the official documentation for an introduction to - * the foreign function mechanism. - */ -static sxi32 jx9NewForeignFunction( - jx9_vm *pVm, /* Target VM */ - const SyString *pName, /* Foreign function name */ - ProcHostFunction xFunc, /* Foreign function implementation */ - void *pUserData, /* Foreign function private data */ - jx9_user_func **ppOut /* OUT: VM image of the foreign function */ - ) -{ - jx9_user_func *pFunc; - char *zDup; - /* Allocate a new user function */ - pFunc = (jx9_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_user_func)); - if( pFunc == 0 ){ - return SXERR_MEM; - } - /* Duplicate function name */ - zDup = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte); - if( zDup == 0 ){ - SyMemBackendPoolFree(&pVm->sAllocator, pFunc); - return SXERR_MEM; - } - /* Zero the structure */ - SyZero(pFunc, sizeof(jx9_user_func)); - /* Initialize structure fields */ - SyStringInitFromBuf(&pFunc->sName, zDup, pName->nByte); - pFunc->pVm = pVm; - pFunc->xFunc = xFunc; - pFunc->pUserData = pUserData; - SySetInit(&pFunc->aAux, &pVm->sAllocator, sizeof(jx9_aux_data)); - /* Write a pointer to the new function */ - *ppOut = pFunc; - return SXRET_OK; -} -/* - * Install a foreign function and it's associated callback so that - * it can be invoked from the target JX9 code. - * This function return SXRET_OK on successful registration. Any other - * return value indicates failure. - * Please refer to the official documentation for an introduction to - * the foreign function mechanism. - */ -JX9_PRIVATE sxi32 jx9VmInstallForeignFunction( - jx9_vm *pVm, /* Target VM */ - const SyString *pName, /* Foreign function name */ - ProcHostFunction xFunc, /* Foreign function implementation */ - void *pUserData /* Foreign function private data */ - ) -{ - jx9_user_func *pFunc; - SyHashEntry *pEntry; - sxi32 rc; - /* Overwrite any previously registered function with the same name */ - pEntry = SyHashGet(&pVm->hHostFunction, pName->zString, pName->nByte); - if( pEntry ){ - pFunc = (jx9_user_func *)pEntry->pUserData; - pFunc->pUserData = pUserData; - pFunc->xFunc = xFunc; - SySetReset(&pFunc->aAux); - return SXRET_OK; - } - /* Create a new user function */ - rc = jx9NewForeignFunction(&(*pVm), &(*pName), xFunc, pUserData, &pFunc); - if( rc != SXRET_OK ){ - return rc; - } - /* Install the function in the corresponding hashtable */ - rc = SyHashInsert(&pVm->hHostFunction, SyStringData(&pFunc->sName), pName->nByte, pFunc); - if( rc != SXRET_OK ){ - SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName)); - SyMemBackendPoolFree(&pVm->sAllocator, pFunc); - return rc; - } - /* User function successfully installed */ - return SXRET_OK; -} -/* - * Initialize a VM function. - */ -JX9_PRIVATE sxi32 jx9VmInitFuncState( - jx9_vm *pVm, /* Target VM */ - jx9_vm_func *pFunc, /* Target Fucntion */ - const char *zName, /* Function name */ - sxu32 nByte, /* zName length */ - sxi32 iFlags, /* Configuration flags */ - void *pUserData /* Function private data */ - ) -{ - /* Zero the structure */ - SyZero(pFunc, sizeof(jx9_vm_func)); - /* Initialize structure fields */ - /* Arguments container */ - SySetInit(&pFunc->aArgs, &pVm->sAllocator, sizeof(jx9_vm_func_arg)); - /* Static variable container */ - SySetInit(&pFunc->aStatic, &pVm->sAllocator, sizeof(jx9_vm_func_static_var)); - /* Bytecode container */ - SySetInit(&pFunc->aByteCode, &pVm->sAllocator, sizeof(VmInstr)); - /* Preallocate some instruction slots */ - SySetAlloc(&pFunc->aByteCode, 0x10); - pFunc->iFlags = iFlags; - pFunc->pUserData = pUserData; - SyStringInitFromBuf(&pFunc->sName, zName, nByte); - return SXRET_OK; -} -/* - * Install a user defined function in the corresponding VM container. - */ -JX9_PRIVATE sxi32 jx9VmInstallUserFunction( - jx9_vm *pVm, /* Target VM */ - jx9_vm_func *pFunc, /* Target function */ - SyString *pName /* Function name */ - ) -{ - SyHashEntry *pEntry; - sxi32 rc; - if( pName == 0 ){ - /* Use the built-in name */ - pName = &pFunc->sName; - } - /* Check for duplicates (functions with the same name) first */ - pEntry = SyHashGet(&pVm->hFunction, pName->zString, pName->nByte); - if( pEntry ){ - jx9_vm_func *pLink = (jx9_vm_func *)pEntry->pUserData; - if( pLink != pFunc ){ - /* Link */ - pFunc->pNextName = pLink; - pEntry->pUserData = pFunc; - } - return SXRET_OK; - } - /* First time seen */ - pFunc->pNextName = 0; - rc = SyHashInsert(&pVm->hFunction, pName->zString, pName->nByte, pFunc); - return rc; -} -/* - * Instruction builder interface. - */ -JX9_PRIVATE sxi32 jx9VmEmitInstr( - jx9_vm *pVm, /* Target VM */ - sxi32 iOp, /* Operation to perform */ - sxi32 iP1, /* First operand */ - sxu32 iP2, /* Second operand */ - void *p3, /* Third operand */ - sxu32 *pIndex /* Instruction index. NULL otherwise */ - ) -{ - VmInstr sInstr; - sxi32 rc; - /* Fill the VM instruction */ - sInstr.iOp = (sxu8)iOp; - sInstr.iP1 = iP1; - sInstr.iP2 = iP2; - sInstr.p3 = p3; - if( pIndex ){ - /* Instruction index in the bytecode array */ - *pIndex = SySetUsed(pVm->pByteContainer); - } - /* Finally, record the instruction */ - rc = SySetPut(pVm->pByteContainer, (const void *)&sInstr); - if( rc != SXRET_OK ){ - jx9GenCompileError(&pVm->sCodeGen, E_ERROR, 1, "Fatal, Cannot emit instruction due to a memory failure"); - /* Fall throw */ - } - return rc; -} -/* - * Swap the current bytecode container with the given one. - */ -JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer) -{ - if( pContainer == 0 ){ - /* Point to the default container */ - pVm->pByteContainer = &pVm->aByteCode; - }else{ - /* Change container */ - pVm->pByteContainer = &(*pContainer); - } - return SXRET_OK; -} -/* - * Return the current bytecode container. - */ -JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm) -{ - return pVm->pByteContainer; -} -/* - * Extract the VM instruction rooted at nIndex. - */ -JX9_PRIVATE VmInstr * jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex) -{ - VmInstr *pInstr; - pInstr = (VmInstr *)SySetAt(pVm->pByteContainer, nIndex); - return pInstr; -} -/* - * Return the total number of VM instructions recorded so far. - */ -JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm) -{ - return SySetUsed(pVm->pByteContainer); -} -/* - * Pop the last VM instruction. - */ -JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm) -{ - return (VmInstr *)SySetPop(pVm->pByteContainer); -} -/* - * Peek the last VM instruction. - */ -JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm) -{ - return (VmInstr *)SySetPeek(pVm->pByteContainer); -} -/* - * Allocate a new virtual machine frame. - */ -static VmFrame * VmNewFrame( - jx9_vm *pVm, /* Target VM */ - void *pUserData /* Upper-layer private data */ - ) -{ - VmFrame *pFrame; - /* Allocate a new vm frame */ - pFrame = (VmFrame *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(VmFrame)); - if( pFrame == 0 ){ - return 0; - } - /* Zero the structure */ - SyZero(pFrame, sizeof(VmFrame)); - /* Initialize frame fields */ - pFrame->pUserData = pUserData; - pFrame->pVm = pVm; - SyHashInit(&pFrame->hVar, &pVm->sAllocator, 0, 0); - SySetInit(&pFrame->sArg, &pVm->sAllocator, sizeof(VmSlot)); - SySetInit(&pFrame->sLocal, &pVm->sAllocator, sizeof(VmSlot)); - return pFrame; -} -/* - * Enter a VM frame. - */ -static sxi32 VmEnterFrame( - jx9_vm *pVm, /* Target VM */ - void *pUserData, /* Upper-layer private data */ - VmFrame **ppFrame /* OUT: Top most active frame */ - ) -{ - VmFrame *pFrame; - /* Allocate a new frame */ - pFrame = VmNewFrame(&(*pVm), pUserData); - if( pFrame == 0 ){ - return SXERR_MEM; - } - /* Link to the list of active VM frame */ - pFrame->pParent = pVm->pFrame; - pVm->pFrame = pFrame; - if( ppFrame ){ - /* Write a pointer to the new VM frame */ - *ppFrame = pFrame; - } - return SXRET_OK; -} -/* - * Link a foreign variable with the TOP most active frame. - * Refer to the JX9_OP_UPLINK instruction implementation for more - * information. - */ -static sxi32 VmFrameLink(jx9_vm *pVm,SyString *pName) -{ - VmFrame *pTarget, *pFrame; - SyHashEntry *pEntry = 0; - sxi32 rc; - /* Point to the upper frame */ - pFrame = pVm->pFrame; - pTarget = pFrame; - pFrame = pTarget->pParent; - while( pFrame ){ - /* Query the current frame */ - pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte); - if( pEntry ){ - /* Variable found */ - break; - } - /* Point to the upper frame */ - pFrame = pFrame->pParent; - } - if( pEntry == 0 ){ - /* Inexistant variable */ - return SXERR_NOTFOUND; - } - /* Link to the current frame */ - rc = SyHashInsert(&pTarget->hVar, pEntry->pKey, pEntry->nKeyLen, pEntry->pUserData); - return rc; -} -/* - * Leave the top-most active frame. - */ -static void VmLeaveFrame(jx9_vm *pVm) -{ - VmFrame *pFrame = pVm->pFrame; - if( pFrame ){ - /* Unlink from the list of active VM frame */ - pVm->pFrame = pFrame->pParent; - if( pFrame->pParent ){ - VmSlot *aSlot; - sxu32 n; - /* Restore local variable to the free pool so that they can be reused again */ - aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal); - for(n = 0 ; n < SySetUsed(&pFrame->sLocal) ; ++n ){ - /* Unset the local variable */ - jx9VmUnsetMemObj(&(*pVm), aSlot[n].nIdx); - } - } - /* Release internal containers */ - SyHashRelease(&pFrame->hVar); - SySetRelease(&pFrame->sArg); - SySetRelease(&pFrame->sLocal); - /* Release the whole structure */ - SyMemBackendPoolFree(&pVm->sAllocator, pFrame); - } -} -/* - * Compare two functions signature and return the comparison result. - */ -static int VmOverloadCompare(SyString *pFirst, SyString *pSecond) -{ - const char *zSend = &pSecond->zString[pSecond->nByte]; - const char *zFend = &pFirst->zString[pFirst->nByte]; - const char *zSin = pSecond->zString; - const char *zFin = pFirst->zString; - const char *zPtr = zFin; - for(;;){ - if( zFin >= zFend || zSin >= zSend ){ - break; - } - if( zFin[0] != zSin[0] ){ - /* mismatch */ - break; - } - zFin++; - zSin++; - } - return (int)(zFin-zPtr); -} -/* - * Select the appropriate VM function for the current call context. - * This is the implementation of the powerful 'function overloading' feature - * introduced by the version 2 of the JX9 engine. - * Refer to the official documentation for more information. - */ -static jx9_vm_func * VmOverload( - jx9_vm *pVm, /* Target VM */ - jx9_vm_func *pList, /* Linked list of candidates for overloading */ - jx9_value *aArg, /* Array of passed arguments */ - int nArg /* Total number of passed arguments */ - ) -{ - int iTarget, i, j, iCur, iMax; - jx9_vm_func *apSet[10]; /* Maximum number of candidates */ - jx9_vm_func *pLink; - SyString sArgSig; - SyBlob sSig; - - pLink = pList; - i = 0; - /* Put functions expecting the same number of passed arguments */ - while( i < (int)SX_ARRAYSIZE(apSet) ){ - if( pLink == 0 ){ - break; - } - if( (int)SySetUsed(&pLink->aArgs) == nArg ){ - /* Candidate for overloading */ - apSet[i++] = pLink; - } - /* Point to the next entry */ - pLink = pLink->pNextName; - } - if( i < 1 ){ - /* No candidates, return the head of the list */ - return pList; - } - if( nArg < 1 || i < 2 ){ - /* Return the only candidate */ - return apSet[0]; - } - /* Calculate function signature */ - SyBlobInit(&sSig, &pVm->sAllocator); - for( j = 0 ; j < nArg ; j++ ){ - int c = 'n'; /* null */ - if( aArg[j].iFlags & MEMOBJ_HASHMAP ){ - /* Hashmap */ - c = 'h'; - }else if( aArg[j].iFlags & MEMOBJ_BOOL ){ - /* bool */ - c = 'b'; - }else if( aArg[j].iFlags & MEMOBJ_INT ){ - /* int */ - c = 'i'; - }else if( aArg[j].iFlags & MEMOBJ_STRING ){ - /* String */ - c = 's'; - }else if( aArg[j].iFlags & MEMOBJ_REAL ){ - /* Float */ - c = 'f'; - } - if( c > 0 ){ - SyBlobAppend(&sSig, (const void *)&c, sizeof(char)); - } - } - SyStringInitFromBuf(&sArgSig, SyBlobData(&sSig), SyBlobLength(&sSig)); - iTarget = 0; - iMax = -1; - /* Select the appropriate function */ - for( j = 0 ; j < i ; j++ ){ - /* Compare the two signatures */ - iCur = VmOverloadCompare(&sArgSig, &apSet[j]->sSignature); - if( iCur > iMax ){ - iMax = iCur; - iTarget = j; - } - } - SyBlobRelease(&sSig); - /* Appropriate function for the current call context */ - return apSet[iTarget]; -} -/* - * Dummy read-only buffer used for slot reservation. - */ -static const char zDummy[sizeof(jx9_value)] = { 0 }; /* Must be >= sizeof(jx9_value) */ -/* - * Reserve a constant memory object. - * Return a pointer to the raw jx9_value on success. NULL on failure. - */ -JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex) -{ - jx9_value *pObj; - sxi32 rc; - if( pIndex ){ - /* Object index in the object table */ - *pIndex = SySetUsed(&pVm->aLitObj); - } - /* Reserve a slot for the new object */ - rc = SySetPut(&pVm->aLitObj, (const void *)zDummy); - if( rc != SXRET_OK ){ - /* If the supplied memory subsystem is so sick that we are unable to allocate - * a tiny chunk of memory, there is no much we can do here. - */ - return 0; - } - pObj = (jx9_value *)SySetPeek(&pVm->aLitObj); - return pObj; -} -/* - * Reserve a memory object. - * Return a pointer to the raw jx9_value on success. NULL on failure. - */ -static jx9_value * VmReserveMemObj(jx9_vm *pVm, sxu32 *pIndex) -{ - jx9_value *pObj; - sxi32 rc; - if( pIndex ){ - /* Object index in the object table */ - *pIndex = SySetUsed(&pVm->aMemObj); - } - /* Reserve a slot for the new object */ - rc = SySetPut(&pVm->aMemObj, (const void *)zDummy); - if( rc != SXRET_OK ){ - /* If the supplied memory subsystem is so sick that we are unable to allocate - * a tiny chunk of memory, there is no much we can do here. - */ - return 0; - } - pObj = (jx9_value *)SySetPeek(&pVm->aMemObj); - return pObj; -} -/* Forward declaration */ -static sxi32 VmEvalChunk(jx9_vm *pVm, jx9_context *pCtx, SyString *pChunk, int iFlags, int bTrueReturn); -/* - * Built-in functions that cannot be implemented directly as foreign functions. - */ -#define JX9_BUILTIN_LIB \ - "function scandir(string $directory, int $sort_order = SCANDIR_SORT_ASCENDING)"\ - "{"\ - " if( func_num_args() < 1 ){ return FALSE; }"\ - " $aDir = [];"\ - " $pHandle = opendir($directory);"\ - " if( $pHandle == FALSE ){ return FALSE; }"\ - " while(FALSE !== ($pEntry = readdir($pHandle)) ){"\ - " $aDir[] = $pEntry;"\ - " }"\ - " closedir($pHandle);"\ - " if( $sort_order == SCANDIR_SORT_DESCENDING ){"\ - " rsort($aDir);"\ - " }else if( $sort_order == SCANDIR_SORT_ASCENDING ){"\ - " sort($aDir);"\ - " }"\ - " return $aDir;"\ - "}"\ - "function glob(string $pattern, int $iFlags = 0){"\ - "/* Open the target directory */"\ - "$zDir = dirname($pattern);"\ - "if(!is_string($zDir) ){ $zDir = './'; }"\ - "$pHandle = opendir($zDir);"\ - "if( $pHandle == FALSE ){"\ - " /* IO error while opening the current directory, return FALSE */"\ - " return FALSE;"\ - "}"\ - "$pattern = basename($pattern);"\ - "$pArray = []; /* Empty array */"\ - "/* Loop throw available entries */"\ - "while( FALSE !== ($pEntry = readdir($pHandle)) ){"\ - " /* Use the built-in strglob function which is a Symisc eXtension for wildcard comparison*/"\ - " $rc = strglob($pattern, $pEntry);"\ - " if( $rc ){"\ - " if( is_dir($pEntry) ){"\ - " if( $iFlags & GLOB_MARK ){"\ - " /* Adds a slash to each directory returned */"\ - " $pEntry .= DIRECTORY_SEPARATOR;"\ - " }"\ - " }else if( $iFlags & GLOB_ONLYDIR ){"\ - " /* Not a directory, ignore */"\ - " continue;"\ - " }"\ - " /* Add the entry */"\ - " $pArray[] = $pEntry;"\ - " }"\ - " }"\ - "/* Close the handle */"\ - "closedir($pHandle);"\ - "if( ($iFlags & GLOB_NOSORT) == 0 ){"\ - " /* Sort the array */"\ - " sort($pArray);"\ - "}"\ - "if( ($iFlags & GLOB_NOCHECK) && sizeof($pArray) < 1 ){"\ - " /* Return the search pattern if no files matching were found */"\ - " $pArray[] = $pattern;"\ - "}"\ - "/* Return the created array */"\ - "return $pArray;"\ - "}"\ - "/* Creates a temporary file */"\ - "function tmpfile(){"\ - " /* Extract the temp directory */"\ - " $zTempDir = sys_get_temp_dir();"\ - " if( strlen($zTempDir) < 1 ){"\ - " /* Use the current dir */"\ - " $zTempDir = '.';"\ - " }"\ - " /* Create the file */"\ - " $pHandle = fopen($zTempDir.DIRECTORY_SEPARATOR.'JX9'.rand_str(12), 'w+');"\ - " return $pHandle;"\ - "}"\ - "/* Creates a temporary filename */"\ - "function tempnam(string $zDir = sys_get_temp_dir() /* Symisc eXtension */, string $zPrefix = 'JX9')"\ - "{"\ - " return $zDir.DIRECTORY_SEPARATOR.$zPrefix.rand_str(12);"\ - "}"\ - "function max(){"\ - " $pArgs = func_get_args();"\ - " if( sizeof($pArgs) < 1 ){"\ - " return null;"\ - " }"\ - " if( sizeof($pArgs) < 2 ){"\ - " $pArg = $pArgs[0];"\ - " if( !is_array($pArg) ){"\ - " return $pArg; "\ - " }"\ - " if( sizeof($pArg) < 1 ){"\ - " return null;"\ - " }"\ - " $pArg = array_copy($pArgs[0]);"\ - " reset($pArg);"\ - " $max = current($pArg);"\ - " while( FALSE !== ($val = next($pArg)) ){"\ - " if( $val > $max ){"\ - " $max = $val;"\ - " }"\ - " }"\ - " return $max;"\ - " }"\ - " $max = $pArgs[0];"\ - " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\ - " $val = $pArgs[$i];"\ - "if( $val > $max ){"\ - " $max = $val;"\ - "}"\ - " }"\ - " return $max;"\ - "}"\ - "function min(){"\ - " $pArgs = func_get_args();"\ - " if( sizeof($pArgs) < 1 ){"\ - " return null;"\ - " }"\ - " if( sizeof($pArgs) < 2 ){"\ - " $pArg = $pArgs[0];"\ - " if( !is_array($pArg) ){"\ - " return $pArg; "\ - " }"\ - " if( sizeof($pArg) < 1 ){"\ - " return null;"\ - " }"\ - " $pArg = array_copy($pArgs[0]);"\ - " reset($pArg);"\ - " $min = current($pArg);"\ - " while( FALSE !== ($val = next($pArg)) ){"\ - " if( $val < $min ){"\ - " $min = $val;"\ - " }"\ - " }"\ - " return $min;"\ - " }"\ - " $min = $pArgs[0];"\ - " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\ - " $val = $pArgs[$i];"\ - "if( $val < $min ){"\ - " $min = $val;"\ - " }"\ - " }"\ - " return $min;"\ - "}" -/* - * Initialize a freshly allocated JX9 Virtual Machine so that we can - * start compiling the target JX9 program. - */ -JX9_PRIVATE sxi32 jx9VmInit( - jx9_vm *pVm, /* Initialize this */ - jx9 *pEngine /* Master engine */ - ) -{ - SyString sBuiltin; - jx9_value *pObj; - sxi32 rc; - /* Zero the structure */ - SyZero(pVm, sizeof(jx9_vm)); - /* Initialize VM fields */ - pVm->pEngine = &(*pEngine); - SyMemBackendInitFromParent(&pVm->sAllocator, &pEngine->sAllocator); - /* Instructions containers */ - SySetInit(&pVm->aByteCode, &pVm->sAllocator, sizeof(VmInstr)); - SySetAlloc(&pVm->aByteCode, 0xFF); - pVm->pByteContainer = &pVm->aByteCode; - /* Object containers */ - SySetInit(&pVm->aMemObj, &pVm->sAllocator, sizeof(jx9_value)); - SySetAlloc(&pVm->aMemObj, 0xFF); - /* Virtual machine internal containers */ - SyBlobInit(&pVm->sConsumer, &pVm->sAllocator); - SyBlobInit(&pVm->sWorker, &pVm->sAllocator); - SyBlobInit(&pVm->sArgv, &pVm->sAllocator); - SySetInit(&pVm->aLitObj, &pVm->sAllocator, sizeof(jx9_value)); - SySetAlloc(&pVm->aLitObj, 0xFF); - SyHashInit(&pVm->hHostFunction, &pVm->sAllocator, 0, 0); - SyHashInit(&pVm->hFunction, &pVm->sAllocator, 0, 0); - SyHashInit(&pVm->hConstant, &pVm->sAllocator, 0, 0); - SyHashInit(&pVm->hSuper, &pVm->sAllocator, 0, 0); - SySetInit(&pVm->aFreeObj, &pVm->sAllocator, sizeof(VmSlot)); - /* Configuration containers */ - SySetInit(&pVm->aFiles, &pVm->sAllocator, sizeof(SyString)); - SySetInit(&pVm->aPaths, &pVm->sAllocator, sizeof(SyString)); - SySetInit(&pVm->aIncluded, &pVm->sAllocator, sizeof(SyString)); - SySetInit(&pVm->aIOstream, &pVm->sAllocator, sizeof(jx9_io_stream *)); - /* Error callbacks containers */ - jx9MemObjInit(&(*pVm), &pVm->sAssertCallback); - /* Set a default recursion limit */ -#if defined(__WINNT__) || defined(__UNIXES__) - pVm->nMaxDepth = 32; -#else - pVm->nMaxDepth = 16; -#endif - /* Default assertion flags */ - pVm->iAssertFlags = JX9_ASSERT_WARNING; /* Issue a warning for each failed assertion */ - /* PRNG context */ - SyRandomnessInit(&pVm->sPrng, 0, 0); - /* Install the null constant */ - pObj = jx9VmReserveConstObj(&(*pVm), 0); - if( pObj == 0 ){ - rc = SXERR_MEM; - goto Err; - } - jx9MemObjInit(pVm, pObj); - /* Install the boolean TRUE constant */ - pObj = jx9VmReserveConstObj(&(*pVm), 0); - if( pObj == 0 ){ - rc = SXERR_MEM; - goto Err; - } - jx9MemObjInitFromBool(pVm, pObj, 1); - /* Install the boolean FALSE constant */ - pObj = jx9VmReserveConstObj(&(*pVm), 0); - if( pObj == 0 ){ - rc = SXERR_MEM; - goto Err; - } - jx9MemObjInitFromBool(pVm, pObj, 0); - /* Create the global frame */ - rc = VmEnterFrame(&(*pVm), 0, 0); - if( rc != SXRET_OK ){ - goto Err; - } - /* Initialize the code generator */ - rc = jx9InitCodeGenerator(pVm, pEngine->xConf.xErr, pEngine->xConf.pErrData); - if( rc != SXRET_OK ){ - goto Err; - } - /* VM correctly initialized, set the magic number */ - pVm->nMagic = JX9_VM_INIT; - SyStringInitFromBuf(&sBuiltin,JX9_BUILTIN_LIB, sizeof(JX9_BUILTIN_LIB)-1); - /* Compile the built-in library */ - VmEvalChunk(&(*pVm), 0, &sBuiltin, 0, FALSE); - /* Reset the code generator */ - jx9ResetCodeGenerator(&(*pVm), pEngine->xConf.xErr, pEngine->xConf.pErrData); - return SXRET_OK; -Err: - SyMemBackendRelease(&pVm->sAllocator); - return rc; -} -/* - * Default VM output consumer callback.That is, all VM output is redirected to this - * routine which store the output in an internal blob. - * The output can be extracted later after program execution [jx9_vm_exec()] via - * the [jx9_vm_config()] interface with a configuration verb set to - * jx9VM_CONFIG_EXTRACT_OUTPUT. - * Refer to the official docurmentation for additional information. - * Note that for performance reason it's preferable to install a VM output - * consumer callback via (jx9VM_CONFIG_OUTPUT) rather than waiting for the VM - * to finish executing and extracting the output. - */ -JX9_PRIVATE sxi32 jx9VmBlobConsumer( - const void *pOut, /* VM Generated output*/ - unsigned int nLen, /* Generated output length */ - void *pUserData /* User private data */ - ) -{ - sxi32 rc; - /* Store the output in an internal BLOB */ - rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen); - return rc; -} -#define VM_STACK_GUARD 16 -/* - * Allocate a new operand stack so that we can start executing - * our compiled JX9 program. - * Return a pointer to the operand stack (array of jx9_values) - * on success. NULL (Fatal error) on failure. - */ -static jx9_value * VmNewOperandStack( - jx9_vm *pVm, /* Target VM */ - sxu32 nInstr /* Total numer of generated bytecode instructions */ - ) -{ - jx9_value *pStack; - /* No instruction ever pushes more than a single element onto the - ** stack and the stack never grows on successive executions of the - ** same loop. So the total number of instructions is an upper bound - ** on the maximum stack depth required. - ** - ** Allocation all the stack space we will ever need. - */ - nInstr += VM_STACK_GUARD; - pStack = (jx9_value *)SyMemBackendAlloc(&pVm->sAllocator, nInstr * sizeof(jx9_value)); - if( pStack == 0 ){ - return 0; - } - /* Initialize the operand stack */ - while( nInstr > 0 ){ - jx9MemObjInit(&(*pVm), &pStack[nInstr - 1]); - --nInstr; - } - /* Ready for bytecode execution */ - return pStack; -} -/* Forward declaration */ -static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm); -/* - * Prepare the Virtual Machine for bytecode execution. - * This routine gets called by the JX9 engine after - * successful compilation of the target JX9 program. - */ -JX9_PRIVATE sxi32 jx9VmMakeReady( - jx9_vm *pVm /* Target VM */ - ) -{ - sxi32 rc; - if( pVm->nMagic != JX9_VM_INIT ){ - /* Initialize your VM first */ - return SXERR_CORRUPT; - } - /* Mark the VM ready for bytecode execution */ - pVm->nMagic = JX9_VM_RUN; - /* Release the code generator now we have compiled our program */ - jx9ResetCodeGenerator(pVm, 0, 0); - /* Emit the DONE instruction */ - rc = jx9VmEmitInstr(&(*pVm), JX9_OP_DONE, 0, 0, 0, 0); - if( rc != SXRET_OK ){ - return SXERR_MEM; - } - /* Script return value */ - jx9MemObjInit(&(*pVm), &pVm->sExec); /* Assume a NULL return value */ - /* Allocate a new operand stack */ - pVm->aOps = VmNewOperandStack(&(*pVm), SySetUsed(pVm->pByteContainer)); - if( pVm->aOps == 0 ){ - return SXERR_MEM; - } - /* Set the default VM output consumer callback and it's - * private data. */ - pVm->sVmConsumer.xConsumer = jx9VmBlobConsumer; - pVm->sVmConsumer.pUserData = &pVm->sConsumer; - /* Register special functions first [i.e: print, func_get_args(), die, etc.] */ - rc = VmRegisterSpecialFunction(&(*pVm)); - if( rc != SXRET_OK ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return rc; - } - /* Create superglobals [i.e: $GLOBALS, $_GET, $_POST...] */ - rc = jx9HashmapLoadBuiltin(&(*pVm)); - if( rc != SXRET_OK ){ - /* Don't worry about freeing memory, everything will be released shortly */ - return rc; - } - /* Register built-in constants [i.e: JX9_EOL, JX9_OS...] */ - jx9RegisterBuiltInConstant(&(*pVm)); - /* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */ - jx9RegisterBuiltInFunction(&(*pVm)); - /* VM is ready for bytecode execution */ - return SXRET_OK; -} -/* - * Reset a Virtual Machine to it's initial state. - */ -JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm) -{ - if( pVm->nMagic != JX9_VM_RUN && pVm->nMagic != JX9_VM_EXEC ){ - return SXERR_CORRUPT; - } - /* TICKET 1433-003: As of this version, the VM is automatically reset */ - SyBlobReset(&pVm->sConsumer); - jx9MemObjRelease(&pVm->sExec); - /* Set the ready flag */ - pVm->nMagic = JX9_VM_RUN; - return SXRET_OK; -} -/* - * Release a Virtual Machine. - * Every virtual machine must be destroyed in order to avoid memory leaks. - */ -JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm) -{ - /* Set the stale magic number */ - pVm->nMagic = JX9_VM_STALE; - /* Release the private memory subsystem */ - SyMemBackendRelease(&pVm->sAllocator); - return SXRET_OK; -} -/* - * Initialize a foreign function call context. - * The context in which a foreign function executes is stored in a jx9_context object. - * A pointer to a jx9_context object is always first parameter to application-defined foreign - * functions. - * The application-defined foreign function implementation will pass this pointer through into - * calls to dozens of interfaces, these includes jx9_result_int(), jx9_result_string(), jx9_result_value(), - * jx9_context_new_scalar(), jx9_context_alloc_chunk(), jx9_context_output(), jx9_context_throw_error() - * and many more. Refer to the C/C++ Interfaces documentation for additional information. - */ -static sxi32 VmInitCallContext( - jx9_context *pOut, /* Call Context */ - jx9_vm *pVm, /* Target VM */ - jx9_user_func *pFunc, /* Foreign function to execute shortly */ - jx9_value *pRet, /* Store return value here*/ - sxi32 iFlags /* Control flags */ - ) -{ - pOut->pFunc = pFunc; - pOut->pVm = pVm; - SySetInit(&pOut->sVar, &pVm->sAllocator, sizeof(jx9_value *)); - SySetInit(&pOut->sChunk, &pVm->sAllocator, sizeof(jx9_aux_data)); - /* Assume a null return value */ - MemObjSetType(pRet, MEMOBJ_NULL); - pOut->pRet = pRet; - pOut->iFlags = iFlags; - return SXRET_OK; -} -/* - * Release a foreign function call context and cleanup the mess - * left behind. - */ -static void VmReleaseCallContext(jx9_context *pCtx) -{ - sxu32 n; - if( SySetUsed(&pCtx->sVar) > 0 ){ - jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar); - for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){ - if( apObj[n] == 0 ){ - /* Already released */ - continue; - } - jx9MemObjRelease(apObj[n]); - SyMemBackendPoolFree(&pCtx->pVm->sAllocator, apObj[n]); - } - SySetRelease(&pCtx->sVar); - } - if( SySetUsed(&pCtx->sChunk) > 0 ){ - jx9_aux_data *aAux; - void *pChunk; - /* Automatic release of dynamically allocated chunk - * using [jx9_context_alloc_chunk()]. - */ - aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk); - for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){ - pChunk = aAux[n].pAuxData; - /* Release the chunk */ - if( pChunk ){ - SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk); - } - } - SySetRelease(&pCtx->sChunk); - } -} -/* - * Release a jx9_value allocated from the body of a foreign function. - * Refer to [jx9_context_release_value()] for additional information. - */ -JX9_PRIVATE void jx9VmReleaseContextValue( - jx9_context *pCtx, /* Call context */ - jx9_value *pValue /* Release this value */ - ) -{ - if( pValue == 0 ){ - /* NULL value is a harmless operation */ - return; - } - if( SySetUsed(&pCtx->sVar) > 0 ){ - jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar); - sxu32 n; - for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){ - if( apObj[n] == pValue ){ - jx9MemObjRelease(pValue); - SyMemBackendPoolFree(&pCtx->pVm->sAllocator, pValue); - /* Mark as released */ - apObj[n] = 0; - break; - } - } - } -} -/* - * Pop and release as many memory object from the operand stack. - */ -static void VmPopOperand( - jx9_value **ppTos, /* Operand stack */ - sxi32 nPop /* Total number of memory objects to pop */ - ) -{ - jx9_value *pTos = *ppTos; - while( nPop > 0 ){ - jx9MemObjRelease(pTos); - pTos--; - nPop--; - } - /* Top of the stack */ - *ppTos = pTos; -} -/* - * Reserve a memory object. - * Return a pointer to the raw jx9_value on success. NULL on failure. - */ -JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIdx) -{ - jx9_value *pObj = 0; - VmSlot *pSlot; - sxu32 nIdx; - /* Check for a free slot */ - nIdx = SXU32_HIGH; /* cc warning */ - pSlot = (VmSlot *)SySetPop(&pVm->aFreeObj); - if( pSlot ){ - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx); - nIdx = pSlot->nIdx; - } - if( pObj == 0 ){ - /* Reserve a new memory object */ - pObj = VmReserveMemObj(&(*pVm), &nIdx); - if( pObj == 0 ){ - return 0; - } - } - /* Set a null default value */ - jx9MemObjInit(&(*pVm), pObj); - if( pIdx ){ - *pIdx = nIdx; - } - pObj->nIdx = nIdx; - return pObj; -} -/* - * Extract a variable value from the top active VM frame. - * Return a pointer to the variable value on success. - * NULL otherwise (non-existent variable/Out-of-memory, ...). - */ -static jx9_value * VmExtractMemObj( - jx9_vm *pVm, /* Target VM */ - const SyString *pName, /* Variable name */ - int bDup, /* True to duplicate variable name */ - int bCreate /* True to create the variable if non-existent */ - ) -{ - int bNullify = FALSE; - SyHashEntry *pEntry; - VmFrame *pFrame; - jx9_value *pObj; - sxu32 nIdx; - sxi32 rc; - /* Point to the top active frame */ - pFrame = pVm->pFrame; - /* Perform the lookup */ - if( pName == 0 || pName->nByte < 1 ){ - static const SyString sAnnon = { " " , sizeof(char) }; - pName = &sAnnon; - /* Always nullify the object */ - bNullify = TRUE; - bDup = FALSE; - } - /* Check the superglobals table first */ - pEntry = SyHashGet(&pVm->hSuper, (const void *)pName->zString, pName->nByte); - if( pEntry == 0 ){ - /* Query the top active frame */ - pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte); - if( pEntry == 0 ){ - char *zName = (char *)pName->zString; - VmSlot sLocal; - if( !bCreate ){ - /* Do not create the variable, return NULL */ - return 0; - } - /* No such variable, automatically create a new one and install - * it in the current frame. - */ - pObj = jx9VmReserveMemObj(&(*pVm),&nIdx); - if( pObj == 0 ){ - return 0; - } - if( bDup ){ - /* Duplicate name */ - zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte); - if( zName == 0 ){ - return 0; - } - } - /* Link to the top active VM frame */ - rc = SyHashInsert(&pFrame->hVar, zName, pName->nByte, SX_INT_TO_PTR(nIdx)); - if( rc != SXRET_OK ){ - /* Return the slot to the free pool */ - sLocal.nIdx = nIdx; - sLocal.pUserData = 0; - SySetPut(&pVm->aFreeObj, (const void *)&sLocal); - return 0; - } - if( pFrame->pParent != 0 ){ - /* Local variable */ - sLocal.nIdx = nIdx; - SySetPut(&pFrame->sLocal, (const void *)&sLocal); - } - }else{ - /* Extract variable contents */ - nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - if( bNullify && pObj ){ - jx9MemObjRelease(pObj); - } - } - }else{ - /* Superglobal */ - nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - } - return pObj; -} -/* - * Extract a superglobal variable such as $_GET, $_POST, $_HEADERS, .... - * Return a pointer to the variable value on success.NULL otherwise. - */ -static jx9_value * VmExtractSuper( - jx9_vm *pVm, /* Target VM */ - const char *zName, /* Superglobal name: NOT NULL TERMINATED */ - sxu32 nByte /* zName length */ - ) -{ - SyHashEntry *pEntry; - jx9_value *pValue; - sxu32 nIdx; - /* Query the superglobal table */ - pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte); - if( pEntry == 0 ){ - /* No such entry */ - return 0; - } - /* Extract the superglobal index in the global object pool */ - nIdx = SX_PTR_TO_INT(pEntry->pUserData); - /* Extract the variable value */ - pValue = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - return pValue; -} -/* - * Perform a raw hashmap insertion. - * Refer to the [jx9VmConfigure()] implementation for additional information. - */ -static sxi32 VmHashmapInsert( - jx9_hashmap *pMap, /* Target hashmap */ - const char *zKey, /* Entry key */ - int nKeylen, /* zKey length*/ - const char *zData, /* Entry data */ - int nLen /* zData length */ - ) -{ - jx9_value sKey,sValue; - jx9_value *pKey; - sxi32 rc; - pKey = 0; - jx9MemObjInit(pMap->pVm, &sKey); - jx9MemObjInitFromString(pMap->pVm, &sValue, 0); - if( zKey ){ - if( nKeylen < 0 ){ - nKeylen = (int)SyStrlen(zKey); - } - jx9MemObjStringAppend(&sKey, zKey, (sxu32)nKeylen); - pKey = &sKey; - } - if( zData ){ - if( nLen < 0 ){ - /* Compute length automatically */ - nLen = (int)SyStrlen(zData); - } - jx9MemObjStringAppend(&sValue, zData, (sxu32)nLen); - } - /* Perform the insertion */ - rc = jx9HashmapInsert(&(*pMap),pKey,&sValue); - jx9MemObjRelease(&sKey); - jx9MemObjRelease(&sValue); - return rc; -} -/* Forward declaration */ -static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte); -/* - * Configure a working virtual machine instance. - * - * This routine is used to configure a JX9 virtual machine obtained by a prior - * successful call to one of the compile interface such as jx9_compile() - * jx9_compile_v2() or jx9_compile_file(). - * The second argument to this function is an integer configuration option - * that determines what property of the JX9 virtual machine is to be configured. - * Subsequent arguments vary depending on the configuration option in the second - * argument. There are many verbs but the most important are JX9_VM_CONFIG_OUTPUT, - * JX9_VM_CONFIG_HTTP_REQUEST and JX9_VM_CONFIG_ARGV_ENTRY. - * Refer to the official documentation for the list of allowed verbs. - */ -JX9_PRIVATE sxi32 jx9VmConfigure( - jx9_vm *pVm, /* Target VM */ - sxi32 nOp, /* Configuration verb */ - va_list ap /* Subsequent option arguments */ - ) -{ - sxi32 rc = SXRET_OK; - switch(nOp){ - case JX9_VM_CONFIG_OUTPUT: { - ProcConsumer xConsumer = va_arg(ap, ProcConsumer); - void *pUserData = va_arg(ap, void *); - /* VM output consumer callback */ -#ifdef UNTRUST - if( xConsumer == 0 ){ - rc = SXERR_CORRUPT; - break; - } -#endif - /* Install the output consumer */ - pVm->sVmConsumer.xConsumer = xConsumer; - pVm->sVmConsumer.pUserData = pUserData; - break; - } - case JX9_VM_CONFIG_IMPORT_PATH: { - /* Import path */ - const char *zPath; - SyString sPath; - zPath = va_arg(ap, const char *); -#if defined(UNTRUST) - if( zPath == 0 ){ - rc = SXERR_EMPTY; - break; - } -#endif - SyStringInitFromBuf(&sPath, zPath, SyStrlen(zPath)); - /* Remove trailing slashes and backslashes */ -#ifdef __WINNT__ - SyStringTrimTrailingChar(&sPath, '\\'); -#endif - SyStringTrimTrailingChar(&sPath, '/'); - /* Remove leading and trailing white spaces */ - SyStringFullTrim(&sPath); - if( sPath.nByte > 0 ){ - /* Store the path in the corresponding conatiner */ - rc = SySetPut(&pVm->aPaths, (const void *)&sPath); - } - break; - } - case JX9_VM_CONFIG_ERR_REPORT: - /* Run-Time Error report */ - pVm->bErrReport = 1; - break; - case JX9_VM_CONFIG_RECURSION_DEPTH:{ - /* Recursion depth */ - int nDepth = va_arg(ap, int); - if( nDepth > 2 && nDepth < 1024 ){ - pVm->nMaxDepth = nDepth; - } - break; - } - case JX9_VM_OUTPUT_LENGTH: { - /* VM output length in bytes */ - sxu32 *pOut = va_arg(ap, sxu32 *); -#ifdef UNTRUST - if( pOut == 0 ){ - rc = SXERR_CORRUPT; - break; - } -#endif - *pOut = pVm->nOutputLen; - break; - } - case JX9_VM_CONFIG_CREATE_VAR: { - /* Create a new superglobal/global variable */ - const char *zName = va_arg(ap, const char *); - jx9_value *pValue = va_arg(ap, jx9_value *); - SyHashEntry *pEntry; - jx9_value *pObj; - sxu32 nByte; - sxu32 nIdx; -#ifdef UNTRUST - if( SX_EMPTY_STR(zName) || pValue == 0 ){ - rc = SXERR_CORRUPT; - break; - } -#endif - nByte = SyStrlen(zName); - /* Check if the superglobal is already installed */ - pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte); - if( pEntry ){ - /* Variable already installed */ - nIdx = SX_PTR_TO_INT(pEntry->pUserData); - /* Extract contents */ - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - if( pObj ){ - /* Overwrite old contents */ - jx9MemObjStore(pValue, pObj); - } - }else{ - /* Install a new variable */ - pObj = jx9VmReserveMemObj(&(*pVm),&nIdx); - if( pObj == 0 ){ - rc = SXERR_MEM; - break; - } - /* Copy value */ - jx9MemObjStore(pValue, pObj); - /* Install the superglobal */ - rc = SyHashInsert(&pVm->hSuper, (const void *)zName, nByte, SX_INT_TO_PTR(nIdx)); - } - break; - } - case JX9_VM_CONFIG_SERVER_ATTR: - case JX9_VM_CONFIG_ENV_ATTR: { - const char *zKey = va_arg(ap, const char *); - const char *zValue = va_arg(ap, const char *); - int nLen = va_arg(ap, int); - jx9_hashmap *pMap; - jx9_value *pValue; - if( nOp == JX9_VM_CONFIG_ENV_ATTR ){ - /* Extract the $_ENV superglobal */ - pValue = VmExtractSuper(&(*pVm), "_ENV", sizeof("_ENV")-1); - }else{ - /* Extract the $_SERVER superglobal */ - pValue = VmExtractSuper(&(*pVm), "_SERVER", sizeof("_SERVER")-1); - } - if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* No such entry */ - rc = SXERR_NOTFOUND; - break; - } - /* Point to the hashmap */ - pMap = (jx9_hashmap *)pValue->x.pOther; - /* Perform the insertion */ - rc = VmHashmapInsert(pMap, zKey, -1, zValue, nLen); - break; - } - case JX9_VM_CONFIG_ARGV_ENTRY:{ - /* Script arguments */ - const char *zValue = va_arg(ap, const char *); - jx9_hashmap *pMap; - jx9_value *pValue; - /* Extract the $argv array */ - pValue = VmExtractSuper(&(*pVm), "argv", sizeof("argv")-1); - if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* No such entry */ - rc = SXERR_NOTFOUND; - break; - } - /* Point to the hashmap */ - pMap = (jx9_hashmap *)pValue->x.pOther; - /* Perform the insertion */ - rc = VmHashmapInsert(pMap, 0, 0, zValue,-1); - if( rc == SXRET_OK && zValue && zValue[0] != 0 ){ - if( pMap->nEntry > 1 ){ - /* Append space separator first */ - SyBlobAppend(&pVm->sArgv, (const void *)" ", sizeof(char)); - } - SyBlobAppend(&pVm->sArgv, (const void *)zValue,SyStrlen(zValue)); - } - break; - } - case JX9_VM_CONFIG_EXEC_VALUE: { - /* Script return value */ - jx9_value **ppValue = va_arg(ap, jx9_value **); -#ifdef UNTRUST - if( ppValue == 0 ){ - rc = SXERR_CORRUPT; - break; - } -#endif - *ppValue = &pVm->sExec; - break; - } - case JX9_VM_CONFIG_IO_STREAM: { - /* Register an IO stream device */ - const jx9_io_stream *pStream = va_arg(ap, const jx9_io_stream *); - /* Make sure we are dealing with a valid IO stream */ - if( pStream == 0 || pStream->zName == 0 || pStream->zName[0] == 0 || - pStream->xOpen == 0 || pStream->xRead == 0 ){ - /* Invalid stream */ - rc = SXERR_INVALID; - break; - } - if( pVm->pDefStream == 0 && SyStrnicmp(pStream->zName, "file", sizeof("file")-1) == 0 ){ - /* Make the 'file://' stream the defaut stream device */ - pVm->pDefStream = pStream; - } - /* Insert in the appropriate container */ - rc = SySetPut(&pVm->aIOstream, (const void *)&pStream); - break; - } - case JX9_VM_CONFIG_EXTRACT_OUTPUT: { - /* Point to the VM internal output consumer buffer */ - const void **ppOut = va_arg(ap, const void **); - unsigned int *pLen = va_arg(ap, unsigned int *); -#ifdef UNTRUST - if( ppOut == 0 || pLen == 0 ){ - rc = SXERR_CORRUPT; - break; - } -#endif - *ppOut = SyBlobData(&pVm->sConsumer); - *pLen = SyBlobLength(&pVm->sConsumer); - break; - } - case JX9_VM_CONFIG_HTTP_REQUEST:{ - /* Raw HTTP request*/ - const char *zRequest = va_arg(ap, const char *); - int nByte = va_arg(ap, int); - if( SX_EMPTY_STR(zRequest) ){ - rc = SXERR_EMPTY; - break; - } - if( nByte < 0 ){ - /* Compute length automatically */ - nByte = (int)SyStrlen(zRequest); - } - /* Process the request */ - rc = VmHttpProcessRequest(&(*pVm), zRequest, nByte); - break; - } - default: - /* Unknown configuration option */ - rc = SXERR_UNKNOWN; - break; - } - return rc; -} -/* Forward declaration */ -static const char * VmInstrToString(sxi32 nOp); -/* - * This routine is used to dump JX9 bytecode instructions to a human readable - * format. - * The dump is redirected to the given consumer callback which is responsible - * of consuming the generated dump perhaps redirecting it to its standard output - * (STDOUT). - */ -static sxi32 VmByteCodeDump( - SySet *pByteCode, /* Bytecode container */ - ProcConsumer xConsumer, /* Dump consumer callback */ - void *pUserData /* Last argument to xConsumer() */ - ) -{ - static const char zDump[] = { - "====================================================\n" - "JX9 VM Dump Copyright (C) 2012-2013 Symisc Systems\n" - " http://jx9.symisc.net/\n" - "====================================================\n" - }; - VmInstr *pInstr, *pEnd; - sxi32 rc = SXRET_OK; - sxu32 n; - /* Point to the JX9 instructions */ - pInstr = (VmInstr *)SySetBasePtr(pByteCode); - pEnd = &pInstr[SySetUsed(pByteCode)]; - n = 0; - xConsumer((const void *)zDump, sizeof(zDump)-1, pUserData); - /* Dump instructions */ - for(;;){ - if( pInstr >= pEnd ){ - /* No more instructions */ - break; - } - /* Format and call the consumer callback */ - rc = SyProcFormat(xConsumer, pUserData, "%s %8d %8u %#8x [%u]\n", - VmInstrToString(pInstr->iOp), pInstr->iP1, pInstr->iP2, - SX_PTR_TO_INT(pInstr->p3), n); - if( rc != SXRET_OK ){ - /* Consumer routine request an operation abort */ - return rc; - } - ++n; - pInstr++; /* Next instruction in the stream */ - } - return rc; -} -/* - * Consume a generated run-time error message by invoking the VM output - * consumer callback. - */ -static sxi32 VmCallErrorHandler(jx9_vm *pVm, SyBlob *pMsg) -{ - jx9_output_consumer *pCons = &pVm->sVmConsumer; - sxi32 rc = SXRET_OK; - /* Append a new line */ -#ifdef __WINNT__ - SyBlobAppend(pMsg, "\r\n", sizeof("\r\n")-1); -#else - SyBlobAppend(pMsg, "\n", sizeof(char)); -#endif - /* Invoke the output consumer callback */ - rc = pCons->xConsumer(SyBlobData(pMsg), SyBlobLength(pMsg), pCons->pUserData); - /* Increment output length */ - pVm->nOutputLen += SyBlobLength(pMsg); - - return rc; -} -/* - * Throw a run-time error and invoke the supplied VM output consumer callback. - * Refer to the implementation of [jx9_context_throw_error()] for additional - * information. - */ -JX9_PRIVATE sxi32 jx9VmThrowError( - jx9_vm *pVm, /* Target VM */ - SyString *pFuncName, /* Function name. NULL otherwise */ - sxi32 iErr, /* Severity level: [i.e: Error, Warning or Notice]*/ - const char *zMessage /* Null terminated error message */ - ) -{ - SyBlob *pWorker = &pVm->sWorker; - SyString *pFile; - char *zErr; - sxi32 rc; - if( !pVm->bErrReport ){ - /* Don't bother reporting errors */ - return SXRET_OK; - } - /* Reset the working buffer */ - SyBlobReset(pWorker); - /* Peek the processed file if available */ - pFile = (SyString *)SySetPeek(&pVm->aFiles); - if( pFile ){ - /* Append file name */ - SyBlobAppend(pWorker, pFile->zString, pFile->nByte); - SyBlobAppend(pWorker, (const void *)" ", sizeof(char)); - } - zErr = "Error: "; - switch(iErr){ - case JX9_CTX_WARNING: zErr = "Warning: "; break; - case JX9_CTX_NOTICE: zErr = "Notice: "; break; - default: - iErr = JX9_CTX_ERR; - break; - } - SyBlobAppend(pWorker, zErr, SyStrlen(zErr)); - if( pFuncName ){ - /* Append function name first */ - SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte); - SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1); - } - SyBlobAppend(pWorker, zMessage, SyStrlen(zMessage)); - /* Consume the error message */ - rc = VmCallErrorHandler(&(*pVm), pWorker); - return rc; -} -/* - * Format and throw a run-time error and invoke the supplied VM output consumer callback. - * Refer to the implementation of [jx9_context_throw_error_format()] for additional - * information. - */ -static sxi32 VmThrowErrorAp( - jx9_vm *pVm, /* Target VM */ - SyString *pFuncName, /* Function name. NULL otherwise */ - sxi32 iErr, /* Severity level: [i.e: Error, Warning or Notice] */ - const char *zFormat, /* Format message */ - va_list ap /* Variable list of arguments */ - ) -{ - SyBlob *pWorker = &pVm->sWorker; - SyString *pFile; - char *zErr; - sxi32 rc; - if( !pVm->bErrReport ){ - /* Don't bother reporting errors */ - return SXRET_OK; - } - /* Reset the working buffer */ - SyBlobReset(pWorker); - /* Peek the processed file if available */ - pFile = (SyString *)SySetPeek(&pVm->aFiles); - if( pFile ){ - /* Append file name */ - SyBlobAppend(pWorker, pFile->zString, pFile->nByte); - SyBlobAppend(pWorker, (const void *)" ", sizeof(char)); - } - zErr = "Error: "; - switch(iErr){ - case JX9_CTX_WARNING: zErr = "Warning: "; break; - case JX9_CTX_NOTICE: zErr = "Notice: "; break; - default: - iErr = JX9_CTX_ERR; - break; - } - SyBlobAppend(pWorker, zErr, SyStrlen(zErr)); - if( pFuncName ){ - /* Append function name first */ - SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte); - SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1); - } - SyBlobFormatAp(pWorker, zFormat, ap); - /* Consume the error message */ - rc = VmCallErrorHandler(&(*pVm), pWorker); - return rc; -} -/* - * Format and throw a run-time error and invoke the supplied VM output consumer callback. - * Refer to the implementation of [jx9_context_throw_error_format()] for additional - * information. - * ------------------------------------ - * Simple boring wrapper function. - * ------------------------------------ - */ -static sxi32 VmErrorFormat(jx9_vm *pVm, sxi32 iErr, const char *zFormat, ...) -{ - va_list ap; - sxi32 rc; - va_start(ap, zFormat); - rc = VmThrowErrorAp(&(*pVm), 0, iErr, zFormat, ap); - va_end(ap); - return rc; -} -/* - * Format and throw a run-time error and invoke the supplied VM output consumer callback. - * Refer to the implementation of [jx9_context_throw_error_format()] for additional - * information. - * ------------------------------------ - * Simple boring wrapper function. - * ------------------------------------ - */ -JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap) -{ - sxi32 rc; - rc = VmThrowErrorAp(&(*pVm), &(*pFuncName), iErr, zFormat, ap); - return rc; -} -/* Forward declaration */ -static sxi32 VmLocalExec(jx9_vm *pVm,SySet *pByteCode,jx9_value *pResult); -/* - * Execute as much of a JX9 bytecode program as we can then return. - * - * [jx9VmMakeReady()] must be called before this routine in order to - * close the program with a final OP_DONE and to set up the default - * consumer routines and other stuff. Refer to the implementation - * of [jx9VmMakeReady()] for additional information. - * If the installed VM output consumer callback ever returns JX9_ABORT - * then the program execution is halted. - * After this routine has finished, [jx9VmRelease()] or [jx9VmReset()] - * should be used respectively to clean up the mess that was left behind - * or to reset the VM to it's initial state. - */ -static sxi32 VmByteCodeExec( - jx9_vm *pVm, /* Target VM */ - VmInstr *aInstr, /* JX9 bytecode program */ - jx9_value *pStack, /* Operand stack */ - int nTos, /* Top entry in the operand stack (usually -1) */ - jx9_value *pResult /* Store program return value here. NULL otherwise */ - ) -{ - VmInstr *pInstr; - jx9_value *pTos; - SySet aArg; - sxi32 pc; - sxi32 rc; - /* Argument container */ - SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *)); - if( nTos < 0 ){ - pTos = &pStack[-1]; - }else{ - pTos = &pStack[nTos]; - } - pc = 0; - /* Execute as much as we can */ - for(;;){ - /* Fetch the instruction to execute */ - pInstr = &aInstr[pc]; - rc = SXRET_OK; -/* - * What follows here is a massive switch statement where each case implements a - * separate instruction in the virtual machine. If we follow the usual - * indentation convention each case should be indented by 6 spaces. But - * that is a lot of wasted space on the left margin. So the code within - * the switch statement will break with convention and be flush-left. - */ - switch(pInstr->iOp){ -/* - * DONE: P1 * * - * - * Program execution completed: Clean up the mess left behind - * and return immediately. - */ -case JX9_OP_DONE: - if( pInstr->iP1 ){ -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( pResult ){ - /* Execution result */ - jx9MemObjStore(pTos, pResult); - } - VmPopOperand(&pTos, 1); - } - goto Done; -/* - * HALT: P1 * * - * - * Program execution aborted: Clean up the mess left behind - * and abort immediately. - */ -case JX9_OP_HALT: - if( pInstr->iP1 ){ -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( pTos->iFlags & MEMOBJ_STRING ){ - if( SyBlobLength(&pTos->sBlob) > 0 ){ - /* Output the exit message */ - pVm->sVmConsumer.xConsumer(SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob), - pVm->sVmConsumer.pUserData); - /* Increment output length */ - pVm->nOutputLen += SyBlobLength(&pTos->sBlob); - } - }else if(pTos->iFlags & MEMOBJ_INT ){ - /* Record exit status */ - pVm->iExitStatus = (sxi32)pTos->x.iVal; - } - VmPopOperand(&pTos, 1); - } - goto Abort; -/* - * JMP: * P2 * - * - * Unconditional jump: The next instruction executed will be - * the one at index P2 from the beginning of the program. - */ -case JX9_OP_JMP: - pc = pInstr->iP2 - 1; - break; -/* - * JZ: P1 P2 * - * - * Take the jump if the top value is zero (FALSE jump).Pop the top most - * entry in the stack if P1 is zero. - */ -case JX9_OP_JZ: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Get a boolean value */ - if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - if( !pTos->x.iVal ){ - /* Take the jump */ - pc = pInstr->iP2 - 1; - } - if( !pInstr->iP1 ){ - VmPopOperand(&pTos, 1); - } - break; -/* - * JNZ: P1 P2 * - * - * Take the jump if the top value is not zero (TRUE jump).Pop the top most - * entry in the stack if P1 is zero. - */ -case JX9_OP_JNZ: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Get a boolean value */ - if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - if( pTos->x.iVal ){ - /* Take the jump */ - pc = pInstr->iP2 - 1; - } - if( !pInstr->iP1 ){ - VmPopOperand(&pTos, 1); - } - break; -/* - * NOOP: * * * - * - * Do nothing. This instruction is often useful as a jump - * destination. - */ -case JX9_OP_NOOP: - break; -/* - * POP: P1 * * - * - * Pop P1 elements from the operand stack. - */ -case JX9_OP_POP: { - sxi32 n = pInstr->iP1; - if( &pTos[-n+1] < pStack ){ - /* TICKET 1433-51 Stack underflow must be handled at run-time */ - n = (sxi32)(pTos - pStack); - } - VmPopOperand(&pTos, n); - break; - } -/* - * CVT_INT: * * * - * - * Force the top of the stack to be an integer. - */ -case JX9_OP_CVT_INT: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if((pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_INT); - break; -/* - * CVT_REAL: * * * - * - * Force the top of the stack to be a real. - */ -case JX9_OP_CVT_REAL: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if((pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_REAL); - break; -/* - * CVT_STR: * * * - * - * Force the top of the stack to be a string. - */ -case JX9_OP_CVT_STR: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pTos); - } - break; -/* - * CVT_BOOL: * * * - * - * Force the top of the stack to be a boolean. - */ -case JX9_OP_CVT_BOOL: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - break; -/* - * CVT_NULL: * * * - * - * Nullify the top of the stack. - */ -case JX9_OP_CVT_NULL: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - jx9MemObjRelease(pTos); - break; -/* - * CVT_NUMC: * * * - * - * Force the top of the stack to be a numeric type (integer, real or both). - */ -case JX9_OP_CVT_NUMC: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a numeric cast */ - jx9MemObjToNumeric(pTos); - break; -/* - * CVT_ARRAY: * * * - * - * Force the top of the stack to be a hashmap aka 'array'. - */ -case JX9_OP_CVT_ARRAY: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a hashmap cast */ - rc = jx9MemObjToHashmap(pTos); - if( rc != SXRET_OK ){ - /* Not so fatal, emit a simple warning */ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING, - "JX9 engine is running out of memory while performing an array cast"); - } - break; -/* - * LOADC P1 P2 * - * - * Load a constant [i.e: JX9_EOL, JX9_OS, __TIME__, ...] indexed at P2 in the constant pool. - * If P1 is set, then this constant is candidate for expansion via user installable callbacks. - */ -case JX9_OP_LOADC: { - jx9_value *pObj; - /* Reserve a room */ - pTos++; - if( (pObj = (jx9_value *)SySetAt(&pVm->aLitObj, pInstr->iP2)) != 0 ){ - if( pInstr->iP1 == 1 && SyBlobLength(&pObj->sBlob) <= 64 ){ - SyHashEntry *pEntry; - /* Candidate for expansion via user defined callbacks */ - pEntry = SyHashGet(&pVm->hConstant, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); - if( pEntry ){ - jx9_constant *pCons = (jx9_constant *)pEntry->pUserData; - /* Set a NULL default value */ - MemObjSetType(pTos, MEMOBJ_NULL); - SyBlobReset(&pTos->sBlob); - /* Invoke the callback and deal with the expanded value */ - pCons->xExpand(pTos, pCons->pUserData); - /* Mark as constant */ - pTos->nIdx = SXU32_HIGH; - break; - } - } - jx9MemObjLoad(pObj, pTos); - }else{ - /* Set a NULL value */ - MemObjSetType(pTos, MEMOBJ_NULL); - } - /* Mark as constant */ - pTos->nIdx = SXU32_HIGH; - break; - } -/* - * LOAD: P1 * P3 - * - * Load a variable where it's name is taken from the top of the stack or - * from the P3 operand. - * If P1 is set, then perform a lookup only.In other words do not create - * the variable if non existent and push the NULL constant instead. - */ -case JX9_OP_LOAD:{ - jx9_value *pObj; - SyString sName; - if( pInstr->p3 == 0 ){ - /* Take the variable name from the top of the stack */ -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a string cast */ - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pTos); - } - SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - }else{ - SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3)); - /* Reserve a room for the target object */ - pTos++; - } - /* Extract the requested memory object */ - pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, pInstr->iP1 != 1); - if( pObj == 0 ){ - if( pInstr->iP1 ){ - /* Variable not found, load NULL */ - if( !pInstr->p3 ){ - jx9MemObjRelease(pTos); - }else{ - MemObjSetType(pTos, MEMOBJ_NULL); - } - pTos->nIdx = SXU32_HIGH; /* Mark as constant */ - break; - }else{ - /* Fatal error */ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName); - goto Abort; - } - } - /* Load variable contents */ - jx9MemObjLoad(pObj, pTos); - pTos->nIdx = pObj->nIdx; - break; - } -/* - * LOAD_MAP P1 * * - * - * Allocate a new empty hashmap (array in the JX9 jargon) and push it on the stack. - * If the P1 operand is greater than zero then pop P1 elements from the - * stack and insert them (key => value pair) in the new hashmap. - */ -case JX9_OP_LOAD_MAP: { - jx9_hashmap *pMap; - int is_json_object; /* TRUE if we are dealing with a JSON object */ - int iIncr = 1; - /* Allocate a new hashmap instance */ - pMap = jx9NewHashmap(&(*pVm), 0, 0); - if( pMap == 0 ){ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, - "Fatal, JX9 engine is running out of memory while loading JSON array/object at instruction #:%d", pc); - goto Abort; - } - is_json_object = 0; - if( pInstr->iP2 ){ - /* JSON object, record that */ - pMap->iFlags |= HASHMAP_JSON_OBJECT; - is_json_object = 1; - iIncr = 2; - } - if( pInstr->iP1 > 0 ){ - jx9_value *pEntry = &pTos[-pInstr->iP1+1]; /* Point to the first entry */ - /* Perform the insertion */ - while( pEntry <= pTos ){ - /* Standard insertion */ - jx9HashmapInsert(pMap, - is_json_object ? pEntry : 0 /* Automatic index assign */, - is_json_object ? &pEntry[1] : pEntry - ); - /* Next pair on the stack */ - pEntry += iIncr; - } - /* Pop P1 elements */ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Push the hashmap */ - pTos++; - pTos->x.pOther = pMap; - MemObjSetType(pTos, MEMOBJ_HASHMAP); - break; - } -/* - * LOAD_IDX: P1 P2 * - * - * Load a hasmap entry where it's index (either numeric or string) is taken - * from the stack. - * If the index does not refer to a valid element, then push the NULL constant - * instead. - */ -case JX9_OP_LOAD_IDX: { - jx9_hashmap_node *pNode = 0; /* cc warning */ - jx9_hashmap *pMap = 0; - jx9_value *pIdx; - pIdx = 0; - if( pInstr->iP1 == 0 ){ - if( !pInstr->iP2){ - /* No available index, load NULL */ - if( pTos >= pStack ){ - jx9MemObjRelease(pTos); - }else{ - /* TICKET 1433-020: Empty stack */ - pTos++; - MemObjSetType(pTos, MEMOBJ_NULL); - pTos->nIdx = SXU32_HIGH; - } - /* Emit a notice */ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_NOTICE, - "JSON Array/Object: Attempt to access an undefined member, JX9 is loading NULL"); - break; - } - }else{ - pIdx = pTos; - pTos--; - } - if( pTos->iFlags & MEMOBJ_STRING ){ - /* String access */ - if( pIdx ){ - sxu32 nOfft; - if( (pIdx->iFlags & MEMOBJ_INT) == 0 ){ - /* Force an int cast */ - jx9MemObjToInteger(pIdx); - } - nOfft = (sxu32)pIdx->x.iVal; - if( nOfft >= SyBlobLength(&pTos->sBlob) ){ - /* Invalid offset, load null */ - jx9MemObjRelease(pTos); - }else{ - const char *zData = (const char *)SyBlobData(&pTos->sBlob); - int c = zData[nOfft]; - jx9MemObjRelease(pTos); - MemObjSetType(pTos, MEMOBJ_STRING); - SyBlobAppend(&pTos->sBlob, (const void *)&c, sizeof(char)); - } - }else{ - /* No available index, load NULL */ - MemObjSetType(pTos, MEMOBJ_NULL); - } - break; - } - if( pInstr->iP2 && (pTos->iFlags & MEMOBJ_HASHMAP) == 0 ){ - if( pTos->nIdx != SXU32_HIGH ){ - jx9_value *pObj; - if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjToHashmap(pObj); - jx9MemObjLoad(pObj, pTos); - } - } - } - rc = SXERR_NOTFOUND; /* Assume the index is invalid */ - if( pTos->iFlags & MEMOBJ_HASHMAP ){ - /* Point to the hashmap */ - pMap = (jx9_hashmap *)pTos->x.pOther; - if( pIdx ){ - /* Load the desired entry */ - rc = jx9HashmapLookup(pMap, pIdx, &pNode); - } - if( rc != SXRET_OK && pInstr->iP2 ){ - /* Create a new empty entry */ - rc = jx9HashmapInsert(pMap, pIdx, 0); - if( rc == SXRET_OK ){ - /* Point to the last inserted entry */ - pNode = pMap->pLast; - } - } - } - if( pIdx ){ - jx9MemObjRelease(pIdx); - } - if( rc == SXRET_OK ){ - /* Load entry contents */ - if( pMap->iRef < 2 ){ - /* TICKET 1433-42: Array will be deleted shortly, so we will make a copy - * of the entry value, rather than pointing to it. - */ - pTos->nIdx = SXU32_HIGH; - jx9HashmapExtractNodeValue(pNode, pTos, TRUE); - }else{ - pTos->nIdx = pNode->nValIdx; - jx9HashmapExtractNodeValue(pNode, pTos, FALSE); - jx9HashmapUnref(pMap); - } - }else{ - /* No such entry, load NULL */ - jx9MemObjRelease(pTos); - pTos->nIdx = SXU32_HIGH; - } - break; - } -/* - * STORE * P2 P3 - * - * Perform a store (Assignment) operation. - */ -case JX9_OP_STORE: { - jx9_value *pObj; - SyString sName; -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( pInstr->iP2 ){ - sxu32 nIdx; - /* Member store operation */ - nIdx = pTos->nIdx; - VmPopOperand(&pTos, 1); - if( nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, - "Cannot perform assignment on a constant object attribute, JX9 is loading NULL"); - pTos->nIdx = SXU32_HIGH; - }else{ - /* Point to the desired memory object */ - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - if( pObj ){ - /* Perform the store operation */ - jx9MemObjStore(pTos, pObj); - } - } - break; - }else if( pInstr->p3 == 0 ){ - /* Take the variable name from the next on the stack */ - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pTos); - } - SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - pTos--; -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - }else{ - SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3)); - } - /* Extract the desired variable and if not available dynamically create it */ - pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, TRUE); - if( pObj == 0 ){ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, - "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName); - goto Abort; - } - if( !pInstr->p3 ){ - jx9MemObjRelease(&pTos[1]); - } - /* Perform the store operation */ - jx9MemObjStore(pTos, pObj); - break; - } -/* - * STORE_IDX: P1 * P3 - * - * Perfrom a store operation an a hashmap entry. - */ -case JX9_OP_STORE_IDX: { - jx9_hashmap *pMap = 0; /* cc warning */ - jx9_value *pKey; - sxu32 nIdx; - if( pInstr->iP1 ){ - /* Key is next on stack */ - pKey = pTos; - pTos--; - }else{ - pKey = 0; - } - nIdx = pTos->nIdx; - if( pTos->iFlags & MEMOBJ_HASHMAP ){ - /* Hashmap already loaded */ - pMap = (jx9_hashmap *)pTos->x.pOther; - if( pMap->iRef < 2 ){ - /* TICKET 1433-48: Prevent garbage collection */ - pMap->iRef = 2; - } - }else{ - jx9_value *pObj; - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx); - if( pObj == 0 ){ - if( pKey ){ - jx9MemObjRelease(pKey); - } - VmPopOperand(&pTos, 1); - break; - } - /* Phase#1: Load the array */ - if( (pObj->iFlags & MEMOBJ_STRING) ){ - VmPopOperand(&pTos, 1); - if( (pTos->iFlags&MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pTos); - } - if( pKey == 0 ){ - /* Append string */ - if( SyBlobLength(&pTos->sBlob) > 0 ){ - SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - } - }else{ - sxu32 nOfft; - if((pKey->iFlags & MEMOBJ_INT)){ - /* Force an int cast */ - jx9MemObjToInteger(pKey); - } - nOfft = (sxu32)pKey->x.iVal; - if( nOfft < SyBlobLength(&pObj->sBlob) && SyBlobLength(&pTos->sBlob) > 0 ){ - const char *zBlob = (const char *)SyBlobData(&pTos->sBlob); - char *zData = (char *)SyBlobData(&pObj->sBlob); - zData[nOfft] = zBlob[0]; - }else{ - if( SyBlobLength(&pTos->sBlob) >= sizeof(char) ){ - /* Perform an append operation */ - SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), sizeof(char)); - } - } - } - if( pKey ){ - jx9MemObjRelease(pKey); - } - break; - }else if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* Force a hashmap cast */ - rc = jx9MemObjToHashmap(pObj); - if( rc != SXRET_OK ){ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while creating a new array"); - goto Abort; - } - } - pMap = (jx9_hashmap *)pObj->x.pOther; - } - VmPopOperand(&pTos, 1); - /* Phase#2: Perform the insertion */ - jx9HashmapInsert(pMap, pKey, pTos); - if( pKey ){ - jx9MemObjRelease(pKey); - } - break; - } -/* - * INCR: P1 * * - * - * Force a numeric cast and increment the top of the stack by 1. - * If the P1 operand is set then perform a duplication of the top of - * the stack and increment after that. - */ -case JX9_OP_INCR: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){ - if( pTos->nIdx != SXU32_HIGH ){ - jx9_value *pObj; - if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - /* Force a numeric cast */ - jx9MemObjToNumeric(pObj); - if( pObj->iFlags & MEMOBJ_REAL ){ - pObj->x.rVal++; - /* Try to get an integer representation */ - jx9MemObjTryInteger(pTos); - }else{ - pObj->x.iVal++; - MemObjSetType(pTos, MEMOBJ_INT); - } - if( pInstr->iP1 ){ - /* Pre-icrement */ - jx9MemObjStore(pObj, pTos); - } - } - }else{ - if( pInstr->iP1 ){ - /* Force a numeric cast */ - jx9MemObjToNumeric(pTos); - /* Pre-increment */ - if( pTos->iFlags & MEMOBJ_REAL ){ - pTos->x.rVal++; - /* Try to get an integer representation */ - jx9MemObjTryInteger(pTos); - }else{ - pTos->x.iVal++; - MemObjSetType(pTos, MEMOBJ_INT); - } - } - } - } - break; -/* - * DECR: P1 * * - * - * Force a numeric cast and decrement the top of the stack by 1. - * If the P1 operand is set then perform a duplication of the top of the stack - * and decrement after that. - */ -case JX9_OP_DECR: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES|MEMOBJ_NULL)) == 0 ){ - /* Force a numeric cast */ - jx9MemObjToNumeric(pTos); - if( pTos->nIdx != SXU32_HIGH ){ - jx9_value *pObj; - if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - /* Force a numeric cast */ - jx9MemObjToNumeric(pObj); - if( pObj->iFlags & MEMOBJ_REAL ){ - pObj->x.rVal--; - /* Try to get an integer representation */ - jx9MemObjTryInteger(pTos); - }else{ - pObj->x.iVal--; - MemObjSetType(pTos, MEMOBJ_INT); - } - if( pInstr->iP1 ){ - /* Pre-icrement */ - jx9MemObjStore(pObj, pTos); - } - } - }else{ - if( pInstr->iP1 ){ - /* Pre-increment */ - if( pTos->iFlags & MEMOBJ_REAL ){ - pTos->x.rVal--; - /* Try to get an integer representation */ - jx9MemObjTryInteger(pTos); - }else{ - pTos->x.iVal--; - MemObjSetType(pTos, MEMOBJ_INT); - } - } - } - } - break; -/* - * UMINUS: * * * - * - * Perform a unary minus operation. - */ -case JX9_OP_UMINUS: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a numeric (integer, real or both) cast */ - jx9MemObjToNumeric(pTos); - if( pTos->iFlags & MEMOBJ_REAL ){ - pTos->x.rVal = -pTos->x.rVal; - } - if( pTos->iFlags & MEMOBJ_INT ){ - pTos->x.iVal = -pTos->x.iVal; - } - break; -/* - * UPLUS: * * * - * - * Perform a unary plus operation. - */ -case JX9_OP_UPLUS: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a numeric (integer, real or both) cast */ - jx9MemObjToNumeric(pTos); - if( pTos->iFlags & MEMOBJ_REAL ){ - pTos->x.rVal = +pTos->x.rVal; - } - if( pTos->iFlags & MEMOBJ_INT ){ - pTos->x.iVal = +pTos->x.iVal; - } - break; -/* - * OP_LNOT: * * * - * - * Interpret the top of the stack as a boolean value. Replace it - * with its complement. - */ -case JX9_OP_LNOT: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force a boolean cast */ - if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - pTos->x.iVal = !pTos->x.iVal; - break; -/* - * OP_BITNOT: * * * - * - * Interpret the top of the stack as an value.Replace it - * with its ones-complement. - */ -case JX9_OP_BITNOT: -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - /* Force an integer cast */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - pTos->x.iVal = ~pTos->x.iVal; - break; -/* OP_MUL * * * - * OP_MUL_STORE * * * - * - * Pop the top two elements from the stack, multiply them together, - * and push the result back onto the stack. - */ -case JX9_OP_MUL: -case JX9_OP_MUL_STORE: { - jx9_value *pNos = &pTos[-1]; - /* Force the operand to be numeric */ -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - jx9MemObjToNumeric(pTos); - jx9MemObjToNumeric(pNos); - /* Perform the requested operation */ - if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ - /* Floating point arithemic */ - jx9_real a, b, r; - if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pNos); - } - a = pNos->x.rVal; - b = pTos->x.rVal; - r = a * b; - /* Push the result */ - pNos->x.rVal = r; - MemObjSetType(pNos, MEMOBJ_REAL); - /* Try to get an integer representation */ - jx9MemObjTryInteger(pNos); - }else{ - /* Integer arithmetic */ - sxi64 a, b, r; - a = pNos->x.iVal; - b = pTos->x.iVal; - r = a * b; - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - } - if( pInstr->iOp == JX9_OP_MUL_STORE ){ - jx9_value *pObj; - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - } - VmPopOperand(&pTos, 1); - break; - } -/* OP_ADD * * * - * - * Pop the top two elements from the stack, add them together, - * and push the result back onto the stack. - */ -case JX9_OP_ADD:{ - jx9_value *pNos = &pTos[-1]; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Perform the addition */ - jx9MemObjAdd(pNos, pTos, FALSE); - VmPopOperand(&pTos, 1); - break; - } -/* - * OP_ADD_STORE * * * - * - * Pop the top two elements from the stack, add them together, - * and push the result back onto the stack. - */ -case JX9_OP_ADD_STORE:{ - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; - sxu32 nIdx; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Perform the addition */ - nIdx = pTos->nIdx; - jx9MemObjAdd(pTos, pNos, TRUE); - /* Peform the store operation */ - if( nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx)) != 0 ){ - jx9MemObjStore(pTos, pObj); - } - /* Ticket 1433-35: Perform a stack dup */ - jx9MemObjStore(pTos, pNos); - VmPopOperand(&pTos, 1); - break; - } -/* OP_SUB * * * - * - * Pop the top two elements from the stack, subtract the - * first (what was next on the stack) from the second (the - * top of the stack) and push the result back onto the stack. - */ -case JX9_OP_SUB: { - jx9_value *pNos = &pTos[-1]; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ - /* Floating point arithemic */ - jx9_real a, b, r; - if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pNos); - } - a = pNos->x.rVal; - b = pTos->x.rVal; - r = a - b; - /* Push the result */ - pNos->x.rVal = r; - MemObjSetType(pNos, MEMOBJ_REAL); - /* Try to get an integer representation */ - jx9MemObjTryInteger(pNos); - }else{ - /* Integer arithmetic */ - sxi64 a, b, r; - a = pNos->x.iVal; - b = pTos->x.iVal; - r = a - b; - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - } - VmPopOperand(&pTos, 1); - break; - } -/* OP_SUB_STORE * * * - * - * Pop the top two elements from the stack, subtract the - * first (what was next on the stack) from the second (the - * top of the stack) and push the result back onto the stack. - */ -case JX9_OP_SUB_STORE: { - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ - /* Floating point arithemic */ - jx9_real a, b, r; - if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pNos); - } - a = pTos->x.rVal; - b = pNos->x.rVal; - r = a - b; - /* Push the result */ - pNos->x.rVal = r; - MemObjSetType(pNos, MEMOBJ_REAL); - /* Try to get an integer representation */ - jx9MemObjTryInteger(pNos); - }else{ - /* Integer arithmetic */ - sxi64 a, b, r; - a = pTos->x.iVal; - b = pNos->x.iVal; - r = a - b; - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - } - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - VmPopOperand(&pTos, 1); - break; - } - -/* - * OP_MOD * * * - * - * Pop the top two elements from the stack, divide the - * first (what was next on the stack) from the second (the - * top of the stack) and push the remainder after division - * onto the stack. - * Note: Only integer arithemtic is allowed. - */ -case JX9_OP_MOD:{ - jx9_value *pNos = &pTos[-1]; - sxi64 a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pNos->x.iVal; - b = pTos->x.iVal; - if( b == 0 ){ - r = 0; - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a); - /* goto Abort; */ - }else{ - r = a%b; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - VmPopOperand(&pTos, 1); - break; - } -/* - * OP_MOD_STORE * * * - * - * Pop the top two elements from the stack, divide the - * first (what was next on the stack) from the second (the - * top of the stack) and push the remainder after division - * onto the stack. - * Note: Only integer arithemtic is allowed. - */ -case JX9_OP_MOD_STORE: { - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; - sxi64 a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pTos->x.iVal; - b = pNos->x.iVal; - if( b == 0 ){ - r = 0; - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a); - /* goto Abort; */ - }else{ - r = a%b; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - VmPopOperand(&pTos, 1); - break; - } -/* - * OP_DIV * * * - * - * Pop the top two elements from the stack, divide the - * first (what was next on the stack) from the second (the - * top of the stack) and push the result onto the stack. - * Note: Only floating point arithemtic is allowed. - */ -case JX9_OP_DIV:{ - jx9_value *pNos = &pTos[-1]; - jx9_real a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be real */ - if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pNos); - } - /* Perform the requested operation */ - a = pNos->x.rVal; - b = pTos->x.rVal; - if( b == 0 ){ - /* Division by zero */ - r = 0; - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Division by zero"); - /* goto Abort; */ - }else{ - r = a/b; - /* Push the result */ - pNos->x.rVal = r; - MemObjSetType(pNos, MEMOBJ_REAL); - /* Try to get an integer representation */ - jx9MemObjTryInteger(pNos); - } - VmPopOperand(&pTos, 1); - break; - } -/* - * OP_DIV_STORE * * * - * - * Pop the top two elements from the stack, divide the - * first (what was next on the stack) from the second (the - * top of the stack) and push the result onto the stack. - * Note: Only floating point arithemtic is allowed. - */ -case JX9_OP_DIV_STORE:{ - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; - jx9_real a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be real */ - if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pTos); - } - if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ - jx9MemObjToReal(pNos); - } - /* Perform the requested operation */ - a = pTos->x.rVal; - b = pNos->x.rVal; - if( b == 0 ){ - /* Division by zero */ - r = 0; - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd/0", a); - /* goto Abort; */ - }else{ - r = a/b; - /* Push the result */ - pNos->x.rVal = r; - MemObjSetType(pNos, MEMOBJ_REAL); - /* Try to get an integer representation */ - jx9MemObjTryInteger(pNos); - } - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - VmPopOperand(&pTos, 1); - break; - } -/* OP_BAND * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise AND of the - * two elements. -*/ -/* OP_BOR * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise OR of the - * two elements. - */ -/* OP_BXOR * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise XOR of the - * two elements. - */ -case JX9_OP_BAND: -case JX9_OP_BOR: -case JX9_OP_BXOR:{ - jx9_value *pNos = &pTos[-1]; - sxi64 a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pNos->x.iVal; - b = pTos->x.iVal; - switch(pInstr->iOp){ - case JX9_OP_BOR_STORE: - case JX9_OP_BOR: r = a|b; break; - case JX9_OP_BXOR_STORE: - case JX9_OP_BXOR: r = a^b; break; - case JX9_OP_BAND_STORE: - case JX9_OP_BAND: - default: r = a&b; break; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - VmPopOperand(&pTos, 1); - break; - } -/* OP_BAND_STORE * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise AND of the - * two elements. -*/ -/* OP_BOR_STORE * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise OR of the - * two elements. - */ -/* OP_BXOR_STORE * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the bit-wise XOR of the - * two elements. - */ -case JX9_OP_BAND_STORE: -case JX9_OP_BOR_STORE: -case JX9_OP_BXOR_STORE:{ - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; - sxi64 a, b, r; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pTos->x.iVal; - b = pNos->x.iVal; - switch(pInstr->iOp){ - case JX9_OP_BOR_STORE: - case JX9_OP_BOR: r = a|b; break; - case JX9_OP_BXOR_STORE: - case JX9_OP_BXOR: r = a^b; break; - case JX9_OP_BAND_STORE: - case JX9_OP_BAND: - default: r = a&b; break; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - VmPopOperand(&pTos, 1); - break; - } -/* OP_SHL * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the second element shifted - * left by N bits where N is the top element on the stack. - * Note: Only integer arithmetic is allowed. - */ -/* OP_SHR * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the second element shifted - * right by N bits where N is the top element on the stack. - * Note: Only integer arithmetic is allowed. - */ -case JX9_OP_SHL: -case JX9_OP_SHR: { - jx9_value *pNos = &pTos[-1]; - sxi64 a, r; - sxi32 b; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pNos->x.iVal; - b = (sxi32)pTos->x.iVal; - if( pInstr->iOp == JX9_OP_SHL ){ - r = a << b; - }else{ - r = a >> b; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - VmPopOperand(&pTos, 1); - break; - } -/* OP_SHL_STORE * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the second element shifted - * left by N bits where N is the top element on the stack. - * Note: Only integer arithmetic is allowed. - */ -/* OP_SHR_STORE * * * - * - * Pop the top two elements from the stack. Convert both elements - * to integers. Push back onto the stack the second element shifted - * right by N bits where N is the top element on the stack. - * Note: Only integer arithmetic is allowed. - */ -case JX9_OP_SHL_STORE: -case JX9_OP_SHR_STORE: { - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; - sxi64 a, r; - sxi32 b; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force the operands to be integer */ - if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pTos); - } - if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ - jx9MemObjToInteger(pNos); - } - /* Perform the requested operation */ - a = pTos->x.iVal; - b = (sxi32)pNos->x.iVal; - if( pInstr->iOp == JX9_OP_SHL_STORE ){ - r = a << b; - }else{ - r = a >> b; - } - /* Push the result */ - pNos->x.iVal = r; - MemObjSetType(pNos, MEMOBJ_INT); - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pNos, pObj); - } - VmPopOperand(&pTos, 1); - break; - } -/* CAT: P1 * * - * - * Pop P1 elements from the stack. Concatenate them togeher and push the result - * back. - */ -case JX9_OP_CAT:{ - jx9_value *pNos, *pCur; - if( pInstr->iP1 < 1 ){ - pNos = &pTos[-1]; - }else{ - pNos = &pTos[-pInstr->iP1+1]; - } -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force a string cast */ - if( (pNos->iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pNos); - } - pCur = &pNos[1]; - while( pCur <= pTos ){ - if( (pCur->iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pCur); - } - /* Perform the concatenation */ - if( SyBlobLength(&pCur->sBlob) > 0 ){ - jx9MemObjStringAppend(pNos, (const char *)SyBlobData(&pCur->sBlob), SyBlobLength(&pCur->sBlob)); - } - SyBlobRelease(&pCur->sBlob); - pCur++; - } - pTos = pNos; - break; - } -/* CAT_STORE: * * * - * - * Pop two elements from the stack. Concatenate them togeher and push the result - * back. - */ -case JX9_OP_CAT_STORE:{ - jx9_value *pNos = &pTos[-1]; - jx9_value *pObj; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - if((pTos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pTos); - } - if((pNos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pNos); - } - /* Perform the concatenation (Reverse order) */ - if( SyBlobLength(&pNos->sBlob) > 0 ){ - jx9MemObjStringAppend(pTos, (const char *)SyBlobData(&pNos->sBlob), SyBlobLength(&pNos->sBlob)); - } - /* Perform the store operation */ - if( pTos->nIdx == SXU32_HIGH ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute"); - }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){ - jx9MemObjStore(pTos, pObj); - } - jx9MemObjStore(pTos, pNos); - VmPopOperand(&pTos, 1); - break; - } -/* OP_AND: * * * - * - * Pop two values off the stack. Take the logical AND of the - * two values and push the resulting boolean value back onto the - * stack. - */ -/* OP_OR: * * * - * - * Pop two values off the stack. Take the logical OR of the - * two values and push the resulting boolean value back onto the - * stack. - */ -case JX9_OP_LAND: -case JX9_OP_LOR: { - jx9_value *pNos = &pTos[-1]; - sxi32 v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */ -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force a boolean cast */ - if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pNos); - } - v1 = pNos->x.iVal == 0 ? 1 : 0; - v2 = pTos->x.iVal == 0 ? 1 : 0; - if( pInstr->iOp == JX9_OP_LAND ){ - static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; - v1 = and_logic[v1*3+v2]; - }else{ - static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; - v1 = or_logic[v1*3+v2]; - } - if( v1 == 2 ){ - v1 = 1; - } - VmPopOperand(&pTos, 1); - pTos->x.iVal = v1 == 0 ? 1 : 0; - MemObjSetType(pTos, MEMOBJ_BOOL); - break; - } -/* OP_LXOR: * * * - * - * Pop two values off the stack. Take the logical XOR of the - * two values and push the resulting boolean value back onto the - * stack. - * According to the JX9 language reference manual: - * $a xor $b is evaluated to TRUE if either $a or $b is - * TRUE, but not both. - */ -case JX9_OP_LXOR:{ - jx9_value *pNos = &pTos[-1]; - sxi32 v = 0; -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - /* Force a boolean cast */ - if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pTos); - } - if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){ - jx9MemObjToBool(pNos); - } - if( (pNos->x.iVal && !pTos->x.iVal) || (pTos->x.iVal && !pNos->x.iVal) ){ - v = 1; - } - VmPopOperand(&pTos, 1); - pTos->x.iVal = v; - MemObjSetType(pTos, MEMOBJ_BOOL); - break; - } -/* OP_EQ P1 P2 P3 - * - * Pop the top two elements from the stack. If they are equal, then - * jump to instruction P2. Otherwise, continue to the next instruction. - * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the - * stack if the jump would have been taken, or a 0 (FALSE) if not. - */ -/* OP_NEQ P1 P2 P3 - * - * Pop the top two elements from the stack. If they are not equal, then - * jump to instruction P2. Otherwise, continue to the next instruction. - * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the - * stack if the jump would have been taken, or a 0 (FALSE) if not. - */ -case JX9_OP_EQ: -case JX9_OP_NEQ: { - jx9_value *pNos = &pTos[-1]; - /* Perform the comparison and act accordingly */ -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - rc = jx9MemObjCmp(pNos, pTos, FALSE, 0); - if( pInstr->iOp == JX9_OP_EQ ){ - rc = rc == 0; - }else{ - rc = rc != 0; - } - VmPopOperand(&pTos, 1); - if( !pInstr->iP2 ){ - /* Push comparison result without taking the jump */ - jx9MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - }else{ - if( rc ){ - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } -/* OP_TEQ P1 P2 * - * - * Pop the top two elements from the stack. If they have the same type and are equal - * then jump to instruction P2. Otherwise, continue to the next instruction. - * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the - * stack if the jump would have been taken, or a 0 (FALSE) if not. - */ -case JX9_OP_TEQ: { - jx9_value *pNos = &pTos[-1]; - /* Perform the comparison and act accordingly */ -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) == 0; - VmPopOperand(&pTos, 1); - if( !pInstr->iP2 ){ - /* Push comparison result without taking the jump */ - jx9MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - }else{ - if( rc ){ - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } -/* OP_TNE P1 P2 * - * - * Pop the top two elements from the stack.If they are not equal an they are not - * of the same type, then jump to instruction P2. Otherwise, continue to the next - * instruction. - * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the - * stack if the jump would have been taken, or a 0 (FALSE) if not. - * - */ -case JX9_OP_TNE: { - jx9_value *pNos = &pTos[-1]; - /* Perform the comparison and act accordingly */ -#ifdef UNTRUST - if( pNos < pStack ){ - goto Abort; - } -#endif - rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) != 0; - VmPopOperand(&pTos, 1); - if( !pInstr->iP2 ){ - /* Push comparison result without taking the jump */ - jx9MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - }else{ - if( rc ){ - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } -/* OP_LT P1 P2 P3 - * - * Pop the top two elements from the stack. If the second element (the top of stack) - * is less than the first (next on stack), then jump to instruction P2.Otherwise - * continue to the next instruction. In other words, jump if pNosiOp == JX9_OP_LE ){ - rc = rc < 1; - }else{ - rc = rc < 0; - } - VmPopOperand(&pTos, 1); - if( !pInstr->iP2 ){ - /* Push comparison result without taking the jump */ - jx9MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - }else{ - if( rc ){ - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } -/* OP_GT P1 P2 P3 - * - * Pop the top two elements from the stack. If the second element (the top of stack) - * is greater than the first (next on stack), then jump to instruction P2.Otherwise - * continue to the next instruction. In other words, jump if pNosiOp == JX9_OP_GE ){ - rc = rc >= 0; - }else{ - rc = rc > 0; - } - VmPopOperand(&pTos, 1); - if( !pInstr->iP2 ){ - /* Push comparison result without taking the jump */ - jx9MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - }else{ - if( rc ){ - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } -/* - * OP_FOREACH_INIT * P2 P3 - * Prepare a foreach step. - */ -case JX9_OP_FOREACH_INIT: { - jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3; - void *pName; -#ifdef UNTRUST - if( pTos < pStack ){ - goto Abort; - } -#endif - if( SyStringLength(&pInfo->sValue) < 1 ){ - /* Take the variable name from the top of the stack */ - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pTos); - } - /* Duplicate name */ - if( SyBlobLength(&pTos->sBlob) > 0 ){ - pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - SyStringInitFromBuf(&pInfo->sValue, pName, SyBlobLength(&pTos->sBlob)); - } - VmPopOperand(&pTos, 1); - } - if( (pInfo->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) < 1 ){ - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pTos); - } - /* Duplicate name */ - if( SyBlobLength(&pTos->sBlob) > 0 ){ - pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - SyStringInitFromBuf(&pInfo->sKey, pName, SyBlobLength(&pTos->sBlob)); - } - VmPopOperand(&pTos, 1); - } - /* Make sure we are dealing with a hashmap [i.e. JSON array or object ]*/ - if( (pTos->iFlags & (MEMOBJ_HASHMAP)) == 0 || SyStringLength(&pInfo->sValue) < 1 ){ - /* Jump out of the loop */ - if( (pTos->iFlags & MEMOBJ_NULL) == 0 ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING, - "Invalid argument supplied for the foreach statement, expecting JSON array or object instance"); - } - pc = pInstr->iP2 - 1; - }else{ - jx9_foreach_step *pStep; - pStep = (jx9_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_foreach_step)); - if( pStep == 0 ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step"); - /* Jump out of the loop */ - pc = pInstr->iP2 - 1; - }else{ - /* Zero the structure */ - SyZero(pStep, sizeof(jx9_foreach_step)); - /* Prepare the step */ - pStep->iFlags = pInfo->iFlags; - if( pTos->iFlags & MEMOBJ_HASHMAP ){ - jx9_hashmap *pMap = (jx9_hashmap *)pTos->x.pOther; - /* Reset the internal loop cursor */ - jx9HashmapResetLoopCursor(pMap); - /* Mark the step */ - pStep->pMap = pMap; - pMap->iRef++; - } - } - if( SXRET_OK != SySetPut(&pInfo->aStep, (const void *)&pStep) ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step"); - SyMemBackendPoolFree(&pVm->sAllocator, pStep); - /* Jump out of the loop */ - pc = pInstr->iP2 - 1; - } - } - VmPopOperand(&pTos, 1); - break; - } -/* - * OP_FOREACH_STEP * P2 P3 - * Perform a foreach step. Jump to P2 at the end of the step. - */ -case JX9_OP_FOREACH_STEP: { - jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3; - jx9_foreach_step **apStep, *pStep; - jx9_hashmap_node *pNode; - jx9_hashmap *pMap; - jx9_value *pValue; - /* Peek the last step */ - apStep = (jx9_foreach_step **)SySetBasePtr(&pInfo->aStep); - pStep = apStep[SySetUsed(&pInfo->aStep) - 1]; - pMap = pStep->pMap; - /* Extract the current node value */ - pNode = jx9HashmapGetNextEntry(pMap); - if( pNode == 0 ){ - /* No more entry to process */ - pc = pInstr->iP2 - 1; /* Jump to this destination */ - /* Automatically reset the loop cursor */ - jx9HashmapResetLoopCursor(pMap); - /* Cleanup the mess left behind */ - SyMemBackendPoolFree(&pVm->sAllocator, pStep); - SySetPop(&pInfo->aStep); - jx9HashmapUnref(pMap); - }else{ - if( (pStep->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0 ){ - jx9_value *pKey = VmExtractMemObj(&(*pVm), &pInfo->sKey, FALSE, TRUE); - if( pKey ){ - jx9HashmapExtractNodeKey(pNode, pKey); - } - } - /* Make a copy of the entry value */ - pValue = VmExtractMemObj(&(*pVm), &pInfo->sValue, FALSE, TRUE); - if( pValue ){ - jx9HashmapExtractNodeValue(pNode, pValue, TRUE); - } - } - break; - } -/* - * OP_MEMBER P1 P2 - * Load JSON object entry on the stack. - */ -case JX9_OP_MEMBER: { - jx9_hashmap_node *pNode = 0; /* cc warning */ - jx9_hashmap *pMap = 0; - jx9_value *pIdx; - pIdx = pTos; - pTos--; - rc = SXERR_NOTFOUND; /* Assume the index is invalid */ - if( pTos->iFlags & MEMOBJ_HASHMAP ){ - /* Point to the hashmap */ - pMap = (jx9_hashmap *)pTos->x.pOther; - /* Load the desired entry */ - rc = jx9HashmapLookup(pMap, pIdx, &pNode); - } - jx9MemObjRelease(pIdx); - if( rc == SXRET_OK ){ - /* Load entry contents */ - if( pMap->iRef < 2 ){ - /* TICKET 1433-42: Array will be deleted shortly, so we will make a copy - * of the entry value, rather than pointing to it. - */ - pTos->nIdx = SXU32_HIGH; - jx9HashmapExtractNodeValue(pNode, pTos, TRUE); - }else{ - pTos->nIdx = pNode->nValIdx; - jx9HashmapExtractNodeValue(pNode, pTos, FALSE); - jx9HashmapUnref(pMap); - } - }else{ - /* No such entry, load NULL */ - jx9MemObjRelease(pTos); - pTos->nIdx = SXU32_HIGH; - } - break; - } -/* - * OP_SWITCH * * P3 - * This is the bytecode implementation of the complex switch() JX9 construct. - */ -case JX9_OP_SWITCH: { - jx9_switch *pSwitch = (jx9_switch *)pInstr->p3; - jx9_case_expr *aCase, *pCase; - jx9_value sValue, sCaseValue; - sxu32 n, nEntry; -#ifdef UNTRUST - if( pSwitch == 0 || pTos < pStack ){ - goto Abort; - } -#endif - /* Point to the case table */ - aCase = (jx9_case_expr *)SySetBasePtr(&pSwitch->aCaseExpr); - nEntry = SySetUsed(&pSwitch->aCaseExpr); - /* Select the appropriate case block to execute */ - jx9MemObjInit(pVm, &sValue); - jx9MemObjInit(pVm, &sCaseValue); - for( n = 0 ; n < nEntry ; ++n ){ - pCase = &aCase[n]; - jx9MemObjLoad(pTos, &sValue); - /* Execute the case expression first */ - VmLocalExec(pVm,&pCase->aByteCode, &sCaseValue); - /* Compare the two expression */ - rc = jx9MemObjCmp(&sValue, &sCaseValue, FALSE, 0); - jx9MemObjRelease(&sValue); - jx9MemObjRelease(&sCaseValue); - if( rc == 0 ){ - /* Value match, jump to this block */ - pc = pCase->nStart - 1; - break; - } - } - VmPopOperand(&pTos, 1); - if( n >= nEntry ){ - /* No approprite case to execute, jump to the default case */ - if( pSwitch->nDefault > 0 ){ - pc = pSwitch->nDefault - 1; - }else{ - /* No default case, jump out of this switch */ - pc = pSwitch->nOut - 1; - } - } - break; - } -/* - * OP_UPLINK P1 * * - * Link a variable to the top active VM frame. - * This is used to implement the 'uplink' JX9 construct. - */ -case JX9_OP_UPLINK: { - if( pVm->pFrame->pParent ){ - jx9_value *pLink = &pTos[-pInstr->iP1+1]; - SyString sName; - /* Perform the link */ - while( pLink <= pTos ){ - if((pLink->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(pLink); - } - SyStringInitFromBuf(&sName, SyBlobData(&pLink->sBlob), SyBlobLength(&pLink->sBlob)); - if( sName.nByte > 0 ){ - VmFrameLink(&(*pVm), &sName); - } - pLink++; - } - } - VmPopOperand(&pTos, pInstr->iP1); - break; - } -/* - * OP_CALL P1 * * - * Call a JX9 or a foreign function and push the return value of the called - * function on the stack. - */ -case JX9_OP_CALL: { - jx9_value *pArg = &pTos[-pInstr->iP1]; - SyHashEntry *pEntry; - SyString sName; - /* Extract function name */ - if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ - /* Raise exception: Invalid function name */ - VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Invalid function name, JX9 is returning NULL."); - /* Pop given arguments */ - if( pInstr->iP1 > 0 ){ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Assume a null return value so that the program continue it's execution normally */ - jx9MemObjRelease(pTos); - break; - } - SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - /* Check for a compiled function first */ - pEntry = SyHashGet(&pVm->hFunction, (const void *)sName.zString, sName.nByte); - if( pEntry ){ - jx9_vm_func_arg *aFormalArg; - jx9_value *pFrameStack; - jx9_vm_func *pVmFunc; - VmFrame *pFrame; - jx9_value *pObj; - VmSlot sArg; - sxu32 n; - pVmFunc = (jx9_vm_func *)pEntry->pUserData; - /* Check The recursion limit */ - if( pVm->nRecursionDepth > pVm->nMaxDepth ){ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, - "Recursion limit reached while invoking user function '%z', JX9 will set a NULL return value", - &pVmFunc->sName); - /* Pop given arguments */ - if( pInstr->iP1 > 0 ){ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Assume a null return value so that the program continue it's execution normally */ - jx9MemObjRelease(pTos); - break; - } - if( pVmFunc->pNextName ){ - /* Function is candidate for overloading, select the appropriate function to call */ - pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (int)(pTos-pArg)); - } - /* Extract the formal argument set */ - aFormalArg = (jx9_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs); - /* Create a new VM frame */ - rc = VmEnterFrame(&(*pVm),pVmFunc,&pFrame); - if( rc != SXRET_OK ){ - /* Raise exception: Out of memory */ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, - "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.", - &pVmFunc->sName); - /* Pop given arguments */ - if( pInstr->iP1 > 0 ){ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Assume a null return value so that the program continue it's execution normally */ - jx9MemObjRelease(pTos); - break; - } - if( SySetUsed(&pVmFunc->aStatic) > 0 ){ - jx9_vm_func_static_var *pStatic, *aStatic; - /* Install static variables */ - aStatic = (jx9_vm_func_static_var *)SySetBasePtr(&pVmFunc->aStatic); - for( n = 0 ; n < SySetUsed(&pVmFunc->aStatic) ; ++n ){ - pStatic = &aStatic[n]; - if( pStatic->nIdx == SXU32_HIGH ){ - /* Initialize the static variables */ - pObj = VmReserveMemObj(&(*pVm), &pStatic->nIdx); - if( pObj ){ - /* Assume a NULL initialization value */ - jx9MemObjInit(&(*pVm), pObj); - if( SySetUsed(&pStatic->aByteCode) > 0 ){ - /* Evaluate initialization expression (Any complex expression) */ - VmLocalExec(&(*pVm), &pStatic->aByteCode, pObj); - } - pObj->nIdx = pStatic->nIdx; - }else{ - continue; - } - } - /* Install in the current frame */ - SyHashInsert(&pFrame->hVar, SyStringData(&pStatic->sName), SyStringLength(&pStatic->sName), - SX_INT_TO_PTR(pStatic->nIdx)); - } - } - /* Push arguments in the local frame */ - n = 0; - while( pArg < pTos ){ - if( n < SySetUsed(&pVmFunc->aArgs) ){ - if( (pArg->iFlags & MEMOBJ_NULL) && SySetUsed(&aFormalArg[n].aByteCode) > 0 ){ - /* NULL values are redirected to default arguments */ - rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pArg); - if( rc == JX9_ABORT ){ - goto Abort; - } - } - /* Make sure the given arguments are of the correct type */ - if( aFormalArg[n].nType > 0 ){ - if( ((pArg->iFlags & aFormalArg[n].nType) == 0) ){ - ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType); - /* Cast to the desired type */ - if( xCast ){ - xCast(pArg); - } - } - } - /* Pass by value, make a copy of the given argument */ - pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE); - }else{ - char zName[32]; - SyString sName; - /* Set a dummy name */ - sName.nByte = SyBufferFormat(zName, sizeof(zName), "[%u]apArg", n); - sName.zString = zName; - /* Annonymous argument */ - pObj = VmExtractMemObj(&(*pVm), &sName, TRUE, TRUE); - } - if( pObj ){ - jx9MemObjStore(pArg, pObj); - /* Insert argument index */ - sArg.nIdx = pObj->nIdx; - sArg.pUserData = 0; - SySetPut(&pFrame->sArg, (const void *)&sArg); - } - jx9MemObjRelease(pArg); - pArg++; - ++n; - } - /* Process default values */ - while( n < SySetUsed(&pVmFunc->aArgs) ){ - if( SySetUsed(&aFormalArg[n].aByteCode) > 0 ){ - pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE); - if( pObj ){ - /* Evaluate the default value and extract it's result */ - rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pObj); - if( rc == JX9_ABORT ){ - goto Abort; - } - /* Insert argument index */ - sArg.nIdx = pObj->nIdx; - sArg.pUserData = 0; - SySetPut(&pFrame->sArg, (const void *)&sArg); - /* Make sure the default argument is of the correct type */ - if( aFormalArg[n].nType > 0 && ((pObj->iFlags & aFormalArg[n].nType) == 0) ){ - ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType); - /* Cast to the desired type */ - xCast(pObj); - } - } - } - ++n; - } - /* Pop arguments, function name from the operand stack and assume the function - * does not return anything. - */ - jx9MemObjRelease(pTos); - pTos = &pTos[-pInstr->iP1]; - /* Allocate a new operand stack and evaluate the function body */ - pFrameStack = VmNewOperandStack(&(*pVm), SySetUsed(&pVmFunc->aByteCode)); - if( pFrameStack == 0 ){ - /* Raise exception: Out of memory */ - VmErrorFormat(&(*pVm), JX9_CTX_ERR, "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.", - &pVmFunc->sName); - if( pInstr->iP1 > 0 ){ - VmPopOperand(&pTos, pInstr->iP1); - } - break; - } - /* Increment nesting level */ - pVm->nRecursionDepth++; - /* Execute function body */ - rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(&pVmFunc->aByteCode), pFrameStack, -1, pTos); - /* Decrement nesting level */ - pVm->nRecursionDepth--; - /* Free the operand stack */ - SyMemBackendFree(&pVm->sAllocator, pFrameStack); - /* Leave the frame */ - VmLeaveFrame(&(*pVm)); - if( rc == JX9_ABORT ){ - /* Abort processing immeditaley */ - goto Abort; - } - }else{ - jx9_user_func *pFunc; - jx9_context sCtx; - jx9_value sRet; - /* Look for an installed foreign function */ - pEntry = SyHashGet(&pVm->hHostFunction, (const void *)sName.zString, sName.nByte); - if( pEntry == 0 ){ - /* Call to undefined function */ - VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Call to undefined function '%z', JX9 is returning NULL.", &sName); - /* Pop given arguments */ - if( pInstr->iP1 > 0 ){ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Assume a null return value so that the program continue it's execution normally */ - jx9MemObjRelease(pTos); - break; - } - pFunc = (jx9_user_func *)pEntry->pUserData; - /* Start collecting function arguments */ - SySetReset(&aArg); - while( pArg < pTos ){ - SySetPut(&aArg, (const void *)&pArg); - pArg++; - } - /* Assume a null return value */ - jx9MemObjInit(&(*pVm), &sRet); - /* Init the call context */ - VmInitCallContext(&sCtx, &(*pVm), pFunc, &sRet, 0); - /* Call the foreign function */ - rc = pFunc->xFunc(&sCtx, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg)); - /* Release the call context */ - VmReleaseCallContext(&sCtx); - if( rc == JX9_ABORT ){ - goto Abort; - } - if( pInstr->iP1 > 0 ){ - /* Pop function name and arguments */ - VmPopOperand(&pTos, pInstr->iP1); - } - /* Save foreign function return value */ - jx9MemObjStore(&sRet, pTos); - jx9MemObjRelease(&sRet); - } - break; - } -/* - * OP_CONSUME: P1 * * - * Consume (Invoke the installed VM output consumer callback) and POP P1 elements from the stack. - */ -case JX9_OP_CONSUME: { - jx9_output_consumer *pCons = &pVm->sVmConsumer; - jx9_value *pCur, *pOut = pTos; - - pOut = &pTos[-pInstr->iP1 + 1]; - pCur = pOut; - /* Start the consume process */ - while( pOut <= pTos ){ - /* Force a string cast */ - if( (pOut->iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(pOut); - } - if( SyBlobLength(&pOut->sBlob) > 0 ){ - /*SyBlobNullAppend(&pOut->sBlob);*/ - /* Invoke the output consumer callback */ - rc = pCons->xConsumer(SyBlobData(&pOut->sBlob), SyBlobLength(&pOut->sBlob), pCons->pUserData); - /* Increment output length */ - pVm->nOutputLen += SyBlobLength(&pOut->sBlob); - SyBlobRelease(&pOut->sBlob); - if( rc == SXERR_ABORT ){ - /* Output consumer callback request an operation abort. */ - goto Abort; - } - } - pOut++; - } - pTos = &pCur[-1]; - break; - } - - } /* Switch() */ - pc++; /* Next instruction in the stream */ - } /* For(;;) */ -Done: - SySetRelease(&aArg); - return SXRET_OK; -Abort: - SySetRelease(&aArg); - while( pTos >= pStack ){ - jx9MemObjRelease(pTos); - pTos--; - } - return JX9_ABORT; -} -/* - * Execute as much of a local JX9 bytecode program as we can then return. - * This function is a wrapper around [VmByteCodeExec()]. - * See block-comment on that function for additional information. - */ -static sxi32 VmLocalExec(jx9_vm *pVm, SySet *pByteCode,jx9_value *pResult) -{ - jx9_value *pStack; - sxi32 rc; - /* Allocate a new operand stack */ - pStack = VmNewOperandStack(&(*pVm), SySetUsed(pByteCode)); - if( pStack == 0 ){ - return SXERR_MEM; - } - /* Execute the program */ - rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pByteCode), pStack, -1, &(*pResult)); - /* Free the operand stack */ - SyMemBackendFree(&pVm->sAllocator, pStack); - /* Execution result */ - return rc; -} -/* - * Execute as much of a JX9 bytecode program as we can then return. - * This function is a wrapper around [VmByteCodeExec()]. - * See block-comment on that function for additional information. - */ -JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm) -{ - /* Make sure we are ready to execute this program */ - if( pVm->nMagic != JX9_VM_RUN ){ - return pVm->nMagic == JX9_VM_EXEC ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */ - } - /* Set the execution magic number */ - pVm->nMagic = JX9_VM_EXEC; - /* Execute the program */ - VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pVm->pByteContainer), pVm->aOps, -1, &pVm->sExec); - /* - * TICKET 1433-100: Do not remove the JX9_VM_EXEC magic number - * so that any following call to [jx9_vm_exec()] without calling - * [jx9_vm_reset()] first would fail. - */ - return SXRET_OK; -} -/* - * Extract a memory object (i.e. a variable) from the running script. - * This function must be called after calling jx9_vm_exec(). Otherwise - * NULL is returned. - */ -JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar) -{ - jx9_value *pValue; - if( pVm->nMagic != JX9_VM_EXEC ){ - /* call jx9_vm_exec() first */ - return 0; - } - /* Perform the lookup */ - pValue = VmExtractMemObj(pVm,pVar,FALSE,FALSE); - /* Lookup result */ - return pValue; -} -/* - * Invoke the installed VM output consumer callback to consume - * the desired message. - * Refer to the implementation of [jx9_context_output()] defined - * in 'api.c' for additional information. - */ -JX9_PRIVATE sxi32 jx9VmOutputConsume( - jx9_vm *pVm, /* Target VM */ - SyString *pString /* Message to output */ - ) -{ - jx9_output_consumer *pCons = &pVm->sVmConsumer; - sxi32 rc = SXRET_OK; - /* Call the output consumer */ - if( pString->nByte > 0 ){ - rc = pCons->xConsumer((const void *)pString->zString, pString->nByte, pCons->pUserData); - /* Increment output length */ - pVm->nOutputLen += pString->nByte; - } - return rc; -} -/* - * Format a message and invoke the installed VM output consumer - * callback to consume the formatted message. - * Refer to the implementation of [jx9_context_output_format()] defined - * in 'api.c' for additional information. - */ -JX9_PRIVATE sxi32 jx9VmOutputConsumeAp( - jx9_vm *pVm, /* Target VM */ - const char *zFormat, /* Formatted message to output */ - va_list ap /* Variable list of arguments */ - ) -{ - jx9_output_consumer *pCons = &pVm->sVmConsumer; - sxi32 rc = SXRET_OK; - SyBlob sWorker; - /* Format the message and call the output consumer */ - SyBlobInit(&sWorker, &pVm->sAllocator); - SyBlobFormatAp(&sWorker, zFormat, ap); - if( SyBlobLength(&sWorker) > 0 ){ - /* Consume the formatted message */ - rc = pCons->xConsumer(SyBlobData(&sWorker), SyBlobLength(&sWorker), pCons->pUserData); - } - /* Increment output length */ - pVm->nOutputLen += SyBlobLength(&sWorker); - /* Release the working buffer */ - SyBlobRelease(&sWorker); - return rc; -} -/* - * Return a string representation of the given JX9 OP code. - * This function never fail and always return a pointer - * to a null terminated string. - */ -static const char * VmInstrToString(sxi32 nOp) -{ - const char *zOp = "Unknown "; - switch(nOp){ - case JX9_OP_DONE: zOp = "DONE "; break; - case JX9_OP_HALT: zOp = "HALT "; break; - case JX9_OP_LOAD: zOp = "LOAD "; break; - case JX9_OP_LOADC: zOp = "LOADC "; break; - case JX9_OP_LOAD_MAP: zOp = "LOAD_MAP "; break; - case JX9_OP_LOAD_IDX: zOp = "LOAD_IDX "; break; - case JX9_OP_NOOP: zOp = "NOOP "; break; - case JX9_OP_JMP: zOp = "JMP "; break; - case JX9_OP_JZ: zOp = "JZ "; break; - case JX9_OP_JNZ: zOp = "JNZ "; break; - case JX9_OP_POP: zOp = "POP "; break; - case JX9_OP_CAT: zOp = "CAT "; break; - case JX9_OP_CVT_INT: zOp = "CVT_INT "; break; - case JX9_OP_CVT_STR: zOp = "CVT_STR "; break; - case JX9_OP_CVT_REAL: zOp = "CVT_REAL "; break; - case JX9_OP_CALL: zOp = "CALL "; break; - case JX9_OP_UMINUS: zOp = "UMINUS "; break; - case JX9_OP_UPLUS: zOp = "UPLUS "; break; - case JX9_OP_BITNOT: zOp = "BITNOT "; break; - case JX9_OP_LNOT: zOp = "LOGNOT "; break; - case JX9_OP_MUL: zOp = "MUL "; break; - case JX9_OP_DIV: zOp = "DIV "; break; - case JX9_OP_MOD: zOp = "MOD "; break; - case JX9_OP_ADD: zOp = "ADD "; break; - case JX9_OP_SUB: zOp = "SUB "; break; - case JX9_OP_SHL: zOp = "SHL "; break; - case JX9_OP_SHR: zOp = "SHR "; break; - case JX9_OP_LT: zOp = "LT "; break; - case JX9_OP_LE: zOp = "LE "; break; - case JX9_OP_GT: zOp = "GT "; break; - case JX9_OP_GE: zOp = "GE "; break; - case JX9_OP_EQ: zOp = "EQ "; break; - case JX9_OP_NEQ: zOp = "NEQ "; break; - case JX9_OP_TEQ: zOp = "TEQ "; break; - case JX9_OP_TNE: zOp = "TNE "; break; - case JX9_OP_BAND: zOp = "BITAND "; break; - case JX9_OP_BXOR: zOp = "BITXOR "; break; - case JX9_OP_BOR: zOp = "BITOR "; break; - case JX9_OP_LAND: zOp = "LOGAND "; break; - case JX9_OP_LOR: zOp = "LOGOR "; break; - case JX9_OP_LXOR: zOp = "LOGXOR "; break; - case JX9_OP_STORE: zOp = "STORE "; break; - case JX9_OP_STORE_IDX: zOp = "STORE_IDX "; break; - case JX9_OP_PULL: zOp = "PULL "; break; - case JX9_OP_SWAP: zOp = "SWAP "; break; - case JX9_OP_YIELD: zOp = "YIELD "; break; - case JX9_OP_CVT_BOOL: zOp = "CVT_BOOL "; break; - case JX9_OP_CVT_NULL: zOp = "CVT_NULL "; break; - case JX9_OP_CVT_ARRAY: zOp = "CVT_JSON "; break; - case JX9_OP_CVT_NUMC: zOp = "CVT_NUMC "; break; - case JX9_OP_INCR: zOp = "INCR "; break; - case JX9_OP_DECR: zOp = "DECR "; break; - case JX9_OP_ADD_STORE: zOp = "ADD_STORE "; break; - case JX9_OP_SUB_STORE: zOp = "SUB_STORE "; break; - case JX9_OP_MUL_STORE: zOp = "MUL_STORE "; break; - case JX9_OP_DIV_STORE: zOp = "DIV_STORE "; break; - case JX9_OP_MOD_STORE: zOp = "MOD_STORE "; break; - case JX9_OP_CAT_STORE: zOp = "CAT_STORE "; break; - case JX9_OP_SHL_STORE: zOp = "SHL_STORE "; break; - case JX9_OP_SHR_STORE: zOp = "SHR_STORE "; break; - case JX9_OP_BAND_STORE: zOp = "BAND_STORE "; break; - case JX9_OP_BOR_STORE: zOp = "BOR_STORE "; break; - case JX9_OP_BXOR_STORE: zOp = "BXOR_STORE "; break; - case JX9_OP_CONSUME: zOp = "CONSUME "; break; - case JX9_OP_MEMBER: zOp = "MEMBER "; break; - case JX9_OP_UPLINK: zOp = "UPLINK "; break; - case JX9_OP_SWITCH: zOp = "SWITCH "; break; - case JX9_OP_FOREACH_INIT: - zOp = "4EACH_INIT "; break; - case JX9_OP_FOREACH_STEP: - zOp = "4EACH_STEP "; break; - default: - break; - } - return zOp; -} -/* - * Dump JX9 bytecodes instructions to a human readable format. - * The xConsumer() callback which is an used defined function - * is responsible of consuming the generated dump. - */ -JX9_PRIVATE sxi32 jx9VmDump( - jx9_vm *pVm, /* Target VM */ - ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */ - void *pUserData /* Last argument to xConsumer() */ - ) -{ - sxi32 rc; - rc = VmByteCodeDump(pVm->pByteContainer, xConsumer, pUserData); - return rc; -} -/* - * Default constant expansion callback used by the 'const' statement if used - * outside a object body [i.e: global or function scope]. - * Refer to the implementation of [JX9_CompileConstant()] defined - * in 'compile.c' for additional information. - */ -JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData) -{ - SySet *pByteCode = (SySet *)pUserData; - /* Evaluate and expand constant value */ - VmLocalExec((jx9_vm *)SySetGetUserData(pByteCode), pByteCode, (jx9_value *)pVal); -} -/* - * Section: - * Function handling functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * int func_num_args(void) - * Returns the number of arguments passed to the function. - * Parameters - * None. - * Return - * Total number of arguments passed into the current user-defined function - * or -1 if called from the globe scope. - */ -static int vm_builtin_func_num_args(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - VmFrame *pFrame; - jx9_vm *pVm; - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Current frame */ - pFrame = pVm->pFrame; - if( pFrame->pParent == 0 ){ - SXUNUSED(nArg); - SXUNUSED(apArg); - /* Global frame, return -1 */ - jx9_result_int(pCtx, -1); - return SXRET_OK; - } - /* Total number of arguments passed to the enclosing function */ - nArg = (int)SySetUsed(&pFrame->sArg); - jx9_result_int(pCtx, nArg); - return SXRET_OK; -} -/* - * value func_get_arg(int $arg_num) - * Return an item from the argument list. - * Parameters - * Argument number(index start from zero). - * Return - * Returns the specified argument or FALSE on error. - */ -static int vm_builtin_func_get_arg(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pObj = 0; - VmSlot *pSlot = 0; - VmFrame *pFrame; - jx9_vm *pVm; - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Current frame */ - pFrame = pVm->pFrame; - if( nArg < 1 || pFrame->pParent == 0 ){ - /* Global frame or Missing arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope"); - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Extract the desired index */ - nArg = jx9_value_to_int(apArg[0]); - if( nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg) ){ - /* Invalid index, return FALSE */ - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Extract the desired argument */ - if( (pSlot = (VmSlot *)SySetAt(&pFrame->sArg, (sxu32)nArg)) != 0 ){ - if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx)) != 0 ){ - /* Return the desired argument */ - jx9_result_value(pCtx, (jx9_value *)pObj); - }else{ - /* No such argument, return false */ - jx9_result_bool(pCtx, 0); - } - }else{ - /* CAN'T HAPPEN */ - jx9_result_bool(pCtx, 0); - } - return SXRET_OK; -} -/* - * array func_get_args(void) - * Returns an array comprising a copy of function's argument list. - * Parameters - * None. - * Return - * Returns an array in which each element is a copy of the corresponding - * member of the current user-defined function's argument list. - * Otherwise FALSE is returned on failure. - */ -static int vm_builtin_func_get_args(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pObj = 0; - jx9_value *pArray; - VmFrame *pFrame; - VmSlot *aSlot; - sxu32 n; - /* Point to the current frame */ - pFrame = pCtx->pVm->pFrame; - if( pFrame->pParent == 0 ){ - /* Global frame, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope"); - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Start filling the array with the given arguments */ - aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg); - for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){ - pObj = (jx9_value *)SySetAt(&pCtx->pVm->aMemObj, aSlot[n].nIdx); - if( pObj ){ - jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pObj); - } - } - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - return SXRET_OK; -} -/* - * bool function_exists(string $name) - * Return TRUE if the given function has been defined. - * Parameters - * The name of the desired function. - * Return - * Return TRUE if the given function has been defined.False otherwise - */ -static int vm_builtin_func_exists(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zName; - jx9_vm *pVm; - int nLen; - int res; - if( nArg < 1 ){ - /* Missing argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Extract the function name */ - zName = jx9_value_to_string(apArg[0], &nLen); - /* Assume the function is not defined */ - res = 0; - /* Perform the lookup */ - if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 || - SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){ - /* Function is defined */ - res = 1; - } - jx9_result_bool(pCtx, res); - return SXRET_OK; -} -/* - * Verify that the contents of a variable can be called as a function. - * [i.e: Whether it is callable or not]. - * Return TRUE if callable.FALSE otherwise. - */ -JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue) -{ - int res = 0; - if( pValue->iFlags & MEMOBJ_STRING ){ - const char *zName; - int nLen; - /* Extract the name */ - zName = jx9_value_to_string(pValue, &nLen); - /* Perform the lookup */ - if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 || - SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){ - /* Function is callable */ - res = 1; - } - } - return res; -} -/* - * bool is_callable(callable $name[, bool $syntax_only = false]) - * Verify that the contents of a variable can be called as a function. - * Parameters - * $name - * The callback function to check - * $syntax_only - * If set to TRUE the function only verifies that name might be a function or method. - * It will only reject simple variables that are not strings, or an array that does - * not have a valid structure to be used as a callback. The valid ones are supposed - * to have only 2 entries, the first of which is an object or a string, and the second - * a string. - * Return - * TRUE if name is callable, FALSE otherwise. - */ -static int vm_builtin_is_callable(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_vm *pVm; - int res; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Perform the requested operation */ - res = jx9VmIsCallable(pVm, apArg[0]); - jx9_result_bool(pCtx, res); - return SXRET_OK; -} -/* - * Hash walker callback used by the [get_defined_functions()] function - * defined below. - */ -static int VmHashFuncStep(SyHashEntry *pEntry, void *pUserData) -{ - jx9_value *pArray = (jx9_value *)pUserData; - jx9_value sName; - sxi32 rc; - /* Prepare the function name for insertion */ - jx9MemObjInitFromString(pArray->pVm, &sName, 0); - jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen); - /* Perform the insertion */ - rc = jx9_array_add_elem(pArray, 0/* Automatic index assign */, &sName); /* Will make it's own copy */ - jx9MemObjRelease(&sName); - return rc; -} -/* - * array get_defined_functions(void) - * Returns an array of all defined functions. - * Parameter - * None. - * Return - * Returns an multidimensional array containing a list of all defined functions - * both built-in (internal) and user-defined. - * The internal functions will be accessible via $arr["internal"], and the user - * defined ones using $arr["user"]. - * Note: - * NULL is returned on failure. - */ -static int vm_builtin_get_defined_func(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pArray; - /* NOTE: - * Don't worry about freeing memory here, every allocated resource will be released - * automatically by the engine as soon we return from this foreign function. - */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* Return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* Fill with the appropriate information */ - SyHashForEach(&pCtx->pVm->hHostFunction,VmHashFuncStep,pArray); - /* Fill with the appropriate information */ - SyHashForEach(&pCtx->pVm->hFunction, VmHashFuncStep,pArray); - /* Return a copy of the array array */ - jx9_result_value(pCtx, pArray); - return SXRET_OK; -} -/* - * Call a user defined or foreign function where the name of the function - * is stored in the pFunc parameter and the given arguments are stored - * in the apArg[] array. - * Return SXRET_OK if the function was successfuly called.Any other - * return value indicates failure. - */ -JX9_PRIVATE sxi32 jx9VmCallUserFunction( - jx9_vm *pVm, /* Target VM */ - jx9_value *pFunc, /* Callback name */ - int nArg, /* Total number of given arguments */ - jx9_value **apArg, /* Callback arguments */ - jx9_value *pResult /* Store callback return value here. NULL otherwise */ - ) -{ - jx9_value *aStack; - VmInstr aInstr[2]; - int i; - if((pFunc->iFlags & (MEMOBJ_STRING)) == 0 ){ - /* Don't bother processing, it's invalid anyway */ - if( pResult ){ - /* Assume a null return value */ - jx9MemObjRelease(pResult); - } - return SXERR_INVALID; - } - /* Create a new operand stack */ - aStack = VmNewOperandStack(&(*pVm), 1+nArg); - if( aStack == 0 ){ - jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, - "JX9 is running out of memory while invoking user callback"); - if( pResult ){ - /* Assume a null return value */ - jx9MemObjRelease(pResult); - } - return SXERR_MEM; - } - /* Fill the operand stack with the given arguments */ - for( i = 0 ; i < nArg ; i++ ){ - jx9MemObjLoad(apArg[i], &aStack[i]); - aStack[i].nIdx = apArg[i]->nIdx; - } - /* Push the function name */ - jx9MemObjLoad(pFunc, &aStack[i]); - aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */ - /* Emit the CALL istruction */ - aInstr[0].iOp = JX9_OP_CALL; - aInstr[0].iP1 = nArg; /* Total number of given arguments */ - aInstr[0].iP2 = 0; - aInstr[0].p3 = 0; - /* Emit the DONE instruction */ - aInstr[1].iOp = JX9_OP_DONE; - aInstr[1].iP1 = 1; /* Extract function return value if available */ - aInstr[1].iP2 = 0; - aInstr[1].p3 = 0; - /* Execute the function body (if available) */ - VmByteCodeExec(&(*pVm), aInstr, aStack, nArg, pResult); - /* Clean up the mess left behind */ - SyMemBackendFree(&pVm->sAllocator, aStack); - return JX9_OK; -} -/* - * Call a user defined or foreign function whith a varibale number - * of arguments where the name of the function is stored in the pFunc - * parameter. - * Return SXRET_OK if the function was successfuly called.Any other - * return value indicates failure. - */ -JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp( - jx9_vm *pVm, /* Target VM */ - jx9_value *pFunc, /* Callback name */ - jx9_value *pResult, /* Store callback return value here. NULL otherwise */ - ... /* 0 (Zero) or more Callback arguments */ - ) -{ - jx9_value *pArg; - SySet aArg; - va_list ap; - sxi32 rc; - SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *)); - /* Copy arguments one after one */ - va_start(ap, pResult); - for(;;){ - pArg = va_arg(ap, jx9_value *); - if( pArg == 0 ){ - break; - } - SySetPut(&aArg, (const void *)&pArg); - } - /* Call the core routine */ - rc = jx9VmCallUserFunction(&(*pVm), pFunc, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg), pResult); - /* Cleanup */ - SySetRelease(&aArg); - return rc; -} -/* - * bool defined(string $name) - * Checks whether a given named constant exists. - * Parameter: - * Name of the desired constant. - * Return - * TRUE if the given constant exists.FALSE otherwise. - */ -static int vm_builtin_defined(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zName; - int nLen = 0; - int res = 0; - if( nArg < 1 ){ - /* Missing constant name, return FALSE */ - jx9_context_throw_error(pCtx,JX9_CTX_NOTICE,"Missing constant name"); - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Extract constant name */ - zName = jx9_value_to_string(apArg[0], &nLen); - /* Perform the lookup */ - if( nLen > 0 && SyHashGet(&pCtx->pVm->hConstant, (const void *)zName, (sxu32)nLen) != 0 ){ - /* Already defined */ - res = 1; - } - jx9_result_bool(pCtx, res); - return SXRET_OK; -} -/* - * Hash walker callback used by the [get_defined_constants()] function - * defined below. - */ -static int VmHashConstStep(SyHashEntry *pEntry, void *pUserData) -{ - jx9_value *pArray = (jx9_value *)pUserData; - jx9_value sName; - sxi32 rc; - /* Prepare the constant name for insertion */ - jx9MemObjInitFromString(pArray->pVm, &sName, 0); - jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen); - /* Perform the insertion */ - rc = jx9_array_add_elem(pArray, 0, &sName); /* Will make it's own copy */ - jx9MemObjRelease(&sName); - return rc; -} -/* - * array get_defined_constants(void) - * Returns an associative array with the names of all defined - * constants. - * Parameters - * NONE. - * Returns - * Returns the names of all the constants currently defined. - */ -static int vm_builtin_get_defined_constants(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pArray; - /* Create the array first*/ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - /* Return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* Fill the array with the defined constants */ - SyHashForEach(&pCtx->pVm->hConstant, VmHashConstStep, pArray); - /* Return the created array */ - jx9_result_value(pCtx, pArray); - return SXRET_OK; -} -/* - * Section: - * Random numbers/string generators. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * Generate a random 32-bit unsigned integer. - * JX9 use it's own private PRNG which is based on the one - * used by te SQLite3 library. - */ -JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm) -{ - sxu32 iNum; - SyRandomness(&pVm->sPrng, (void *)&iNum, sizeof(sxu32)); - return iNum; -} -/* - * Generate a random string (English Alphabet) of length nLen. - * Note that the generated string is NOT null terminated. - * JX9 use it's own private PRNG which is based on the one used - * by te SQLite3 library. - */ -JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen) -{ - static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */ - int i; - /* Generate a binary string first */ - SyRandomness(&pVm->sPrng, zBuf, (sxu32)nLen); - /* Turn the binary string into english based alphabet */ - for( i = 0 ; i < nLen ; ++i ){ - zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)]; - } -} -/* - * int rand() - * Generate a random (unsigned 32-bit) integer. - * Parameter - * $min - * The lowest value to return (default: 0) - * $max - * The highest value to return (default: getrandmax()) - * Return - * A pseudo random value between min (or 0) and max (or getrandmax(), inclusive). - * Note: - * JX9 use it's own private PRNG which is based on the one used - * by te SQLite3 library. - */ -static int vm_builtin_rand(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - sxu32 iNum; - /* Generate the random number */ - iNum = jx9VmRandomNum(pCtx->pVm); - if( nArg > 1 ){ - sxu32 iMin, iMax; - iMin = (sxu32)jx9_value_to_int(apArg[0]); - iMax = (sxu32)jx9_value_to_int(apArg[1]); - if( iMin < iMax ){ - sxu32 iDiv = iMax+1-iMin; - if( iDiv > 0 ){ - iNum = (iNum % iDiv)+iMin; - } - }else if(iMax > 0 ){ - iNum %= iMax; - } - } - /* Return the number */ - jx9_result_int64(pCtx, (jx9_int64)iNum); - return SXRET_OK; -} -/* - * int getrandmax(void) - * Show largest possible random value - * Return - * The largest possible random value returned by rand() which is in - * this implementation 0xFFFFFFFF. - * Note: - * JX9 use it's own private PRNG which is based on the one used - * by te SQLite3 library. - */ -static int vm_builtin_getrandmax(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - jx9_result_int64(pCtx, SXU32_HIGH); - return SXRET_OK; -} -/* - * string rand_str() - * string rand_str(int $len) - * Generate a random string (English alphabet). - * Parameter - * $len - * Length of the desired string (default: 16, Min: 1, Max: 1024) - * Return - * A pseudo random string. - * Note: - * JX9 use it's own private PRNG which is based on the one used - * by te SQLite3 library. - */ -static int vm_builtin_rand_str(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - char zString[1024]; - int iLen = 0x10; - if( nArg > 0 ){ - /* Get the desired length */ - iLen = jx9_value_to_int(apArg[0]); - if( iLen < 1 || iLen > 1024 ){ - /* Default length */ - iLen = 0x10; - } - } - /* Generate the random string */ - jx9VmRandomString(pCtx->pVm, zString, iLen); - /* Return the generated string */ - jx9_result_string(pCtx, zString, iLen); /* Will make it's own copy */ - return SXRET_OK; -} -/* - * Section: - * Language construct implementation as foreign functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * void print($string...) - * Output one or more messages. - * Parameters - * $string - * Message to output. - * Return - * NULL. - */ -static int vm_builtin_print(jx9_context *pCtx, int nArg,jx9_value **apArg) -{ - const char *zData; - int nDataLen = 0; - jx9_vm *pVm; - int i, rc; - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Output */ - for( i = 0 ; i < nArg ; ++i ){ - zData = jx9_value_to_string(apArg[i], &nDataLen); - if( nDataLen > 0 ){ - rc = pVm->sVmConsumer.xConsumer((const void *)zData, (unsigned int)nDataLen, pVm->sVmConsumer.pUserData); - /* Increment output length */ - pVm->nOutputLen += nDataLen; - if( rc == SXERR_ABORT ){ - /* Output consumer callback request an operation abort */ - return JX9_ABORT; - } - } - } - return SXRET_OK; -} -/* - * void exit(string $msg) - * void exit(int $status) - * void die(string $ms) - * void die(int $status) - * Output a message and terminate program execution. - * Parameter - * If status is a string, this function prints the status just before exiting. - * If status is an integer, that value will be used as the exit status - * and not printed - * Return - * NULL - */ -static int vm_builtin_exit(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg > 0 ){ - if( jx9_value_is_string(apArg[0]) ){ - const char *zData; - int iLen = 0; - /* Print exit message */ - zData = jx9_value_to_string(apArg[0], &iLen); - jx9_context_output(pCtx, zData, iLen); - }else if(jx9_value_is_int(apArg[0]) ){ - sxi32 iExitStatus; - /* Record exit status code */ - iExitStatus = jx9_value_to_int(apArg[0]); - pCtx->pVm->iExitStatus = iExitStatus; - } - } - /* Abort processing immediately */ - return JX9_ABORT; -} -/* - * Unset a memory object [i.e: a jx9_value]. - */ -JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm,sxu32 nObjIdx) -{ - jx9_value *pObj; - pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nObjIdx); - if( pObj ){ - VmSlot sFree; - /* Release the object */ - jx9MemObjRelease(pObj); - /* Restore to the free list */ - sFree.nIdx = nObjIdx; - sFree.pUserData = 0; - SySetPut(&pVm->aFreeObj, (const void *)&sFree); - } - return SXRET_OK; -} -/* - * string gettype($var) - * Get the type of a variable - * Parameters - * $var - * The variable being type checked. - * Return - * String representation of the given variable type. - */ -static int vm_builtin_gettype(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zType = "null"; - if( nArg > 0 ){ - zType = jx9MemObjTypeDump(apArg[0]); - } - /* Return the variable type */ - jx9_result_string(pCtx, zType, -1/*Compute length automatically*/); - return SXRET_OK; -} -/* - * string get_resource_type(resource $handle) - * This function gets the type of the given resource. - * Parameters - * $handle - * The evaluated resource handle. - * Return - * If the given handle is a resource, this function will return a string - * representing its type. If the type is not identified by this function - * the return value will be the string Unknown. - * This function will return FALSE and generate an error if handle - * is not a resource. - */ -static int vm_builtin_get_resource_type(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE*/ - jx9_result_bool(pCtx, 0); - return SXRET_OK; - } - jx9_result_string_format(pCtx, "resID_%#x", apArg[0]->x.pOther); - return SXRET_OK; -} -/* - * void dump(expression, ....) - * dump — Dumps information about a variable - * Parameters - * One or more expression to dump. - * Returns - * Nothing. - */ -static int vm_builtin_dump(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyBlob sDump; /* Generated dump is stored here */ - int i; - SyBlobInit(&sDump,&pCtx->pVm->sAllocator); - /* Dump one or more expressions */ - for( i = 0 ; i < nArg ; i++ ){ - jx9_value *pObj = apArg[i]; - /* Reset the working buffer */ - SyBlobReset(&sDump); - /* Dump the given expression */ - jx9MemObjDump(&sDump,pObj); - /* Output */ - if( SyBlobLength(&sDump) > 0 ){ - jx9_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump)); - } - } - /* Release the working buffer */ - SyBlobRelease(&sDump); - return SXRET_OK; -} -/* - * Section: - * Version, Credits and Copyright related functions. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Stable. - */ -/* - * string jx9_version(void) - * string jx9_credits(void) - * Returns the running version of the jx9 version. - * Parameters - * None - * Return - * Current jx9 version. - */ -static int vm_builtin_jx9_version(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SXUNUSED(nArg); - SXUNUSED(apArg); /* cc warning */ - /* Current engine version, signature and cipyright notice */ - jx9_result_string_format(pCtx,"%s %s, %s",JX9_VERSION,JX9_SIG,JX9_COPYRIGHT); - return JX9_OK; -} -/* - * Section: - * URL related routines. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* Forward declaration */ -static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen); -/* - * value parse_url(string $url [, int $component = -1 ]) - * Parse a URL and return its fields. - * Parameters - * $url - * The URL to parse. - * $component - * Specify one of JX9_URL_SCHEME, JX9_URL_HOST, JX9_URL_PORT, JX9_URL_USER - * JX9_URL_PASS, JX9_URL_PATH, JX9_URL_QUERY or JX9_URL_FRAGMENT to retrieve - * just a specific URL component as a string (except when JX9_URL_PORT is given - * in which case the return value will be an integer). - * Return - * If the component parameter is omitted, an associative array is returned. - * At least one element will be present within the array. Potential keys within - * this array are: - * scheme - e.g. http - * host - * port - * user - * pass - * path - * query - after the question mark ? - * fragment - after the hashmark # - * Note: - * FALSE is returned on failure. - * This function work with relative URL unlike the one shipped - * with the standard JX9 engine. - */ -static int vm_builtin_parse_url(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zStr; /* Input string */ - SyString *pComp; /* Pointer to the URI component */ - SyhttpUri sURI; /* Parse of the given URI */ - int nLen; - sxi32 rc; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract the given URI */ - zStr = jx9_value_to_string(apArg[0], &nLen); - if( nLen < 1 ){ - /* Nothing to process, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Get a parse */ - rc = VmHttpSplitURI(&sURI, zStr, (sxu32)nLen); - if( rc != SXRET_OK ){ - /* Malformed input, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( nArg > 1 ){ - int nComponent = jx9_value_to_int(apArg[1]); - /* Refer to constant.c for constants values */ - switch(nComponent){ - case 1: /* JX9_URL_SCHEME */ - pComp = &sURI.sScheme; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 2: /* JX9_URL_HOST */ - pComp = &sURI.sHost; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 3: /* JX9_URL_PORT */ - pComp = &sURI.sPort; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - int iPort = 0; - /* Cast the value to integer */ - SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0); - jx9_result_int(pCtx, iPort); - } - break; - case 4: /* JX9_URL_USER */ - pComp = &sURI.sUser; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 5: /* JX9_URL_PASS */ - pComp = &sURI.sPass; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 7: /* JX9_URL_QUERY */ - pComp = &sURI.sQuery; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 8: /* JX9_URL_FRAGMENT */ - pComp = &sURI.sFragment; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - case 6: /* JX9_URL_PATH */ - pComp = &sURI.sPath; - if( pComp->nByte < 1 ){ - /* No available value, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); - } - break; - default: - /* No such entry, return NULL */ - jx9_result_null(pCtx); - break; - } - }else{ - jx9_value *pArray, *pValue; - /* Return an associative array */ - pArray = jx9_context_new_array(pCtx); /* Empty array */ - pValue = jx9_context_new_scalar(pCtx); /* Array value */ - if( pArray == 0 || pValue == 0 ){ - /* Out of memory */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "jx9 engine is running out of memory"); - /* Return false */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Fill the array */ - pComp = &sURI.sScheme; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "scheme", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sHost; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "host", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sPort; - if( pComp->nByte > 0 ){ - int iPort = 0;/* cc warning */ - /* Convert to integer */ - SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0); - jx9_value_int(pValue, iPort); - jx9_array_add_strkey_elem(pArray, "port", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sUser; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "user", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sPass; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "pass", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sPath; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "path", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sQuery; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "query", pValue); /* Will make it's own copy */ - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pValue); - pComp = &sURI.sFragment; - if( pComp->nByte > 0 ){ - jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); - jx9_array_add_strkey_elem(pArray, "fragment", pValue); /* Will make it's own copy */ - } - /* Return the created array */ - jx9_result_value(pCtx, pArray); - /* NOTE: - * Don't worry about freeing 'pValue', everything will be released - * automatically as soon we return from this function. - */ - } - /* All done */ - return JX9_OK; -} -/* - * Section: - * Array related routines. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - * Note 2012-5-21 01:04:15: - * Array related functions that need access to the underlying - * virtual machine are implemented here rather than 'hashmap.c' - */ -/* - * The [extract()] function store it's state information in an instance - * of the following structure. - */ -typedef struct extract_aux_data extract_aux_data; -struct extract_aux_data -{ - jx9_vm *pVm; /* VM that own this instance */ - int iCount; /* Number of variables successfully imported */ - const char *zPrefix; /* Prefix name */ - int Prefixlen; /* Prefix length */ - int iFlags; /* Control flags */ - char zWorker[1024]; /* Working buffer */ -}; -/* Forward declaration */ -static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData); -/* - * int extract(array $var_array[, int $extract_type = EXTR_OVERWRITE[, string $prefix = NULL ]]) - * Import variables into the current symbol table from an array. - * Parameters - * $var_array - * An associative array. This function treats keys as variable names and values - * as variable values. For each key/value pair it will create a variable in the current symbol - * table, subject to extract_type and prefix parameters. - * You must use an associative array; a numerically indexed array will not produce results - * unless you use EXTR_PREFIX_ALL or EXTR_PREFIX_INVALID. - * $extract_type - * The way invalid/numeric keys and collisions are treated is determined by the extract_type. - * It can be one of the following values: - * EXTR_OVERWRITE - * If there is a collision, overwrite the existing variable. - * EXTR_SKIP - * If there is a collision, don't overwrite the existing variable. - * EXTR_PREFIX_SAME - * If there is a collision, prefix the variable name with prefix. - * EXTR_PREFIX_ALL - * Prefix all variable names with prefix. - * EXTR_PREFIX_INVALID - * Only prefix invalid/numeric variable names with prefix. - * EXTR_IF_EXISTS - * Only overwrite the variable if it already exists in the current symbol table - * otherwise do nothing. - * This is useful for defining a list of valid variables and then extracting only those - * variables you have defined out of $_REQUEST, for example. - * EXTR_PREFIX_IF_EXISTS - * Only create prefixed variable names if the non-prefixed version of the same variable exists in - * the current symbol table. - * $prefix - * Note that prefix is only required if extract_type is EXTR_PREFIX_SAME, EXTR_PREFIX_ALL - * EXTR_PREFIX_INVALID or EXTR_PREFIX_IF_EXISTS. If the prefixed result is not a valid variable name - * it is not imported into the symbol table. Prefixes are automatically separated from the array key by an - * underscore character. - * Return - * Returns the number of variables successfully imported into the symbol table. - */ -static int vm_builtin_extract(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - extract_aux_data sAux; - jx9_hashmap *pMap; - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing/Invalid arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Point to the target hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry < 1 ){ - /* Empty map, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Prepare the aux data */ - SyZero(&sAux, sizeof(extract_aux_data)-sizeof(sAux.zWorker)); - if( nArg > 1 ){ - sAux.iFlags = jx9_value_to_int(apArg[1]); - if( nArg > 2 ){ - sAux.zPrefix = jx9_value_to_string(apArg[2], &sAux.Prefixlen); - } - } - sAux.pVm = pCtx->pVm; - /* Invoke the worker callback */ - jx9HashmapWalk(pMap, VmExtractCallback, &sAux); - /* Number of variables successfully imported */ - jx9_result_int(pCtx, sAux.iCount); - return JX9_OK; -} -/* - * Worker callback for the [extract()] function defined - * below. - */ -static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData) -{ - extract_aux_data *pAux = (extract_aux_data *)pUserData; - int iFlags = pAux->iFlags; - jx9_vm *pVm = pAux->pVm; - jx9_value *pObj; - SyString sVar; - if( (iFlags & 0x10/* EXTR_PREFIX_INVALID */) && (pKey->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL|MEMOBJ_REAL))){ - iFlags |= 0x08; /*EXTR_PREFIX_ALL*/ - } - /* Perform a string cast */ - jx9MemObjToString(pKey); - if( SyBlobLength(&pKey->sBlob) < 1 ){ - /* Unavailable variable name */ - return SXRET_OK; - } - sVar.nByte = 0; /* cc warning */ - if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/ ) && pAux->Prefixlen > 0 ){ - sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker, sizeof(pAux->zWorker), "%.*s_%.*s", - pAux->Prefixlen, pAux->zPrefix, - SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob) - ); - }else{ - sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob), pAux->zWorker, - SXMIN(SyBlobLength(&pKey->sBlob), sizeof(pAux->zWorker))); - } - sVar.zString = pAux->zWorker; - /* Try to extract the variable */ - pObj = VmExtractMemObj(pVm, &sVar, TRUE, FALSE); - if( pObj ){ - /* Collision */ - if( iFlags & 0x02 /* EXTR_SKIP */ ){ - return SXRET_OK; - } - if( iFlags & 0x04 /* EXTR_PREFIX_SAME */ ){ - if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/) || pAux->Prefixlen < 1){ - /* Already prefixed */ - return SXRET_OK; - } - sVar.nByte = SyBufferFormat( - pAux->zWorker, sizeof(pAux->zWorker), - "%.*s_%.*s", - pAux->Prefixlen, pAux->zPrefix, - SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob) - ); - pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE); - } - }else{ - /* Create the variable */ - pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE); - } - if( pObj ){ - /* Overwrite the old value */ - jx9MemObjStore(pValue, pObj); - /* Increment counter */ - pAux->iCount++; - } - return SXRET_OK; -} -/* - * Compile and evaluate a JX9 chunk at run-time. - * Refer to the include language construct implementation for more - * information. - */ -static sxi32 VmEvalChunk( - jx9_vm *pVm, /* Underlying Virtual Machine */ - jx9_context *pCtx, /* Call Context */ - SyString *pChunk, /* JX9 chunk to evaluate */ - int iFlags, /* Compile flag */ - int bTrueReturn /* TRUE to return execution result */ - ) -{ - SySet *pByteCode, aByteCode; - ProcConsumer xErr = 0; - void *pErrData = 0; - /* Initialize bytecode container */ - SySetInit(&aByteCode, &pVm->sAllocator, sizeof(VmInstr)); - SySetAlloc(&aByteCode, 0x20); - /* Reset the code generator */ - if( bTrueReturn ){ - /* Included file, log compile-time errors */ - xErr = pVm->pEngine->xConf.xErr; - pErrData = pVm->pEngine->xConf.pErrData; - } - jx9ResetCodeGenerator(pVm, xErr, pErrData); - /* Swap bytecode container */ - pByteCode = pVm->pByteContainer; - pVm->pByteContainer = &aByteCode; - /* Compile the chunk */ - jx9CompileScript(pVm, pChunk, iFlags); - if( pVm->sCodeGen.nErr > 0 ){ - /* Compilation error, return false */ - if( pCtx ){ - jx9_result_bool(pCtx, 0); - } - }else{ - jx9_value sResult; /* Return value */ - if( SXRET_OK != jx9VmEmitInstr(pVm, JX9_OP_DONE, 0, 0, 0, 0) ){ - /* Out of memory */ - if( pCtx ){ - jx9_result_bool(pCtx, 0); - } - goto Cleanup; - } - if( bTrueReturn ){ - /* Assume a boolean true return value */ - jx9MemObjInitFromBool(pVm, &sResult, 1); - }else{ - /* Assume a null return value */ - jx9MemObjInit(pVm, &sResult); - } - /* Execute the compiled chunk */ - VmLocalExec(pVm, &aByteCode, &sResult); - if( pCtx ){ - /* Set the execution result */ - jx9_result_value(pCtx, &sResult); - } - jx9MemObjRelease(&sResult); - } -Cleanup: - /* Cleanup the mess left behind */ - pVm->pByteContainer = pByteCode; - SySetRelease(&aByteCode); - return SXRET_OK; -} -/* - * Check if a file path is already included. - */ -static int VmIsIncludedFile(jx9_vm *pVm, SyString *pFile) -{ - SyString *aEntries; - sxu32 n; - aEntries = (SyString *)SySetBasePtr(&pVm->aIncluded); - /* Perform a linear search */ - for( n = 0 ; n < SySetUsed(&pVm->aIncluded) ; ++n ){ - if( SyStringCmp(pFile, &aEntries[n], SyMemcmp) == 0 ){ - /* Already included */ - return TRUE; - } - } - return FALSE; -} -/* - * Push a file path in the appropriate VM container. - */ -JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew) -{ - SyString sPath; - char *zDup; -#ifdef __WINNT__ - char *zCur; -#endif - sxi32 rc; - if( nLen < 0 ){ - nLen = SyStrlen(zPath); - } - /* Duplicate the file path first */ - zDup = SyMemBackendStrDup(&pVm->sAllocator, zPath, nLen); - if( zDup == 0 ){ - return SXERR_MEM; - } -#ifdef __WINNT__ - /* Normalize path on windows - * Example: - * Path/To/File.jx9 - * becomes - * path\to\file.jx9 - */ - zCur = zDup; - while( zCur[0] != 0 ){ - if( zCur[0] == '/' ){ - zCur[0] = '\\'; - }else if( (unsigned char)zCur[0] < 0xc0 && SyisUpper(zCur[0]) ){ - int c = SyToLower(zCur[0]); - zCur[0] = (char)c; /* MSVC stupidity */ - } - zCur++; - } -#endif - /* Install the file path */ - SyStringInitFromBuf(&sPath, zDup, nLen); - if( !bMain ){ - if( VmIsIncludedFile(&(*pVm), &sPath) ){ - /* Already included */ - *pNew = 0; - }else{ - /* Insert in the corresponding container */ - rc = SySetPut(&pVm->aIncluded, (const void *)&sPath); - if( rc != SXRET_OK ){ - SyMemBackendFree(&pVm->sAllocator, zDup); - return rc; - } - *pNew = 1; - } - } - SySetPut(&pVm->aFiles, (const void *)&sPath); - return SXRET_OK; -} -/* - * Compile and Execute a JX9 script at run-time. - * SXRET_OK is returned on sucessful evaluation.Any other return values - * indicates failure. - * Note that the JX9 script to evaluate can be a local or remote file.In - * either cases the [jx9StreamReadWholeFile()] function handle all the underlying - * operations. - * If the [jJX9_DISABLE_BUILTIN_FUNC] compile-time directive is defined, then - * this function is a no-op. - * Refer to the implementation of the include(), import() language - * constructs for more information. - */ -static sxi32 VmExecIncludedFile( - jx9_context *pCtx, /* Call Context */ - SyString *pPath, /* Script path or URL*/ - int IncludeOnce /* TRUE if called from import() or require_once() */ - ) -{ - sxi32 rc; -#ifndef JX9_DISABLE_BUILTIN_FUNC - const jx9_io_stream *pStream; - SyBlob sContents; - void *pHandle; - jx9_vm *pVm; - int isNew; - /* Initialize fields */ - pVm = pCtx->pVm; - SyBlobInit(&sContents, &pVm->sAllocator); - isNew = 0; - /* Extract the associated stream */ - pStream = jx9VmGetStreamDevice(pVm, &pPath->zString, pPath->nByte); - /* - * Open the file or the URL [i.e: http://jx9.symisc.net/example/hello.jx9.txt"] - * in a read-only mode. - */ - pHandle = jx9StreamOpenHandle(pVm, pStream,pPath->zString, JX9_IO_OPEN_RDONLY, TRUE, 0, TRUE, &isNew); - if( pHandle == 0 ){ - return SXERR_IO; - } - rc = SXRET_OK; /* Stupid cc warning */ - if( IncludeOnce && !isNew ){ - /* Already included */ - rc = SXERR_EXISTS; - }else{ - /* Read the whole file contents */ - rc = jx9StreamReadWholeFile(pHandle, pStream, &sContents); - if( rc == SXRET_OK ){ - SyString sScript; - /* Compile and execute the script */ - SyStringInitFromBuf(&sScript, SyBlobData(&sContents), SyBlobLength(&sContents)); - VmEvalChunk(pCtx->pVm, &(*pCtx), &sScript, 0, TRUE); - } - } - /* Pop from the set of included file */ - (void)SySetPop(&pVm->aFiles); - /* Close the handle */ - jx9StreamCloseHandle(pStream, pHandle); - /* Release the working buffer */ - SyBlobRelease(&sContents); -#else - pCtx = 0; /* cc warning */ - pPath = 0; - IncludeOnce = 0; - rc = SXERR_IO; -#endif /* JX9_DISABLE_BUILTIN_FUNC */ - return rc; -} -/* * include: - * According to the JX9 reference manual. - * The include() function includes and evaluates the specified file. - * Files are included based on the file path given or, if none is given - * the include_path specified.If the file isn't found in the include_path - * include() will finally check in the calling script's own directory - * and the current working directory before failing. The include() - * construct will emit a warning if it cannot find a file; this is different - * behavior from require(), which will emit a fatal error. - * If a path is defined — whether absolute (starting with a drive letter - * or \ on Windows, or / on Unix/Linux systems) or relative to the current - * directory (starting with . or ..) — the include_path will be ignored altogether. - * For example, if a filename begins with ../, the parser will look in the parent - * directory to find the requested file. - * When a file is included, the code it contains inherits the variable scope - * of the line on which the include occurs. Any variables available at that line - * in the calling file will be available within the called file, from that point forward. - * However, all functions and objectes defined in the included file have the global scope. - */ -static int vm_builtin_include(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyString sFile; - sxi32 rc; - if( nArg < 1 ){ - /* Nothing to evaluate, return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* File to include */ - sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte); - if( sFile.nByte < 1 ){ - /* Empty string, return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* Open, compile and execute the desired script */ - rc = VmExecIncludedFile(&(*pCtx), &sFile, FALSE); - if( rc != SXRET_OK ){ - /* Emit a warning and return false */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile); - jx9_result_bool(pCtx, 0); - } - return SXRET_OK; -} -/* - * import: - * According to the JX9 reference manual. - * The import() statement includes and evaluates the specified file during - * the execution of the script. This is a behavior similar to the include() - * statement, with the only difference being that if the code from a file has already - * been included, it will not be included again. As the name suggests, it will be included - * just once. - */ -static int vm_builtin_import(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - SyString sFile; - sxi32 rc; - if( nArg < 1 ){ - /* Nothing to evaluate, return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* File to include */ - sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte); - if( sFile.nByte < 1 ){ - /* Empty string, return NULL */ - jx9_result_null(pCtx); - return SXRET_OK; - } - /* Open, compile and execute the desired script */ - rc = VmExecIncludedFile(&(*pCtx), &sFile, TRUE); - if( rc == SXERR_EXISTS ){ - /* File already included, return TRUE */ - jx9_result_bool(pCtx, 1); - return SXRET_OK; - } - if( rc != SXRET_OK ){ - /* Emit a warning and return false */ - jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile); - jx9_result_bool(pCtx, 0); - } - return SXRET_OK; -} -/* - * Section: - * Command line arguments processing. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ -/* - * Check if a short option argument [i.e: -c] is available in the command - * line string. Return a pointer to the start of the stream on success. - * NULL otherwise. - */ -static const char * VmFindShortOpt(int c, const char *zIn, const char *zEnd) -{ - while( zIn < zEnd ){ - if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == c ){ - /* Got one */ - return &zIn[1]; - } - /* Advance the cursor */ - zIn++; - } - /* No such option */ - return 0; -} -/* - * Check if a long option argument [i.e: --opt] is available in the command - * line string. Return a pointer to the start of the stream on success. - * NULL otherwise. - */ -static const char * VmFindLongOpt(const char *zLong, int nByte, const char *zIn, const char *zEnd) -{ - const char *zOpt; - while( zIn < zEnd ){ - if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == '-' ){ - zIn += 2; - zOpt = zIn; - while( zIn < zEnd && !SyisSpace(zIn[0]) ){ - if( zIn[0] == '=' /* --opt=val */){ - break; - } - zIn++; - } - /* Test */ - if( (int)(zIn-zOpt) == nByte && SyMemcmp(zOpt, zLong, nByte) == 0 ){ - /* Got one, return it's value */ - return zIn; - } - - }else{ - zIn++; - } - } - /* No such option */ - return 0; -} -/* - * Long option [i.e: --opt] arguments private data structure. - */ -struct getopt_long_opt -{ - const char *zArgIn, *zArgEnd; /* Command line arguments */ - jx9_value *pWorker; /* Worker variable*/ - jx9_value *pArray; /* getopt() return value */ - jx9_context *pCtx; /* Call Context */ -}; -/* Forward declaration */ -static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData); -/* - * Extract short or long argument option values. - */ -static void VmExtractOptArgValue( - jx9_value *pArray, /* getopt() return value */ - jx9_value *pWorker, /* Worker variable */ - const char *zArg, /* Argument stream */ - const char *zArgEnd, /* End of the argument stream */ - int need_val, /* TRUE to fetch option argument */ - jx9_context *pCtx, /* Call Context */ - const char *zName /* Option name */) -{ - jx9_value_bool(pWorker, 0); - if( !need_val ){ - /* - * Option does not need arguments. - * Insert the option name and a boolean FALSE. - */ - jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */ - }else{ - const char *zCur; - /* Extract option argument */ - zArg++; - if( zArg < zArgEnd && zArg[0] == '=' ){ - zArg++; - } - while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ - zArg++; - } - if( zArg >= zArgEnd || zArg[0] == '-' ){ - /* - * Argument not found. - * Insert the option name and a boolean FALSE. - */ - jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */ - return; - } - /* Delimit the value */ - zCur = zArg; - if( zArg[0] == '\'' || zArg[0] == '"' ){ - int d = zArg[0]; - /* Delimt the argument */ - zArg++; - zCur = zArg; - while( zArg < zArgEnd ){ - if( zArg[0] == d && zArg[-1] != '\\' ){ - /* Delimiter found, exit the loop */ - break; - } - zArg++; - } - /* Save the value */ - jx9_value_string(pWorker, zCur, (int)(zArg-zCur)); - if( zArg < zArgEnd ){ zArg++; } - }else{ - while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){ - zArg++; - } - /* Save the value */ - jx9_value_string(pWorker, zCur, (int)(zArg-zCur)); - } - /* - * Check if we are dealing with multiple values. - * If so, create an array to hold them, rather than a scalar variable. - */ - while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ - zArg++; - } - if( zArg < zArgEnd && zArg[0] != '-' ){ - jx9_value *pOptArg; /* Array of option arguments */ - pOptArg = jx9_context_new_array(pCtx); - if( pOptArg == 0 ){ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); - }else{ - /* Insert the first value */ - jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */ - for(;;){ - if( zArg >= zArgEnd || zArg[0] == '-' ){ - /* No more value */ - break; - } - /* Delimit the value */ - zCur = zArg; - if( zArg < zArgEnd && zArg[0] == '\\' ){ - zArg++; - zCur = zArg; - } - while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){ - zArg++; - } - /* Reset the string cursor */ - jx9_value_reset_string_cursor(pWorker); - /* Save the value */ - jx9_value_string(pWorker, zCur, (int)(zArg-zCur)); - /* Insert */ - jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */ - /* Jump trailing white spaces */ - while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ - zArg++; - } - } - /* Insert the option arg array */ - jx9_array_add_strkey_elem(pArray, (const char *)zName, pOptArg); /* Will make it's own copy */ - /* Safely release */ - jx9_context_release_value(pCtx, pOptArg); - } - }else{ - /* Single value */ - jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */ - } - } -} -/* - * array getopt(string $options[, array $longopts ]) - * Gets options from the command line argument list. - * Parameters - * $options - * Each character in this string will be used as option characters - * and matched against options passed to the script starting with - * a single hyphen (-). For example, an option string "x" recognizes - * an option -x. Only a-z, A-Z and 0-9 are allowed. - * $longopts - * An array of options. Each element in this array will be used as option - * strings and matched against options passed to the script starting with - * two hyphens (--). For example, an longopts element "opt" recognizes an - * option --opt. - * Return - * This function will return an array of option / argument pairs or FALSE - * on failure. - */ -static int vm_builtin_getopt(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zIn, *zEnd, *zArg, *zArgIn, *zArgEnd; - struct getopt_long_opt sLong; - jx9_value *pArray, *pWorker; - SyBlob *pArg; - int nByte; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Missing/Invalid option arguments"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Extract option arguments */ - zIn = jx9_value_to_string(apArg[0], &nByte); - zEnd = &zIn[nByte]; - /* Point to the string representation of the $argv[] array */ - pArg = &pCtx->pVm->sArgv; - /* Create a new empty array and a worker variable */ - pArray = jx9_context_new_array(pCtx); - pWorker = jx9_context_new_scalar(pCtx); - if( pArray == 0 || pWorker == 0 ){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR, "JX9 is running out of memory"); - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - if( SyBlobLength(pArg) < 1 ){ - /* Empty command line, return the empty array*/ - jx9_result_value(pCtx, pArray); - /* Everything will be released automatically when we return - * from this function. - */ - return JX9_OK; - } - zArgIn = (const char *)SyBlobData(pArg); - zArgEnd = &zArgIn[SyBlobLength(pArg)]; - /* Fill the long option structure */ - sLong.pArray = pArray; - sLong.pWorker = pWorker; - sLong.zArgIn = zArgIn; - sLong.zArgEnd = zArgEnd; - sLong.pCtx = pCtx; - /* Start processing */ - while( zIn < zEnd ){ - int c = zIn[0]; - int need_val = 0; - /* Advance the stream cursor */ - zIn++; - /* Ignore non-alphanum characters */ - if( !SyisAlphaNum(c) ){ - continue; - } - if( zIn < zEnd && zIn[0] == ':' ){ - zIn++; - need_val = 1; - if( zIn < zEnd && zIn[0] == ':' ){ - zIn++; - } - } - /* Find option */ - zArg = VmFindShortOpt(c, zArgIn, zArgEnd); - if( zArg == 0 ){ - /* No such option */ - continue; - } - /* Extract option argument value */ - VmExtractOptArgValue(pArray, pWorker, zArg, zArgEnd, need_val, pCtx, (const char *)&c); - } - if( nArg > 1 && jx9_value_is_json_array(apArg[1]) && jx9_array_count(apArg[1]) > 0 ){ - /* Process long options */ - jx9_array_walk(apArg[1], VmProcessLongOpt, &sLong); - } - /* Return the option array */ - jx9_result_value(pCtx, pArray); - /* - * Don't worry about freeing memory, everything will be released - * automatically as soon we return from this foreign function. - */ - return JX9_OK; -} -/* - * Array walker callback used for processing long options values. - */ -static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData) -{ - struct getopt_long_opt *pOpt = (struct getopt_long_opt *)pUserData; - const char *zArg, *zOpt, *zEnd; - int need_value = 0; - int nByte; - /* Value must be of type string */ - if( !jx9_value_is_string(pValue) ){ - /* Simply ignore */ - return JX9_OK; - } - zOpt = jx9_value_to_string(pValue, &nByte); - if( nByte < 1 ){ - /* Empty string, ignore */ - return JX9_OK; - } - zEnd = &zOpt[nByte - 1]; - if( zEnd[0] == ':' ){ - char *zTerm; - /* Try to extract a value */ - need_value = 1; - while( zEnd >= zOpt && zEnd[0] == ':' ){ - zEnd--; - } - if( zOpt >= zEnd ){ - /* Empty string, ignore */ - SXUNUSED(pKey); - return JX9_OK; - } - zEnd++; - zTerm = (char *)zEnd; - zTerm[0] = 0; - }else{ - zEnd = &zOpt[nByte]; - } - /* Find the option */ - zArg = VmFindLongOpt(zOpt, (int)(zEnd-zOpt), pOpt->zArgIn, pOpt->zArgEnd); - if( zArg == 0 ){ - /* No such option, return immediately */ - return JX9_OK; - } - /* Try to extract a value */ - VmExtractOptArgValue(pOpt->pArray, pOpt->pWorker, zArg, pOpt->zArgEnd, need_value, pOpt->pCtx, zOpt); - return JX9_OK; -} -/* - * int utf8_encode(string $input) - * UTF-8 encoding. - * This function encodes the string data to UTF-8, and returns the encoded version. - * UTF-8 is a standard mechanism used by Unicode for encoding wide character values - * into a byte stream. UTF-8 is transparent to plain ASCII characters, is self-synchronized - * (meaning it is possible for a program to figure out where in the bytestream characters start) - * and can be used with normal string comparison functions for sorting and such. - * Notes on UTF-8 (According to SQLite3 authors): - * Byte-0 Byte-1 Byte-2 Byte-3 Value - * 0xxxxxxx 00000000 00000000 0xxxxxxx - * 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx - * 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx - * 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx - * Parameters - * $input - * String to encode or NULL on failure. - * Return - * An UTF-8 encoded string. - */ -static int vm_builtin_utf8_encode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nByte, c, e; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte); - if( nByte < 1 ){ - /* Empty string, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - zEnd = &zIn[nByte]; - /* Start the encoding process */ - for(;;){ - if( zIn >= zEnd ){ - /* End of input */ - break; - } - c = zIn[0]; - /* Advance the stream cursor */ - zIn++; - /* Encode */ - if( c<0x00080 ){ - e = (c&0xFF); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - }else if( c<0x00800 ){ - e = 0xC0 + ((c>>6)&0x1F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + (c & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - }else if( c<0x10000 ){ - e = 0xE0 + ((c>>12)&0x0F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + ((c>>6) & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + (c & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - }else{ - e = 0xF0 + ((c>>18) & 0x07); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + ((c>>12) & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + ((c>>6) & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - e = 0x80 + (c & 0x3F); - jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char)); - } - } - /* All done */ - return JX9_OK; -} -/* - * UTF-8 decoding routine extracted from the sqlite3 source tree. - * Original author: D. Richard Hipp (http://www.sqlite.org) - * Status: Public Domain - */ -/* -** This lookup table is used to help decode the first byte of -** a multi-byte UTF8 character. -*/ -static const unsigned char UtfTrans1[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, -}; -/* -** Translate a single UTF-8 character. Return the unicode value. -** -** During translation, assume that the byte that zTerm points -** is a 0x00. -** -** Write a pointer to the next unread byte back into *pzNext. -** -** Notes On Invalid UTF-8: -** -** * This routine never allows a 7-bit character (0x00 through 0x7f) to -** be encoded as a multi-byte character. Any multi-byte character that -** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd. -** -** * This routine never allows a UTF16 surrogate value to be encoded. -** If a multi-byte character attempts to encode a value between -** 0xd800 and 0xe000 then it is rendered as 0xfffd. -** -** * Bytes in the range of 0x80 through 0xbf which occur as the first -** byte of a character are interpreted as single-byte characters -** and rendered as themselves even though they are technically -** invalid characters. -** -** * This routine accepts an infinite number of different UTF8 encodings -** for unicode values 0x80 and greater. It do not change over-length -** encodings to 0xfffd as some systems recommend. -*/ -#define READ_UTF8(zIn, zTerm, c) \ - c = *(zIn++); \ - if( c>=0xc0 ){ \ - c = UtfTrans1[c-0xc0]; \ - while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ - c = (c<<6) + (0x3f & *(zIn++)); \ - } \ - if( c<0x80 \ - || (c&0xFFFFF800)==0xD800 \ - || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ - } -JX9_PRIVATE int jx9Utf8Read( - const unsigned char *z, /* First byte of UTF-8 character */ - const unsigned char *zTerm, /* Pretend this byte is 0x00 */ - const unsigned char **pzNext /* Write first byte past UTF-8 char here */ -){ - int c; - READ_UTF8(z, zTerm, c); - *pzNext = z; - return c; -} -/* - * string utf8_decode(string $data) - * This function decodes data, assumed to be UTF-8 encoded, to unicode. - * Parameters - * data - * An UTF-8 encoded string. - * Return - * Unicode decoded string or NULL on failure. - */ -static int vm_builtin_utf8_decode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const unsigned char *zIn, *zEnd; - int nByte, c; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the target string */ - zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte); - if( nByte < 1 ){ - /* Empty string, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - zEnd = &zIn[nByte]; - /* Start the decoding process */ - while( zIn < zEnd ){ - c = jx9Utf8Read(zIn, zEnd, &zIn); - if( c == 0x0 ){ - break; - } - jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - } - return JX9_OK; -} -/* - * string json_encode(mixed $value) - * Returns a string containing the JSON representation of value. - * Parameters - * $value - * The value being encoded. Can be any type except a resource. - * Return - * Returns a JSON encoded string on success. FALSE otherwise - */ -static int vm_builtin_json_encode(jx9_context *pCtx,int nArg,jx9_value **apArg) -{ - SyBlob sBlob; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Init the working buffer */ - SyBlobInit(&sBlob,&pCtx->pVm->sAllocator); - /* Perform the encoding operation */ - jx9JsonSerialize(apArg[0],&sBlob); - /* Return the serialized value */ - jx9_result_string(pCtx,(const char *)SyBlobData(&sBlob),(int)SyBlobLength(&sBlob)); - /* Cleanup */ - SyBlobRelease(&sBlob); - /* All done */ - return JX9_OK; -} -/* - * mixed json_decode(string $json) - * Takes a JSON encoded string and converts it into a JX9 variable. - * Parameters - * $json - * The json string being decoded. - * Return - * The value encoded in json in appropriate JX9 type. Values true, false and null (case-insensitive) - * are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded - * or if the encoded data is deeper than the recursion limit. - */ -static int vm_builtin_json_decode(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - const char *zJSON; - int nByte; - if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ - /* Missing/Invalid arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the JSON string */ - zJSON = jx9_value_to_string(apArg[0], &nByte); - if( nByte < 1 ){ - /* Empty string, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Decode the raw JSON */ - jx9JsonDecode(pCtx,zJSON,nByte); - return JX9_OK; -} -/* Table of built-in VM functions. */ -static const jx9_builtin_func aVmFunc[] = { - /* JSON Encoding/Decoding */ - { "json_encode", vm_builtin_json_encode }, - { "json_decode", vm_builtin_json_decode }, - /* Functions calls */ - { "func_num_args" , vm_builtin_func_num_args }, - { "func_get_arg" , vm_builtin_func_get_arg }, - { "func_get_args" , vm_builtin_func_get_args }, - { "function_exists", vm_builtin_func_exists }, - { "is_callable" , vm_builtin_is_callable }, - { "get_defined_functions", vm_builtin_get_defined_func }, - /* Constants management */ - { "defined", vm_builtin_defined }, - { "get_defined_constants", vm_builtin_get_defined_constants }, - /* Random numbers/strings generators */ - { "rand", vm_builtin_rand }, - { "rand_str", vm_builtin_rand_str }, - { "getrandmax", vm_builtin_getrandmax }, - /* Language constructs functions */ - { "print", vm_builtin_print }, - { "exit", vm_builtin_exit }, - { "die", vm_builtin_exit }, - /* Variable handling functions */ - { "gettype", vm_builtin_gettype }, - { "get_resource_type", vm_builtin_get_resource_type}, - /* Variable dumping */ - { "dump", vm_builtin_dump }, - /* Release info */ - {"jx9_version", vm_builtin_jx9_version }, - {"jx9_credits", vm_builtin_jx9_version }, - {"jx9_info", vm_builtin_jx9_version }, - {"jx9_copyright", vm_builtin_jx9_version }, - /* hashmap */ - {"extract", vm_builtin_extract }, - /* URL related function */ - {"parse_url", vm_builtin_parse_url }, - /* UTF-8 encoding/decoding */ - {"utf8_encode", vm_builtin_utf8_encode}, - {"utf8_decode", vm_builtin_utf8_decode}, - /* Command line processing */ - {"getopt", vm_builtin_getopt }, - /* Files/URI inclusion facility */ - { "include", vm_builtin_include }, - { "import", vm_builtin_import } -}; -/* - * Register the built-in VM functions defined above. - */ -static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm) -{ - sxi32 rc; - sxu32 n; - for( n = 0 ; n < SX_ARRAYSIZE(aVmFunc) ; ++n ){ - /* Note that these special functions have access - * to the underlying virtual machine as their - * private data. - */ - rc = jx9_create_function(&(*pVm), aVmFunc[n].zName, aVmFunc[n].xFunc, &(*pVm)); - if( rc != SXRET_OK ){ - return rc; - } - } - return SXRET_OK; -} -#ifndef JX9_DISABLE_BUILTIN_FUNC -/* - * Extract the IO stream device associated with a given scheme. - * Return a pointer to an instance of jx9_io_stream when the scheme - * have an associated IO stream registered with it. NULL otherwise. - * If no scheme:// is avalilable then the file:// scheme is assumed. - * For more information on how to register IO stream devices, please - * refer to the official documentation. - */ -JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice( - jx9_vm *pVm, /* Target VM */ - const char **pzDevice, /* Full path, URI, ... */ - int nByte /* *pzDevice length*/ - ) -{ - const char *zIn, *zEnd, *zCur, *zNext; - jx9_io_stream **apStream, *pStream; - SyString sDev, sCur; - sxu32 n, nEntry; - int rc; - /* Check if a scheme [i.e: file://, http://, zip://...] is available */ - zNext = zCur = zIn = *pzDevice; - zEnd = &zIn[nByte]; - while( zIn < zEnd ){ - if( zIn < &zEnd[-3]/*://*/ && zIn[0] == ':' && zIn[1] == '/' && zIn[2] == '/' ){ - /* Got one */ - zNext = &zIn[sizeof("://")-1]; - break; - } - /* Advance the cursor */ - zIn++; - } - if( zIn >= zEnd ){ - /* No such scheme, return the default stream */ - return pVm->pDefStream; - } - SyStringInitFromBuf(&sDev, zCur, zIn-zCur); - /* Remove leading and trailing white spaces */ - SyStringFullTrim(&sDev); - /* Perform a linear lookup on the installed stream devices */ - apStream = (jx9_io_stream **)SySetBasePtr(&pVm->aIOstream); - nEntry = SySetUsed(&pVm->aIOstream); - for( n = 0 ; n < nEntry ; n++ ){ - pStream = apStream[n]; - SyStringInitFromBuf(&sCur, pStream->zName, SyStrlen(pStream->zName)); - /* Perfrom a case-insensitive comparison */ - rc = SyStringCmp(&sDev, &sCur, SyStrnicmp); - if( rc == 0 ){ - /* Stream device found */ - *pzDevice = zNext; - return pStream; - } - } - /* No such stream, return NULL */ - return 0; -} -#endif /* JX9_DISABLE_BUILTIN_FUNC */ -/* - * Section: - * HTTP/URI related routines. - * Authors: - * Symisc Systems, devel@symisc.net. - * Copyright (C) Symisc Systems, http://jx9.symisc.net - * Status: - * Stable. - */ - /* - * URI Parser: Split an URI into components [i.e: Host, Path, Query, ...]. - * URI syntax: [method:/][/[user[:pwd]@]host[:port]/][document] - * This almost, but not quite, RFC1738 URI syntax. - * This routine is not a validator, it does not check for validity - * nor decode URI parts, the only thing this routine does is splitting - * the input to its fields. - * Upper layer are responsible of decoding and validating URI parts. - * On success, this function populate the "SyhttpUri" structure passed - * as the first argument. Otherwise SXERR_* is returned when a malformed - * input is encountered. - */ - static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen) - { - const char *zEnd = &zUri[nLen]; - sxu8 bHostOnly = FALSE; - sxu8 bIPv6 = FALSE ; - const char *zCur; - SyString *pComp; - sxu32 nPos = 0; - sxi32 rc; - /* Zero the structure first */ - SyZero(pOut, sizeof(SyhttpUri)); - /* Remove leading and trailing white spaces */ - SyStringInitFromBuf(&pOut->sRaw, zUri, nLen); - SyStringFullTrim(&pOut->sRaw); - /* Find the first '/' separator */ - rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos); - if( rc != SXRET_OK ){ - /* Assume a host name only */ - zCur = zEnd; - bHostOnly = TRUE; - goto ProcessHost; - } - zCur = &zUri[nPos]; - if( zUri != zCur && zCur[-1] == ':' ){ - /* Extract a scheme: - * Not that we can get an invalid scheme here. - * Fortunately the caller can discard any URI by comparing this scheme with its - * registered schemes and will report the error as soon as his comparison function - * fail. - */ - pComp = &pOut->sScheme; - SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri - 1)); - SyStringLeftTrim(pComp); - } - if( zCur[1] != '/' ){ - if( zCur == zUri || zCur[-1] == ':' ){ - /* No authority */ - goto PathSplit; - } - /* There is something here , we will assume its an authority - * and someone has forgot the two prefix slashes "//", - * sooner or later we will detect if we are dealing with a malicious - * user or not, but now assume we are dealing with an authority - * and let the caller handle all the validation process. - */ - goto ProcessHost; - } - zUri = &zCur[2]; - zCur = zEnd; - rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos); - if( rc == SXRET_OK ){ - zCur = &zUri[nPos]; - } - ProcessHost: - /* Extract user information if present */ - rc = SyByteFind(zUri, (sxu32)(zCur - zUri), '@', &nPos); - if( rc == SXRET_OK ){ - if( nPos > 0 ){ - sxu32 nPassOfft; /* Password offset */ - pComp = &pOut->sUser; - SyStringInitFromBuf(pComp, zUri, nPos); - /* Extract the password if available */ - rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPassOfft); - if( rc == SXRET_OK && nPassOfft < nPos){ - pComp->nByte = nPassOfft; - pComp = &pOut->sPass; - pComp->zString = &zUri[nPassOfft+sizeof(char)]; - pComp->nByte = nPos - nPassOfft - 1; - } - /* Update the cursor */ - zUri = &zUri[nPos+1]; - }else{ - zUri++; - } - } - pComp = &pOut->sHost; - while( zUri < zCur && SyisSpace(zUri[0])){ - zUri++; - } - SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri)); - if( pComp->zString[0] == '[' ){ - /* An IPv6 Address: Make a simple naive test - */ - zUri++; pComp->zString++; pComp->nByte = 0; - while( ((unsigned char)zUri[0] < 0xc0 && SyisHex(zUri[0])) || zUri[0] == ':' ){ - zUri++; pComp->nByte++; - } - if( zUri[0] != ']' ){ - return SXERR_CORRUPT; /* Malformed IPv6 address */ - } - zUri++; - bIPv6 = TRUE; - } - /* Extract a port number if available */ - rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPos); - if( rc == SXRET_OK ){ - if( bIPv6 == FALSE ){ - pComp->nByte = (sxu32)(&zUri[nPos] - zUri); - } - pComp = &pOut->sPort; - SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zCur - &zUri[nPos+1])); - } - if( bHostOnly == TRUE ){ - return SXRET_OK; - } -PathSplit: - zUri = zCur; - pComp = &pOut->sPath; - SyStringInitFromBuf(pComp, zUri, (sxu32)(zEnd-zUri)); - if( pComp->nByte == 0 ){ - return SXRET_OK; /* Empty path */ - } - if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '?', &nPos) ){ - pComp->nByte = nPos; /* Update path length */ - pComp = &pOut->sQuery; - SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1])); - } - if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '#', &nPos) ){ - /* Update path or query length */ - if( pComp == &pOut->sPath ){ - pComp->nByte = nPos; - }else{ - if( &zUri[nPos] < (char *)SyStringData(pComp) ){ - /* Malformed syntax : Query must be present before fragment */ - return SXERR_SYNTAX; - } - pComp->nByte -= (sxu32)(zEnd - &zUri[nPos]); - } - pComp = &pOut->sFragment; - SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1])) - } - return SXRET_OK; - } - /* - * Extract a single line from a raw HTTP request. - * Return SXRET_OK on success, SXERR_EOF when end of input - * and SXERR_MORE when more input is needed. - */ -static sxi32 VmGetNextLine(SyString *pCursor, SyString *pCurrent) -{ - const char *zIn; - sxu32 nPos; - /* Jump leading white spaces */ - SyStringLeftTrim(pCursor); - if( pCursor->nByte < 1 ){ - SyStringInitFromBuf(pCurrent, 0, 0); - return SXERR_EOF; /* End of input */ - } - zIn = SyStringData(pCursor); - if( SXRET_OK != SyByteListFind(pCursor->zString, pCursor->nByte, "\r\n", &nPos) ){ - /* Line not found, tell the caller to read more input from source */ - SyStringDupPtr(pCurrent, pCursor); - return SXERR_MORE; - } - pCurrent->zString = zIn; - pCurrent->nByte = nPos; - /* advance the cursor so we can call this routine again */ - pCursor->zString = &zIn[nPos]; - pCursor->nByte -= nPos; - return SXRET_OK; - } - /* - * Split a single MIME header into a name value pair. - * This function return SXRET_OK, SXERR_CONTINUE on success. - * Otherwise SXERR_NEXT is returned when a malformed header - * is encountered. - * Note: This function handle also mult-line headers. - */ - static sxi32 VmHttpProcessOneHeader(SyhttpHeader *pHdr, SyhttpHeader *pLast, const char *zLine, sxu32 nLen) - { - SyString *pName; - sxu32 nPos; - sxi32 rc; - if( nLen < 1 ){ - return SXERR_NEXT; - } - /* Check for multi-line header */ - if( pLast && (zLine[-1] == ' ' || zLine[-1] == '\t') ){ - SyString *pTmp = &pLast->sValue; - SyStringFullTrim(pTmp); - if( pTmp->nByte == 0 ){ - SyStringInitFromBuf(pTmp, zLine, nLen); - }else{ - /* Update header value length */ - pTmp->nByte = (sxu32)(&zLine[nLen] - pTmp->zString); - } - /* Simply tell the caller to reset its states and get another line */ - return SXERR_CONTINUE; - } - /* Split the header */ - pName = &pHdr->sName; - rc = SyByteFind(zLine, nLen, ':', &nPos); - if(rc != SXRET_OK ){ - return SXERR_NEXT; /* Malformed header;Check the next entry */ - } - SyStringInitFromBuf(pName, zLine, nPos); - SyStringFullTrim(pName); - /* Extract a header value */ - SyStringInitFromBuf(&pHdr->sValue, &zLine[nPos + 1], nLen - nPos - 1); - /* Remove leading and trailing whitespaces */ - SyStringFullTrim(&pHdr->sValue); - return SXRET_OK; - } - /* - * Extract all MIME headers associated with a HTTP request. - * After processing the first line of a HTTP request, the following - * routine is called in order to extract MIME headers. - * This function return SXRET_OK on success, SXERR_MORE when it needs - * more inputs. - * Note: Any malformed header is simply discarded. - */ - static sxi32 VmHttpExtractHeaders(SyString *pRequest, SySet *pOut) - { - SyhttpHeader *pLast = 0; - SyString sCurrent; - SyhttpHeader sHdr; - sxu8 bEol; - sxi32 rc; - if( SySetUsed(pOut) > 0 ){ - pLast = (SyhttpHeader *)SySetAt(pOut, SySetUsed(pOut)-1); - } - bEol = FALSE; - for(;;){ - SyZero(&sHdr, sizeof(SyhttpHeader)); - /* Extract a single line from the raw HTTP request */ - rc = VmGetNextLine(pRequest, &sCurrent); - if(rc != SXRET_OK ){ - if( sCurrent.nByte < 1 ){ - break; - } - bEol = TRUE; - } - /* Process the header */ - if( SXRET_OK == VmHttpProcessOneHeader(&sHdr, pLast, sCurrent.zString, sCurrent.nByte)){ - if( SXRET_OK != SySetPut(pOut, (const void *)&sHdr) ){ - break; - } - /* Retrieve the last parsed header so we can handle multi-line header - * in case we face one of them. - */ - pLast = (SyhttpHeader *)SySetPeek(pOut); - } - if( bEol ){ - break; - } - } /* for(;;) */ - return SXRET_OK; - } - /* - * Process the first line of a HTTP request. - * This routine perform the following operations - * 1) Extract the HTTP method. - * 2) Split the request URI to it's fields [ie: host, path, query, ...]. - * 3) Extract the HTTP protocol version. - */ - static sxi32 VmHttpProcessFirstLine( - SyString *pRequest, /* Raw HTTP request */ - sxi32 *pMethod, /* OUT: HTTP method */ - SyhttpUri *pUri, /* OUT: Parse of the URI */ - sxi32 *pProto /* OUT: HTTP protocol */ - ) - { - static const char *azMethods[] = { "get", "post", "head", "put"}; - static const sxi32 aMethods[] = { HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_HEAD, HTTP_METHOD_PUT}; - const char *zIn, *zEnd, *zPtr; - SyString sLine; - sxu32 nLen; - sxi32 rc; - /* Extract the first line and update the pointer */ - rc = VmGetNextLine(pRequest, &sLine); - if( rc != SXRET_OK ){ - return rc; - } - if ( sLine.nByte < 1 ){ - /* Empty HTTP request */ - return SXERR_EMPTY; - } - /* Delimit the line and ignore trailing and leading white spaces */ - zIn = sLine.zString; - zEnd = &zIn[sLine.nByte]; - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ - zIn++; - } - /* Extract the HTTP method */ - zPtr = zIn; - while( zIn < zEnd && !SyisSpace(zIn[0]) ){ - zIn++; - } - *pMethod = HTTP_METHOD_OTHR; - if( zIn > zPtr ){ - sxu32 i; - nLen = (sxu32)(zIn-zPtr); - for( i = 0 ; i < SX_ARRAYSIZE(azMethods) ; ++i ){ - if( SyStrnicmp(azMethods[i], zPtr, nLen) == 0 ){ - *pMethod = aMethods[i]; - break; - } - } - } - /* Jump trailing white spaces */ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ - zIn++; - } - /* Extract the request URI */ - zPtr = zIn; - while( zIn < zEnd && !SyisSpace(zIn[0]) ){ - zIn++; - } - if( zIn > zPtr ){ - nLen = (sxu32)(zIn-zPtr); - /* Split raw URI to it's fields */ - VmHttpSplitURI(pUri, zPtr, nLen); - } - /* Jump trailing white spaces */ - while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ - zIn++; - } - /* Extract the HTTP version */ - zPtr = zIn; - while( zIn < zEnd && !SyisSpace(zIn[0]) ){ - zIn++; - } - *pProto = HTTP_PROTO_11; /* HTTP/1.1 */ - rc = 1; - if( zIn > zPtr ){ - rc = SyStrnicmp(zPtr, "http/1.0", (sxu32)(zIn-zPtr)); - } - if( !rc ){ - *pProto = HTTP_PROTO_10; /* HTTP/1.0 */ - } - return SXRET_OK; - } - /* - * Tokenize, decode and split a raw query encoded as: "x-www-form-urlencoded" - * into a name value pair. - * Note that this encoding is implicit in GET based requests. - * After the tokenization process, register the decoded queries - * in the $_GET/$_POST/$_REQUEST superglobals arrays. - */ - static sxi32 VmHttpSplitEncodedQuery( - jx9_vm *pVm, /* Target VM */ - SyString *pQuery, /* Raw query to decode */ - SyBlob *pWorker, /* Working buffer */ - int is_post /* TRUE if we are dealing with a POST request */ - ) - { - const char *zEnd = &pQuery->zString[pQuery->nByte]; - const char *zIn = pQuery->zString; - jx9_value *pGet, *pRequest; - SyString sName, sValue; - const char *zPtr; - sxu32 nBlobOfft; - /* Extract superglobals */ - if( is_post ){ - /* $_POST superglobal */ - pGet = VmExtractSuper(&(*pVm), "_POST", sizeof("_POST")-1); - }else{ - /* $_GET superglobal */ - pGet = VmExtractSuper(&(*pVm), "_GET", sizeof("_GET")-1); - } - pRequest = VmExtractSuper(&(*pVm), "_REQUEST", sizeof("_REQUEST")-1); - /* Split up the raw query */ - for(;;){ - /* Jump leading white spaces */ - while(zIn < zEnd && SyisSpace(zIn[0]) ){ - zIn++; - } - if( zIn >= zEnd ){ - break; - } - zPtr = zIn; - while( zPtr < zEnd && zPtr[0] != '=' && zPtr[0] != '&' && zPtr[0] != ';' ){ - zPtr++; - } - /* Reset the working buffer */ - SyBlobReset(pWorker); - /* Decode the entry */ - SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE); - /* Save the entry */ - sName.nByte = SyBlobLength(pWorker); - sValue.zString = 0; - sValue.nByte = 0; - if( zPtr < zEnd && zPtr[0] == '=' ){ - zPtr++; - zIn = zPtr; - /* Store field value */ - while( zPtr < zEnd && zPtr[0] != '&' && zPtr[0] != ';' ){ - zPtr++; - } - if( zPtr > zIn ){ - /* Decode the value */ - nBlobOfft = SyBlobLength(pWorker); - SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE); - sValue.zString = (const char *)SyBlobDataAt(pWorker, nBlobOfft); - sValue.nByte = SyBlobLength(pWorker) - nBlobOfft; - - } - /* Synchronize pointers */ - zIn = zPtr; - } - sName.zString = (const char *)SyBlobData(pWorker); - /* Install the decoded query in the $_GET/$_REQUEST array */ - if( pGet && (pGet->iFlags & MEMOBJ_HASHMAP) ){ - VmHashmapInsert((jx9_hashmap *)pGet->x.pOther, - sName.zString, (int)sName.nByte, - sValue.zString, (int)sValue.nByte - ); - } - if( pRequest && (pRequest->iFlags & MEMOBJ_HASHMAP) ){ - VmHashmapInsert((jx9_hashmap *)pRequest->x.pOther, - sName.zString, (int)sName.nByte, - sValue.zString, (int)sValue.nByte - ); - } - /* Advance the pointer */ - zIn = &zPtr[1]; - } - /* All done*/ - return SXRET_OK; - } - /* - * Extract MIME header value from the given set. - * Return header value on success. NULL otherwise. - */ - static SyString * VmHttpExtractHeaderValue(SySet *pSet, const char *zMime, sxu32 nByte) - { - SyhttpHeader *aMime, *pMime; - SyString sMime; - sxu32 n; - SyStringInitFromBuf(&sMime, zMime, nByte); - /* Point to the MIME entries */ - aMime = (SyhttpHeader *)SySetBasePtr(pSet); - /* Perform the lookup */ - for( n = 0 ; n < SySetUsed(pSet) ; ++n ){ - pMime = &aMime[n]; - if( SyStringCmp(&sMime, &pMime->sName, SyStrnicmp) == 0 ){ - /* Header found, return it's associated value */ - return &pMime->sValue; - } - } - /* No such MIME header */ - return 0; - } - /* - * Tokenize and decode a raw "Cookie:" MIME header into a name value pair - * and insert it's fields [i.e name, value] in the $_COOKIE superglobal. - */ - static sxi32 VmHttpPorcessCookie(jx9_vm *pVm, SyBlob *pWorker, const char *zIn, sxu32 nByte) - { - const char *zPtr, *zDelimiter, *zEnd = &zIn[nByte]; - SyString sName, sValue; - jx9_value *pCookie; - sxu32 nOfft; - /* Make sure the $_COOKIE superglobal is available */ - pCookie = VmExtractSuper(&(*pVm), "_COOKIE", sizeof("_COOKIE")-1); - if( pCookie == 0 || (pCookie->iFlags & MEMOBJ_HASHMAP) == 0 ){ - /* $_COOKIE superglobal not available */ - return SXERR_NOTFOUND; - } - for(;;){ - /* Jump leading white spaces */ - while( zIn < zEnd && SyisSpace(zIn[0]) ){ - zIn++; - } - if( zIn >= zEnd ){ - break; - } - /* Reset the working buffer */ - SyBlobReset(pWorker); - zDelimiter = zIn; - /* Delimit the name[=value]; pair */ - while( zDelimiter < zEnd && zDelimiter[0] != ';' ){ - zDelimiter++; - } - zPtr = zIn; - while( zPtr < zDelimiter && zPtr[0] != '=' ){ - zPtr++; - } - /* Decode the cookie */ - SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE); - sName.nByte = SyBlobLength(pWorker); - zPtr++; - sValue.zString = 0; - sValue.nByte = 0; - if( zPtr < zDelimiter ){ - /* Got a Cookie value */ - nOfft = SyBlobLength(pWorker); - SyUriDecode(zPtr, (sxu32)(zDelimiter-zPtr), jx9VmBlobConsumer, pWorker, TRUE); - SyStringInitFromBuf(&sValue, SyBlobDataAt(pWorker, nOfft), SyBlobLength(pWorker)-nOfft); - } - /* Synchronize pointers */ - zIn = &zDelimiter[1]; - /* Perform the insertion */ - sName.zString = (const char *)SyBlobData(pWorker); - VmHashmapInsert((jx9_hashmap *)pCookie->x.pOther, - sName.zString, (int)sName.nByte, - sValue.zString, (int)sValue.nByte - ); - } - return SXRET_OK; - } - /* - * Process a full HTTP request and populate the appropriate arrays - * such as $_SERVER, $_GET, $_POST, $_COOKIE, $_REQUEST, ... with the information - * extracted from the raw HTTP request. As an extension Symisc introduced - * the $_HEADER array which hold a copy of the processed HTTP MIME headers - * and their associated values. [i.e: $_HEADER['Server'], $_HEADER['User-Agent'], ...]. - * This function return SXRET_OK on success. Any other return value indicates - * a malformed HTTP request. - */ - static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte) - { - SyString *pName, *pValue, sRequest; /* Raw HTTP request */ - jx9_value *pHeaderArray; /* $_HEADER superglobal (Symisc eXtension to the JX9 specification)*/ - SyhttpHeader *pHeader; /* MIME header */ - SyhttpUri sUri; /* Parse of the raw URI*/ - SyBlob sWorker; /* General purpose working buffer */ - SySet sHeader; /* MIME headers set */ - sxi32 iMethod; /* HTTP method [i.e: GET, POST, HEAD...]*/ - sxi32 iVer; /* HTTP protocol version */ - sxi32 rc; - SyStringInitFromBuf(&sRequest, zRequest, nByte); - SySetInit(&sHeader, &pVm->sAllocator, sizeof(SyhttpHeader)); - SyBlobInit(&sWorker, &pVm->sAllocator); - /* Ignore leading and trailing white spaces*/ - SyStringFullTrim(&sRequest); - /* Process the first line */ - rc = VmHttpProcessFirstLine(&sRequest, &iMethod, &sUri, &iVer); - if( rc != SXRET_OK ){ - return rc; - } - /* Process MIME headers */ - VmHttpExtractHeaders(&sRequest, &sHeader); - /* - * Setup $_SERVER environments - */ - /* 'SERVER_PROTOCOL': Name and revision of the information protocol via which the page was requested */ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "SERVER_PROTOCOL", - iVer == HTTP_PROTO_10 ? "HTTP/1.0" : "HTTP/1.1", - sizeof("HTTP/1.1")-1 - ); - /* 'REQUEST_METHOD': Which request method was used to access the page */ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "REQUEST_METHOD", - iMethod == HTTP_METHOD_GET ? "GET" : - (iMethod == HTTP_METHOD_POST ? "POST": - (iMethod == HTTP_METHOD_PUT ? "PUT" : - (iMethod == HTTP_METHOD_HEAD ? "HEAD" : "OTHER"))), - -1 /* Compute attribute length automatically */ - ); - if( SyStringLength(&sUri.sQuery) > 0 && iMethod == HTTP_METHOD_GET ){ - pValue = &sUri.sQuery; - /* 'QUERY_STRING': The query string, if any, via which the page was accessed */ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "QUERY_STRING", - pValue->zString, - pValue->nByte - ); - /* Decoded the raw query */ - VmHttpSplitEncodedQuery(&(*pVm), pValue, &sWorker, FALSE); - } - /* REQUEST_URI: The URI which was given in order to access this page; for instance, '/index.html' */ - pValue = &sUri.sRaw; - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "REQUEST_URI", - pValue->zString, - pValue->nByte - ); - /* - * 'PATH_INFO' - * 'ORIG_PATH_INFO' - * Contains any client-provided pathname information trailing the actual script filename but preceding - * the query string, if available. For instance, if the current script was accessed via the URL - * http://www.example.com/jx9/path_info.jx9/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain - * /some/stuff. - */ - pValue = &sUri.sPath; - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "PATH_INFO", - pValue->zString, - pValue->nByte - ); - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "ORIG_PATH_INFO", - pValue->zString, - pValue->nByte - ); - /* 'HTTP_ACCEPT': Contents of the Accept: header from the current request, if there is one */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Accept", sizeof("Accept")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_ACCEPT", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_ACCEPT_CHARSET': Contents of the Accept-Charset: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Charset", sizeof("Accept-Charset")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_ACCEPT_CHARSET", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_ACCEPT_ENCODING': Contents of the Accept-Encoding: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Encoding", sizeof("Accept-Encoding")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_ACCEPT_ENCODING", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_ACCEPT_LANGUAGE': Contents of the Accept-Language: header from the current request, if there is one */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Language", sizeof("Accept-Language")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_ACCEPT_LANGUAGE", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_CONNECTION': Contents of the Connection: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Connection", sizeof("Connection")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_CONNECTION", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_HOST': Contents of the Host: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Host", sizeof("Host")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_HOST", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_REFERER': Contents of the Referer: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Referer", sizeof("Referer")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_REFERER", - pValue->zString, - pValue->nByte - ); - } - /* 'HTTP_USER_AGENT': Contents of the Referer: header from the current request, if there is one. */ - pValue = VmHttpExtractHeaderValue(&sHeader, "User-Agent", sizeof("User-Agent")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "HTTP_USER_AGENT", - pValue->zString, - pValue->nByte - ); - } - /* 'JX9_AUTH_DIGEST': When doing Digest HTTP authentication this variable is set to the 'Authorization' - * header sent by the client (which you should then use to make the appropriate validation). - */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Authorization", sizeof("Authorization")-1); - if( pValue ){ - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "JX9_AUTH_DIGEST", - pValue->zString, - pValue->nByte - ); - jx9_vm_config(pVm, - JX9_VM_CONFIG_SERVER_ATTR, - "JX9_AUTH", - pValue->zString, - pValue->nByte - ); - } - /* Install all clients HTTP headers in the $_HEADER superglobal */ - pHeaderArray = VmExtractSuper(&(*pVm), "_HEADER", sizeof("_HEADER")-1); - /* Iterate throw the available MIME headers*/ - SySetResetCursor(&sHeader); - pHeader = 0; /* stupid cc warning */ - while( SXRET_OK == SySetGetNextEntry(&sHeader, (void **)&pHeader) ){ - pName = &pHeader->sName; - pValue = &pHeader->sValue; - if( pHeaderArray && (pHeaderArray->iFlags & MEMOBJ_HASHMAP)){ - /* Insert the MIME header and it's associated value */ - VmHashmapInsert((jx9_hashmap *)pHeaderArray->x.pOther, - pName->zString, (int)pName->nByte, - pValue->zString, (int)pValue->nByte - ); - } - if( pName->nByte == sizeof("Cookie")-1 && SyStrnicmp(pName->zString, "Cookie", sizeof("Cookie")-1) == 0 - && pValue->nByte > 0){ - /* Process the name=value pair and insert them in the $_COOKIE superglobal array */ - VmHttpPorcessCookie(&(*pVm), &sWorker, pValue->zString, pValue->nByte); - } - } - if( iMethod == HTTP_METHOD_POST ){ - /* Extract raw POST data */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Type", sizeof("Content-Type") - 1); - if( pValue && pValue->nByte >= sizeof("application/x-www-form-urlencoded") - 1 && - SyMemcmp("application/x-www-form-urlencoded", pValue->zString, pValue->nByte) == 0 ){ - /* Extract POST data length */ - pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Length", sizeof("Content-Length") - 1); - if( pValue ){ - sxi32 iLen = 0; /* POST data length */ - SyStrToInt32(pValue->zString, pValue->nByte, (void *)&iLen, 0); - if( iLen > 0 ){ - /* Remove leading and trailing white spaces */ - SyStringFullTrim(&sRequest); - if( (int)sRequest.nByte > iLen ){ - sRequest.nByte = (sxu32)iLen; - } - /* Decode POST data now */ - VmHttpSplitEncodedQuery(&(*pVm), &sRequest, &sWorker, TRUE); - } - } - } - } - /* All done, clean-up the mess left behind */ - SySetRelease(&sHeader); - SyBlobRelease(&sWorker); - return SXRET_OK; - } - -/* - * ---------------------------------------------------------- - * File: lhash_kv.c - * MD5: 1c9e0b9759c2c53c78e1e04b44dac494 - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: lhash_kv.c v1.7 Solaris 2013-01-14 12:56 stable $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif -/* - * This file implements disk based hashtable using the linear hashing algorithm. - * This implementation is the one decribed in the paper: - * LINEAR HASHING : A NEW TOOL FOR FILE AND TABLE ADDRESSING. Witold Litwin. I. N. Ft. I. A.. 78 150 Le Chesnay, France. - * Plus a smart extension called Virtual Bucket Table. (contact devel@symisc.net for additional information). - */ -/* Magic number identifying a valid storage image */ -#define L_HASH_MAGIC 0xFA782DCB -/* - * Magic word to hash to identify a valid hash function. - */ -#define L_HASH_WORD "chm@symisc" -/* - * Cell size on disk. - */ -#define L_HASH_CELL_SZ (4/*Hash*/+4/*Key*/+8/*Data*/+2/* Offset of the next cell */+8/*Overflow*/) -/* - * Primary page (not overflow pages) header size on disk. - */ -#define L_HASH_PAGE_HDR_SZ (2/* Cell offset*/+2/* Free block offset*/+8/*Slave page number*/) -/* - * The maximum amount of payload (in bytes) that can be stored locally for - * a database entry. If the entry contains more data than this, the - * extra goes onto overflow pages. -*/ -#define L_HASH_MX_PAYLOAD(PageSize) (PageSize-(L_HASH_PAGE_HDR_SZ+L_HASH_CELL_SZ)) -/* - * Maxium free space on a single page. - */ -#define L_HASH_MX_FREE_SPACE(PageSize) (PageSize - (L_HASH_PAGE_HDR_SZ)) -/* -** The maximum number of bytes of payload allowed on a single overflow page. -*/ -#define L_HASH_OVERFLOW_SIZE(PageSize) (PageSize-8) -/* Forward declaration */ -typedef struct lhash_kv_engine lhash_kv_engine; -typedef struct lhpage lhpage; -/* - * Each record in the database is identified either in-memory or in - * disk by an instance of the following structure. - */ -typedef struct lhcell lhcell; -struct lhcell -{ - /* Disk-data (Big-Endian) */ - sxu32 nHash; /* Hash of the key: 4 bytes */ - sxu32 nKey; /* Key length: 4 bytes */ - sxu64 nData; /* Data length: 8 bytes */ - sxu16 iNext; /* Offset of the next cell: 2 bytes */ - pgno iOvfl; /* Overflow page number if any: 8 bytes */ - /* In-memory data only */ - lhpage *pPage; /* Page this cell belongs */ - sxu16 iStart; /* Offset of this cell */ - pgno iDataPage; /* Data page number when overflow */ - sxu16 iDataOfft; /* Offset of the data in iDataPage */ - SyBlob sKey; /* Record key for fast lookup (Kept in-memory if < 256KB ) */ - lhcell *pNext,*pPrev; /* Linked list of the loaded memory cells */ - lhcell *pNextCol,*pPrevCol; /* Collison chain */ -}; -/* -** Each database page has a header that is an instance of this -** structure. -*/ -typedef struct lhphdr lhphdr; -struct lhphdr -{ - sxu16 iOfft; /* Offset of the first cell */ - sxu16 iFree; /* Offset of the first free block*/ - pgno iSlave; /* Slave page number */ -}; -/* - * Each loaded primary disk page is represented in-memory using - * an instance of the following structure. - */ -struct lhpage -{ - lhash_kv_engine *pHash; /* KV Storage engine that own this page */ - unqlite_page *pRaw; /* Raw page contents */ - lhphdr sHdr; /* Processed page header */ - lhcell **apCell; /* Cell buckets */ - lhcell *pList,*pFirst; /* Linked list of cells */ - sxu32 nCell; /* Total number of cells */ - sxu32 nCellSize; /* apCell[] size */ - lhpage *pMaster; /* Master page in case we are dealing with a slave page */ - lhpage *pSlave; /* List of slave pages */ - lhpage *pNextSlave; /* Next slave page on the list */ - sxi32 iSlave; /* Total number of slave pages */ - sxu16 nFree; /* Amount of free space available in the page */ -}; -/* - * A Bucket map record which is used to map logical bucket number to real - * bucket number is represented by an instance of the following structure. - */ -typedef struct lhash_bmap_rec lhash_bmap_rec; -struct lhash_bmap_rec -{ - pgno iLogic; /* Logical bucket number */ - pgno iReal; /* Real bucket number */ - lhash_bmap_rec *pNext,*pPrev; /* Link to other bucket map */ - lhash_bmap_rec *pNextCol,*pPrevCol; /* Collision links */ -}; -typedef struct lhash_bmap_page lhash_bmap_page; -struct lhash_bmap_page -{ - pgno iNum; /* Page number where this entry is stored */ - sxu16 iPtr; /* Offset to start reading/writing from */ - sxu32 nRec; /* Total number of records in this page */ - pgno iNext; /* Next map page */ -}; -/* - * An in memory linear hash implemenation is represented by in an isntance - * of the following structure. - */ -struct lhash_kv_engine -{ - const unqlite_kv_io *pIo; /* IO methods: Must be first */ - /* Private fields */ - SyMemBackend sAllocator; /* Private memory backend */ - ProcHash xHash; /* Default hash function */ - ProcCmp xCmp; /* Default comparison function */ - unqlite_page *pHeader; /* Page one to identify a valid implementation */ - lhash_bmap_rec **apMap; /* Buckets map records */ - sxu32 nBuckRec; /* Total number of bucket map records */ - sxu32 nBuckSize; /* apMap[] size */ - lhash_bmap_rec *pList; /* List of bucket map records */ - lhash_bmap_rec *pFirst; /* First record*/ - lhash_bmap_page sPageMap; /* Primary bucket map */ - int iPageSize; /* Page size */ - pgno nFreeList; /* List of free pages */ - pgno split_bucket; /* Current split bucket: MUST BE A POWER OF TWO */ - pgno max_split_bucket; /* Maximum split bucket: MUST BE A POWER OF TWO */ - pgno nmax_split_nucket; /* Next maximum split bucket (1 << nMsb): In-memory only */ - sxu32 nMagic; /* Magic number to identify a valid linear hash disk database */ -}; -/* - * Given a logical bucket number, return the record associated with it. - */ -static lhash_bmap_rec * lhMapFindBucket(lhash_kv_engine *pEngine,pgno iLogic) -{ - lhash_bmap_rec *pRec; - if( pEngine->nBuckRec < 1 ){ - /* Don't bother */ - return 0; - } - pRec = pEngine->apMap[iLogic & (pEngine->nBuckSize - 1)]; - for(;;){ - if( pRec == 0 ){ - break; - } - if( pRec->iLogic == iLogic ){ - return pRec; - } - /* Point to the next entry */ - pRec = pRec->pNextCol; - } - /* No such record */ - return 0; -} -/* - * Install a new bucket map record. - */ -static int lhMapInstallBucket(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal) -{ - lhash_bmap_rec *pRec; - sxu32 iBucket; - /* Allocate a new instance */ - pRec = (lhash_bmap_rec *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhash_bmap_rec)); - if( pRec == 0 ){ - return UNQLITE_NOMEM; - } - /* Zero the structure */ - SyZero(pRec,sizeof(lhash_bmap_rec)); - /* Fill in the structure */ - pRec->iLogic = iLogic; - pRec->iReal = iReal; - iBucket = iLogic & (pEngine->nBuckSize - 1); - pRec->pNextCol = pEngine->apMap[iBucket]; - if( pEngine->apMap[iBucket] ){ - pEngine->apMap[iBucket]->pPrevCol = pRec; - } - pEngine->apMap[iBucket] = pRec; - /* Link */ - if( pEngine->pFirst == 0 ){ - pEngine->pFirst = pEngine->pList = pRec; - }else{ - MACRO_LD_PUSH(pEngine->pList,pRec); - } - pEngine->nBuckRec++; - if( (pEngine->nBuckRec >= pEngine->nBuckSize * 3) && pEngine->nBuckRec < 100000 ){ - /* Allocate a new larger table */ - sxu32 nNewSize = pEngine->nBuckSize << 1; - lhash_bmap_rec *pEntry; - lhash_bmap_rec **apNew; - sxu32 n; - - apNew = (lhash_bmap_rec **)SyMemBackendAlloc(&pEngine->sAllocator, nNewSize * sizeof(lhash_bmap_rec *)); - if( apNew ){ - /* Zero the new table */ - SyZero((void *)apNew, nNewSize * sizeof(lhash_bmap_rec *)); - /* Rehash all entries */ - n = 0; - pEntry = pEngine->pList; - for(;;){ - /* Loop one */ - if( n >= pEngine->nBuckRec ){ - break; - } - pEntry->pNextCol = pEntry->pPrevCol = 0; - /* Install in the new bucket */ - iBucket = pEntry->iLogic & (nNewSize - 1); - pEntry->pNextCol = apNew[iBucket]; - if( apNew[iBucket] ){ - apNew[iBucket]->pPrevCol = pEntry; - } - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - n++; - } - /* Release the old table and reflect the change */ - SyMemBackendFree(&pEngine->sAllocator,(void *)pEngine->apMap); - pEngine->apMap = apNew; - pEngine->nBuckSize = nNewSize; - } - } - return UNQLITE_OK; -} -/* - * Process a raw bucket map record. - */ -static int lhMapLoadPage(lhash_kv_engine *pEngine,lhash_bmap_page *pMap,const unsigned char *zRaw) -{ - const unsigned char *zEnd = &zRaw[pEngine->iPageSize]; - const unsigned char *zPtr = zRaw; - pgno iLogic,iReal; - sxu32 n; - int rc; - if( pMap->iPtr == 0 ){ - /* Read the map header */ - SyBigEndianUnpack64(zRaw,&pMap->iNext); - zRaw += 8; - SyBigEndianUnpack32(zRaw,&pMap->nRec); - zRaw += 4; - }else{ - /* Mostly page one of the database */ - zRaw += pMap->iPtr; - } - /* Start processing */ - for( n = 0; n < pMap->nRec ; ++n ){ - if( zRaw >= zEnd ){ - break; - } - /* Extract the logical and real bucket number */ - SyBigEndianUnpack64(zRaw,&iLogic); - zRaw += 8; - SyBigEndianUnpack64(zRaw,&iReal); - zRaw += 8; - /* Install the record in the map */ - rc = lhMapInstallBucket(pEngine,iLogic,iReal); - if( rc != UNQLITE_OK ){ - return rc; - } - } - pMap->iPtr = (sxu16)(zRaw-zPtr); - /* All done */ - return UNQLITE_OK; -} -/* - * Allocate a new cell instance. - */ -static lhcell * lhNewCell(lhash_kv_engine *pEngine,lhpage *pPage) -{ - lhcell *pCell; - pCell = (lhcell *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhcell)); - if( pCell == 0 ){ - return 0; - } - /* Zero the structure */ - SyZero(pCell,sizeof(lhcell)); - /* Fill in the structure */ - SyBlobInit(&pCell->sKey,&pEngine->sAllocator); - pCell->pPage = pPage; - return pCell; -} -/* - * Discard a cell from the page table. - */ -static void lhCellDiscard(lhcell *pCell) -{ - lhpage *pPage = pCell->pPage->pMaster; - - if( pCell->pPrevCol ){ - pCell->pPrevCol->pNextCol = pCell->pNextCol; - }else{ - pPage->apCell[pCell->nHash & (pPage->nCellSize - 1)] = pCell->pNextCol; - } - if( pCell->pNextCol ){ - pCell->pNextCol->pPrevCol = pCell->pPrevCol; - } - MACRO_LD_REMOVE(pPage->pList,pCell); - if( pCell == pPage->pFirst ){ - pPage->pFirst = pCell->pPrev; - } - pPage->nCell--; - /* Release the cell */ - SyBlobRelease(&pCell->sKey); - SyMemBackendPoolFree(&pPage->pHash->sAllocator,pCell); -} -/* - * Install a cell in the page table. - */ -static int lhInstallCell(lhcell *pCell) -{ - lhpage *pPage = pCell->pPage->pMaster; - sxu32 iBucket; - if( pPage->nCell < 1 ){ - sxu32 nTableSize = 32; /* Must be a power of two */ - lhcell **apTable; - /* Allocate a new cell table */ - apTable = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nTableSize * sizeof(lhcell *)); - if( apTable == 0 ){ - return UNQLITE_NOMEM; - } - /* Zero the new table */ - SyZero((void *)apTable, nTableSize * sizeof(lhcell *)); - /* Install it */ - pPage->apCell = apTable; - pPage->nCellSize = nTableSize; - } - iBucket = pCell->nHash & (pPage->nCellSize - 1); - pCell->pNextCol = pPage->apCell[iBucket]; - if( pPage->apCell[iBucket] ){ - pPage->apCell[iBucket]->pPrevCol = pCell; - } - pPage->apCell[iBucket] = pCell; - if( pPage->pFirst == 0 ){ - pPage->pFirst = pPage->pList = pCell; - }else{ - MACRO_LD_PUSH(pPage->pList,pCell); - } - pPage->nCell++; - if( (pPage->nCell >= pPage->nCellSize * 3) && pPage->nCell < 100000 ){ - /* Allocate a new larger table */ - sxu32 nNewSize = pPage->nCellSize << 1; - lhcell *pEntry; - lhcell **apNew; - sxu32 n; - - apNew = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nNewSize * sizeof(lhcell *)); - if( apNew ){ - /* Zero the new table */ - SyZero((void *)apNew, nNewSize * sizeof(lhcell *)); - /* Rehash all entries */ - n = 0; - pEntry = pPage->pList; - for(;;){ - /* Loop one */ - if( n >= pPage->nCell ){ - break; - } - pEntry->pNextCol = pEntry->pPrevCol = 0; - /* Install in the new bucket */ - iBucket = pEntry->nHash & (nNewSize - 1); - pEntry->pNextCol = apNew[iBucket]; - if( apNew[iBucket] ){ - apNew[iBucket]->pPrevCol = pEntry; - } - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - n++; - } - /* Release the old table and reflect the change */ - SyMemBackendFree(&pPage->pHash->sAllocator,(void *)pPage->apCell); - pPage->apCell = apNew; - pPage->nCellSize = nNewSize; - } - } - return UNQLITE_OK; -} -/* - * Private data of lhKeyCmp(). - */ -struct lhash_key_cmp -{ - const char *zIn; /* Start of the stream */ - const char *zEnd; /* End of the stream */ - ProcCmp xCmp; /* Comparison function */ -}; -/* - * Comparsion callback for large key > 256 KB - */ -static int lhKeyCmp(const void *pData,sxu32 nLen,void *pUserData) -{ - struct lhash_key_cmp *pCmp = (struct lhash_key_cmp *)pUserData; - int rc; - if( pCmp->zIn >= pCmp->zEnd ){ - if( nLen > 0 ){ - return UNQLITE_ABORT; - } - return UNQLITE_OK; - } - /* Perform the comparison */ - rc = pCmp->xCmp((const void *)pCmp->zIn,pData,nLen); - if( rc != 0 ){ - /* Abort comparison */ - return UNQLITE_ABORT; - } - /* Advance the cursor */ - pCmp->zIn += nLen; - return UNQLITE_OK; -} -/* Forward declaration */ -static int lhConsumeCellkey(lhcell *pCell,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData,int offt_only); -/* - * given a key, return the cell associated with it on success. NULL otherwise. - */ -static lhcell * lhFindCell( - lhpage *pPage, /* Target page */ - const void *pKey, /* Lookup key */ - sxu32 nByte, /* Key length */ - sxu32 nHash /* Hash of the key */ - ) -{ - lhcell *pEntry; - if( pPage->nCell < 1 ){ - /* Don't bother hashing */ - return 0; - } - /* Point to the corresponding bucket */ - pEntry = pPage->apCell[nHash & (pPage->nCellSize - 1)]; - for(;;){ - if( pEntry == 0 ){ - break; - } - if( pEntry->nHash == nHash && pEntry->nKey == nByte ){ - if( SyBlobLength(&pEntry->sKey) < 1 ){ - /* Large key (> 256 KB) are not kept in-memory */ - struct lhash_key_cmp sCmp; - int rc; - /* Fill-in the structure */ - sCmp.zIn = (const char *)pKey; - sCmp.zEnd = &sCmp.zIn[nByte]; - sCmp.xCmp = pPage->pHash->xCmp; - /* Fetch the key from disk and perform the comparison */ - rc = lhConsumeCellkey(pEntry,lhKeyCmp,&sCmp,0); - if( rc == UNQLITE_OK ){ - /* Cell found */ - return pEntry; - } - }else if ( pPage->pHash->xCmp(pKey,SyBlobData(&pEntry->sKey),nByte) == 0 ){ - /* Cell found */ - return pEntry; - } - } - /* Point to the next entry */ - pEntry = pEntry->pNextCol; - } - /* No such entry */ - return 0; -} -/* - * Parse a raw cell fetched from disk. - */ -static int lhParseOneCell(lhpage *pPage,const unsigned char *zRaw,const unsigned char *zEnd,lhcell **ppOut) -{ - sxu16 iNext,iOfft; - sxu32 iHash,nKey; - lhcell *pCell; - sxu64 nData; - int rc; - /* Offset this cell is stored */ - iOfft = (sxu16)(zRaw - (const unsigned char *)pPage->pRaw->zData); - /* 4 byte hash number */ - SyBigEndianUnpack32(zRaw,&iHash); - zRaw += 4; - /* 4 byte key length */ - SyBigEndianUnpack32(zRaw,&nKey); - zRaw += 4; - /* 8 byte data length */ - SyBigEndianUnpack64(zRaw,&nData); - zRaw += 8; - /* 2 byte offset of the next cell */ - SyBigEndianUnpack16(zRaw,&iNext); - /* Perform a sanity check */ - if( iNext > 0 && &pPage->pRaw->zData[iNext] >= zEnd ){ - return UNQLITE_CORRUPT; - } - zRaw += 2; - pCell = lhNewCell(pPage->pHash,pPage); - if( pCell == 0 ){ - return UNQLITE_NOMEM; - } - /* Fill in the structure */ - pCell->iNext = iNext; - pCell->nKey = nKey; - pCell->nData = nData; - pCell->nHash = iHash; - /* Overflow page if any */ - SyBigEndianUnpack64(zRaw,&pCell->iOvfl); - zRaw += 8; - /* Cell offset */ - pCell->iStart = iOfft; - /* Consume the key */ - rc = lhConsumeCellkey(pCell,unqliteDataConsumer,&pCell->sKey,pCell->nKey > 262144 /* 256 KB */? 1 : 0); - if( rc != UNQLITE_OK ){ - /* TICKET: 14-32-chm@symisc.net: Key too large for memory */ - SyBlobRelease(&pCell->sKey); - } - /* Finally install the cell */ - rc = lhInstallCell(pCell); - if( rc != UNQLITE_OK ){ - return rc; - } - if( ppOut ){ - *ppOut = pCell; - } - return UNQLITE_OK; -} -/* - * Compute the total number of free space on a given page. - */ -static int lhPageFreeSpace(lhpage *pPage) -{ - const unsigned char *zEnd,*zRaw = pPage->pRaw->zData; - lhphdr *pHdr = &pPage->sHdr; - sxu16 iNext,iAmount; - sxu16 nFree = 0; - if( pHdr->iFree < 1 ){ - /* Don't bother processing, the page is full */ - pPage->nFree = 0; - return UNQLITE_OK; - } - /* Point to first free block */ - zEnd = &zRaw[pPage->pHash->iPageSize]; - zRaw += pHdr->iFree; - for(;;){ - /* Offset of the next free block */ - SyBigEndianUnpack16(zRaw,&iNext); - zRaw += 2; - /* Available space on this block */ - SyBigEndianUnpack16(zRaw,&iAmount); - nFree += iAmount; - if( iNext < 1 ){ - /* No more free blocks */ - break; - } - /* Point to the next free block*/ - zRaw = &pPage->pRaw->zData[iNext]; - if( zRaw >= zEnd ){ - /* Corrupt page */ - return UNQLITE_CORRUPT; - } - } - /* Save the amount of free space */ - pPage->nFree = nFree; - return UNQLITE_OK; -} -/* - * Given a primary page, load all its cell. - */ -static int lhLoadCells(lhpage *pPage) -{ - const unsigned char *zEnd,*zRaw = pPage->pRaw->zData; - lhphdr *pHdr = &pPage->sHdr; - lhcell *pCell = 0; /* cc warning */ - int rc; - /* Calculate the amount of free space available first */ - rc = lhPageFreeSpace(pPage); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pHdr->iOfft < 1 ){ - /* Don't bother processing, the page is empty */ - return UNQLITE_OK; - } - /* Point to first cell */ - zRaw += pHdr->iOfft; - zEnd = &zRaw[pPage->pHash->iPageSize]; - for(;;){ - /* Parse a single cell */ - rc = lhParseOneCell(pPage,zRaw,zEnd,&pCell); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pCell->iNext < 1 ){ - /* No more cells */ - break; - } - /* Point to the next cell */ - zRaw = &pPage->pRaw->zData[pCell->iNext]; - if( zRaw >= zEnd ){ - /* Corrupt page */ - return UNQLITE_CORRUPT; - } - } - /* All done */ - return UNQLITE_OK; -} -/* - * Given a page, parse its raw headers. - */ -static int lhParsePageHeader(lhpage *pPage) -{ - const unsigned char *zRaw = pPage->pRaw->zData; - lhphdr *pHdr = &pPage->sHdr; - /* Offset of the first cell */ - SyBigEndianUnpack16(zRaw,&pHdr->iOfft); - zRaw += 2; - /* Offset of the first free block */ - SyBigEndianUnpack16(zRaw,&pHdr->iFree); - zRaw += 2; - /* Slave page number */ - SyBigEndianUnpack64(zRaw,&pHdr->iSlave); - /* All done */ - return UNQLITE_OK; -} -/* - * Allocate a new page instance. - */ -static lhpage * lhNewPage( - lhash_kv_engine *pEngine, /* KV store which own this instance */ - unqlite_page *pRaw, /* Raw page contents */ - lhpage *pMaster /* Master page in case we are dealing with a slave page */ - ) -{ - lhpage *pPage; - /* Allocate a new instance */ - pPage = (lhpage *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhpage)); - if( pPage == 0 ){ - return 0; - } - /* Zero the structure */ - SyZero(pPage,sizeof(lhpage)); - /* Fill-in the structure */ - pPage->pHash = pEngine; - pPage->pRaw = pRaw; - pPage->pMaster = pMaster ? pMaster /* Slave page */ : pPage /* Master page */ ; - if( pPage->pMaster != pPage ){ - /* Slave page, attach it to its master */ - pPage->pNextSlave = pMaster->pSlave; - pMaster->pSlave = pPage; - pMaster->iSlave++; - } - /* Save this instance for future fast lookup */ - pRaw->pUserData = pPage; - /* All done */ - return pPage; -} -/* - * Load a primary and its associated slave pages from disk. - */ -static int lhLoadPage(lhash_kv_engine *pEngine,pgno pnum,lhpage *pMaster,lhpage **ppOut,int iNest) -{ - unqlite_page *pRaw; - lhpage *pPage = 0; /* cc warning */ - int rc; - /* Aquire the page from the pager first */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pnum,&pRaw); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pRaw->pUserData ){ - /* The page is already parsed and loaded in memory. Point to it */ - pPage = (lhpage *)pRaw->pUserData; - }else{ - /* Allocate a new page */ - pPage = lhNewPage(pEngine,pRaw,pMaster); - if( pPage == 0 ){ - return UNQLITE_NOMEM; - } - /* Process the page */ - rc = lhParsePageHeader(pPage); - if( rc == UNQLITE_OK ){ - /* Load cells */ - rc = lhLoadCells(pPage); - } - if( rc != UNQLITE_OK ){ - pEngine->pIo->xPageUnref(pPage->pRaw); /* pPage will be released inside this call */ - return rc; - } - if( pPage->sHdr.iSlave > 0 && iNest < 128 ){ - if( pMaster == 0 ){ - pMaster = pPage; - } - /* Slave page. Not a fatal error if something goes wrong here */ - lhLoadPage(pEngine,pPage->sHdr.iSlave,pMaster,0,iNest++); - } - } - if( ppOut ){ - *ppOut = pPage; - } - return UNQLITE_OK; -} -/* - * Given a cell, Consume its key by invoking the given callback for each extracted chunk. - */ -static int lhConsumeCellkey( - lhcell *pCell, /* Target cell */ - int (*xConsumer)(const void *,unsigned int,void *), /* Consumer callback */ - void *pUserData, /* Last argument to xConsumer() */ - int offt_only - ) -{ - lhpage *pPage = pCell->pPage; - const unsigned char *zRaw = pPage->pRaw->zData; - const unsigned char *zPayload; - int rc; - /* Point to the payload area */ - zPayload = &zRaw[pCell->iStart]; - if( pCell->iOvfl == 0 ){ - /* Best scenario, consume the key directly without any overflow page */ - zPayload += L_HASH_CELL_SZ; - rc = xConsumer((const void *)zPayload,pCell->nKey,pUserData); - if( rc != UNQLITE_OK ){ - rc = UNQLITE_ABORT; - } - }else{ - lhash_kv_engine *pEngine = pPage->pHash; - sxu32 nByte,nData = pCell->nKey; - unqlite_page *pOvfl; - int data_offset = 0; - pgno iOvfl; - /* Overflow page */ - iOvfl = pCell->iOvfl; - /* Total usable bytes in an overflow page */ - nByte = L_HASH_OVERFLOW_SIZE(pEngine->iPageSize); - for(;;){ - if( iOvfl == 0 || nData < 1 ){ - /* no more overflow page */ - break; - } - /* Point to the overflow page */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOvfl); - if( rc != UNQLITE_OK ){ - return rc; - } - zPayload = &pOvfl->zData[8]; - /* Point to the raw content */ - if( !data_offset ){ - /* Get the data page and offset */ - SyBigEndianUnpack64(zPayload,&pCell->iDataPage); - zPayload += 8; - SyBigEndianUnpack16(zPayload,&pCell->iDataOfft); - zPayload += 2; - if( offt_only ){ - /* Key too large, grab the data offset and return */ - pEngine->pIo->xPageUnref(pOvfl); - return UNQLITE_OK; - } - data_offset = 1; - } - /* Consume the key */ - if( nData <= nByte ){ - rc = xConsumer((const void *)zPayload,nData,pUserData); - if( rc != UNQLITE_OK ){ - pEngine->pIo->xPageUnref(pOvfl); - return UNQLITE_ABORT; - } - nData = 0; - }else{ - rc = xConsumer((const void *)zPayload,nByte,pUserData); - if( rc != UNQLITE_OK ){ - pEngine->pIo->xPageUnref(pOvfl); - return UNQLITE_ABORT; - } - nData -= nByte; - } - /* Next overflow page in the chain */ - SyBigEndianUnpack64(pOvfl->zData,&iOvfl); - /* Unref the page */ - pEngine->pIo->xPageUnref(pOvfl); - } - rc = UNQLITE_OK; - } - return rc; -} -/* - * Given a cell, Consume its data by invoking the given callback for each extracted chunk. - */ -static int lhConsumeCellData( - lhcell *pCell, /* Target cell */ - int (*xConsumer)(const void *,unsigned int,void *), /* Data consumer callback */ - void *pUserData /* Last argument to xConsumer() */ - ) -{ - lhpage *pPage = pCell->pPage; - const unsigned char *zRaw = pPage->pRaw->zData; - const unsigned char *zPayload; - int rc; - /* Point to the payload area */ - zPayload = &zRaw[pCell->iStart]; - if( pCell->iOvfl == 0 ){ - /* Best scenario, consume the data directly without any overflow page */ - zPayload += L_HASH_CELL_SZ + pCell->nKey; - rc = xConsumer((const void *)zPayload,(sxu32)pCell->nData,pUserData); - if( rc != UNQLITE_OK ){ - rc = UNQLITE_ABORT; - } - }else{ - lhash_kv_engine *pEngine = pPage->pHash; - sxu64 nData = pCell->nData; - unqlite_page *pOvfl; - int fix_offset = 0; - sxu32 nByte; - pgno iOvfl; - /* Overflow page where data is stored */ - iOvfl = pCell->iDataPage; - for(;;){ - if( iOvfl == 0 || nData < 1 ){ - /* no more overflow page */ - break; - } - /* Point to the overflow page */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOvfl); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Point to the raw content */ - zPayload = pOvfl->zData; - if( !fix_offset ){ - /* Point to the data */ - zPayload += pCell->iDataOfft; - nByte = pEngine->iPageSize - pCell->iDataOfft; - fix_offset = 1; - }else{ - zPayload += 8; - /* Total usable bytes in an overflow page */ - nByte = L_HASH_OVERFLOW_SIZE(pEngine->iPageSize); - } - /* Consume the data */ - if( nData <= (sxu64)nByte ){ - rc = xConsumer((const void *)zPayload,(unsigned int)nData,pUserData); - if( rc != UNQLITE_OK ){ - pEngine->pIo->xPageUnref(pOvfl); - return UNQLITE_ABORT; - } - nData = 0; - }else{ - if( nByte > 0 ){ - rc = xConsumer((const void *)zPayload,nByte,pUserData); - if( rc != UNQLITE_OK ){ - pEngine->pIo->xPageUnref(pOvfl); - return UNQLITE_ABORT; - } - nData -= nByte; - } - } - /* Next overflow page in the chain */ - SyBigEndianUnpack64(pOvfl->zData,&iOvfl); - /* Unref the page */ - pEngine->pIo->xPageUnref(pOvfl); - } - rc = UNQLITE_OK; - } - return rc; -} -/* - * Read the linear hash header (Page one of the database). - */ -static int lhash_read_header(lhash_kv_engine *pEngine,unqlite_page *pHeader) -{ - const unsigned char *zRaw = pHeader->zData; - lhash_bmap_page *pMap; - sxu32 nHash; - int rc; - pEngine->pHeader = pHeader; - /* 4 byte magic number */ - SyBigEndianUnpack32(zRaw,&pEngine->nMagic); - zRaw += 4; - if( pEngine->nMagic != L_HASH_MAGIC ){ - /* Corrupt implementation */ - return UNQLITE_CORRUPT; - } - /* 4 byte hash value to identify a valid hash function */ - SyBigEndianUnpack32(zRaw,&nHash); - zRaw += 4; - /* Sanity check */ - if( pEngine->xHash(L_HASH_WORD,sizeof(L_HASH_WORD)-1) != nHash ){ - /* Different hash function */ - pEngine->pIo->xErr(pEngine->pIo->pHandle,"Invalid hash function"); - return UNQLITE_INVALID; - } - /* List of free pages */ - SyBigEndianUnpack64(zRaw,&pEngine->nFreeList); - zRaw += 8; - /* Current split bucket */ - SyBigEndianUnpack64(zRaw,&pEngine->split_bucket); - zRaw += 8; - /* Maximum split bucket */ - SyBigEndianUnpack64(zRaw,&pEngine->max_split_bucket); - zRaw += 8; - /* Next generation */ - pEngine->nmax_split_nucket = pEngine->max_split_bucket << 1; - /* Initialiaze the bucket map */ - pMap = &pEngine->sPageMap; - /* Fill in the structure */ - pMap->iNum = pHeader->pgno; - /* Next page in the bucket map */ - SyBigEndianUnpack64(zRaw,&pMap->iNext); - zRaw += 8; - /* Total number of records in the bucket map (This page only) */ - SyBigEndianUnpack32(zRaw,&pMap->nRec); - zRaw += 4; - pMap->iPtr = (sxu16)(zRaw - pHeader->zData); - /* Load the map in memory */ - rc = lhMapLoadPage(pEngine,pMap,pHeader->zData); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Load the bucket map chain if any */ - for(;;){ - pgno iNext = pMap->iNext; - unqlite_page *pPage; - if( iNext == 0 ){ - /* No more map pages */ - break; - } - /* Point to the target page */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iNext,&pPage); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Fill in the structure */ - pMap->iNum = iNext; - pMap->iPtr = 0; - /* Load the map in memory */ - rc = lhMapLoadPage(pEngine,pMap,pPage->zData); - if( rc != UNQLITE_OK ){ - return rc; - } - } - /* All done */ - return UNQLITE_OK; -} -/* - * Perform a record lookup. - */ -static int lhRecordLookup( - lhash_kv_engine *pEngine, /* KV storage engine */ - const void *pKey, /* Lookup key */ - sxu32 nByte, /* Key length */ - lhcell **ppCell /* OUT: Target cell on success */ - ) -{ - lhash_bmap_rec *pRec; - lhpage *pPage; - lhcell *pCell; - pgno iBucket; - sxu32 nHash; - int rc; - /* Acquire the first page (hash Header) so that everything gets loaded autmatically */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Compute the hash of the key first */ - nHash = pEngine->xHash(pKey,nByte); - /* Extract the logical (i.e. not real) page number */ - iBucket = nHash & (pEngine->nmax_split_nucket - 1); - if( iBucket >= (pEngine->split_bucket + pEngine->max_split_bucket) ){ - /* Low mask */ - iBucket = nHash & (pEngine->max_split_bucket - 1); - } - /* Map the logical bucket number to real page number */ - pRec = lhMapFindBucket(pEngine,iBucket); - if( pRec == 0 ){ - /* No such entry */ - return UNQLITE_NOTFOUND; - } - /* Load the master page and it's slave page in-memory */ - rc = lhLoadPage(pEngine,pRec->iReal,0,&pPage,0); - if( rc != UNQLITE_OK ){ - /* IO error, unlikely scenario */ - return rc; - } - /* Lookup for the cell */ - pCell = lhFindCell(pPage,pKey,nByte,nHash); - if( pCell == 0 ){ - /* No such entry */ - return UNQLITE_NOTFOUND; - } - if( ppCell ){ - *ppCell = pCell; - } - return UNQLITE_OK; -} -/* - * Acquire a new page either from the free list or ask the pager - * for a new one. - */ -static int lhAcquirePage(lhash_kv_engine *pEngine,unqlite_page **ppOut) -{ - unqlite_page *pPage; - int rc; - if( pEngine->nFreeList != 0 ){ - /* Acquire one from the free list */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pEngine->nFreeList,&pPage); - if( rc == UNQLITE_OK ){ - /* Point to the next free page */ - SyBigEndianUnpack64(pPage->zData,&pEngine->nFreeList); - /* Update the database header */ - rc = pEngine->pIo->xWrite(pEngine->pHeader); - if( rc != UNQLITE_OK ){ - return rc; - } - SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/],pEngine->nFreeList); - /* Tell the pager do not journal this page */ - pEngine->pIo->xDontJournal(pPage); - /* Return to the caller */ - *ppOut = pPage; - /* All done */ - return UNQLITE_OK; - } - } - /* Acquire a new page */ - rc = pEngine->pIo->xNew(pEngine->pIo->pHandle,&pPage); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Point to the target page */ - *ppOut = pPage; - return UNQLITE_OK; -} -/* - * Write a bucket map record to disk. - */ -static int lhMapWriteRecord(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal) -{ - lhash_bmap_page *pMap = &pEngine->sPageMap; - unqlite_page *pPage = 0; - int rc; - if( pMap->iPtr > (pEngine->iPageSize - 16) /* 8 byte logical bucket number + 8 byte real bucket number */ ){ - unqlite_page *pOld; - /* Point to the old page */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pMap->iNum,&pOld); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Acquire a new page */ - rc = lhAcquirePage(pEngine,&pPage); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Reflect the change */ - pMap->iNext = 0; - pMap->iNum = pPage->pgno; - pMap->nRec = 0; - pMap->iPtr = 8/* Next page number */+4/* Total records in the map*/; - /* Link this page */ - rc = pEngine->pIo->xWrite(pOld); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pOld->pgno == pEngine->pHeader->pgno ){ - /* First page (Hash header) */ - SyBigEndianPack64(&pOld->zData[4/*magic*/+4/*hash*/+8/* Free page */+8/*current split bucket*/+8/*Maximum split bucket*/],pPage->pgno); - }else{ - /* Link the new page */ - SyBigEndianPack64(pOld->zData,pPage->pgno); - /* Unref */ - pEngine->pIo->xPageUnref(pOld); - } - /* Assume the last bucket map page */ - rc = pEngine->pIo->xWrite(pPage); - if( rc != UNQLITE_OK ){ - return rc; - } - SyBigEndianPack64(pPage->zData,0); /* Next bucket map page on the list */ - } - if( pPage == 0){ - /* Point to the current map page */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pMap->iNum,&pPage); - if( rc != UNQLITE_OK ){ - return rc; - } - } - /* Make page writable */ - rc = pEngine->pIo->xWrite(pPage); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Write the data */ - SyBigEndianPack64(&pPage->zData[pMap->iPtr],iLogic); - pMap->iPtr += 8; - SyBigEndianPack64(&pPage->zData[pMap->iPtr],iReal); - pMap->iPtr += 8; - /* Install the bucket map */ - rc = lhMapInstallBucket(pEngine,iLogic,iReal); - if( rc == UNQLITE_OK ){ - /* Total number of records */ - pMap->nRec++; - if( pPage->pgno == pEngine->pHeader->pgno ){ - /* Page one: Always writable */ - SyBigEndianPack32( - &pPage->zData[4/*magic*/+4/*hash*/+8/* Free page */+8/*current split bucket*/+8/*Maximum split bucket*/+8/*Next map page*/], - pMap->nRec); - }else{ - /* Make page writable */ - rc = pEngine->pIo->xWrite(pPage); - if( rc != UNQLITE_OK ){ - return rc; - } - SyBigEndianPack32(&pPage->zData[8],pMap->nRec); - } - } - return rc; -} -/* - * Defragment a page. - */ -static int lhPageDefragment(lhpage *pPage) -{ - lhash_kv_engine *pEngine = pPage->pHash; - unsigned char *zTmp,*zPtr,*zEnd,*zPayload; - lhcell *pCell; - /* Get a temporary page from the pager. This opertaion never fail */ - zTmp = pEngine->pIo->xTmpPage(pEngine->pIo->pHandle); - /* Move the target cells to the begining */ - pCell = pPage->pList; - /* Write the slave page number */ - SyBigEndianPack64(&zTmp[2/*Offset of the first cell */+2/*Offset of the first free block */],pPage->sHdr.iSlave); - zPtr = &zTmp[L_HASH_PAGE_HDR_SZ]; /* Offset to start writing from */ - zEnd = &zTmp[pEngine->iPageSize]; - pPage->sHdr.iOfft = 0; /* Offset of the first cell */ - for(;;){ - if( pCell == 0 ){ - /* No more cells */ - break; - } - if( pCell->pPage->pRaw->pgno == pPage->pRaw->pgno ){ - /* Cell payload if locally stored */ - zPayload = 0; - if( pCell->iOvfl == 0 ){ - zPayload = &pCell->pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ]; - } - /* Move the cell */ - pCell->iNext = pPage->sHdr.iOfft; - pCell->iStart = (sxu16)(zPtr - zTmp); /* Offset where this cell start */ - pPage->sHdr.iOfft = pCell->iStart; - /* Write the cell header */ - /* 4 byte hash number */ - SyBigEndianPack32(zPtr,pCell->nHash); - zPtr += 4; - /* 4 byte ley length */ - SyBigEndianPack32(zPtr,pCell->nKey); - zPtr += 4; - /* 8 byte data length */ - SyBigEndianPack64(zPtr,pCell->nData); - zPtr += 8; - /* 2 byte offset of the next cell */ - SyBigEndianPack16(zPtr,pCell->iNext); - zPtr += 2; - /* 8 byte overflow page number */ - SyBigEndianPack64(zPtr,pCell->iOvfl); - zPtr += 8; - if( zPayload ){ - /* Local payload */ - SyMemcpy((const void *)zPayload,zPtr,(sxu32)(pCell->nKey + pCell->nData)); - zPtr += pCell->nKey + pCell->nData; - } - if( zPtr >= zEnd ){ - /* Can't happen */ - break; - } - } - /* Point to the next page */ - pCell = pCell->pNext; - } - /* Mark the free block */ - pPage->nFree = (sxu16)(zEnd - zPtr); /* Block length */ - if( pPage->nFree > 3 ){ - pPage->sHdr.iFree = (sxu16)(zPtr - zTmp); /* Offset of the free block */ - /* Mark the block */ - SyBigEndianPack16(zPtr,0); /* Offset of the next free block */ - SyBigEndianPack16(&zPtr[2],pPage->nFree); /* Block length */ - }else{ - /* Block of length less than 4 bytes are simply discarded */ - pPage->nFree = 0; - pPage->sHdr.iFree = 0; - } - /* Reflect the change */ - SyBigEndianPack16(zTmp,pPage->sHdr.iOfft); /* Offset of the first cell */ - SyBigEndianPack16(&zTmp[2],pPage->sHdr.iFree); /* Offset of the first free block */ - SyMemcpy((const void *)zTmp,pPage->pRaw->zData,pEngine->iPageSize); - /* All done */ - return UNQLITE_OK; -} -/* -** Allocate nByte bytes of space on a page. -** -** Return the index into pPage->pRaw->zData[] of the first byte of -** the new allocation. Or return 0 if there is not enough free -** space on the page to satisfy the allocation request. -** -** If the page contains nBytes of free space but does not contain -** nBytes of contiguous free space, then this routine automatically -** calls defragementPage() to consolidate all free space before -** allocating the new chunk. -*/ -static int lhAllocateSpace(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft) -{ - const unsigned char *zEnd,*zPtr; - sxu16 iNext,iBlksz,nByte; - unsigned char *zPrev; - int rc; - if( (sxu64)pPage->nFree < nAmount ){ - /* Don't bother looking for a free chunk */ - return UNQLITE_FULL; - } - if( pPage->nCell < 10 && ((int)nAmount >= (pPage->pHash->iPageSize / 2)) ){ - /* Big chunk need an overflow page for its data */ - return UNQLITE_FULL; - } - zPtr = &pPage->pRaw->zData[pPage->sHdr.iFree]; - zEnd = &pPage->pRaw->zData[pPage->pHash->iPageSize]; - nByte = (sxu16)nAmount; - zPrev = 0; - iBlksz = 0; /* cc warning */ - /* Perform the lookup */ - for(;;){ - if( zPtr >= zEnd ){ - return UNQLITE_FULL; - } - /* Offset of the next free block */ - SyBigEndianUnpack16(zPtr,&iNext); - /* Block size */ - SyBigEndianUnpack16(&zPtr[2],&iBlksz); - if( iBlksz >= nByte ){ - /* Got one */ - break; - } - zPrev = (unsigned char *)zPtr; - if( iNext == 0 ){ - /* No more free blocks, defragment the page */ - rc = lhPageDefragment(pPage); - if( rc == UNQLITE_OK && pPage->nFree >= nByte) { - /* Free blocks are merged together */ - iNext = 0; - zPtr = &pPage->pRaw->zData[pPage->sHdr.iFree]; - iBlksz = pPage->nFree; - zPrev = 0; - break; - }else{ - return UNQLITE_FULL; - } - } - /* Point to the next free block */ - zPtr = &pPage->pRaw->zData[iNext]; - } - /* Acquire writer lock on this page */ - rc = pPage->pHash->pIo->xWrite(pPage->pRaw); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Save block offset */ - *pOfft = (sxu16)(zPtr - pPage->pRaw->zData); - /* Fix pointers */ - if( iBlksz >= nByte && (iBlksz - nByte) > 3 ){ - unsigned char *zBlock = &pPage->pRaw->zData[(*pOfft) + nByte]; - /* Create a new block */ - zPtr = zBlock; - SyBigEndianPack16(zBlock,iNext); /* Offset of the next block */ - SyBigEndianPack16(&zBlock[2],iBlksz-nByte); /* Block size*/ - /* Offset of the new block */ - iNext = (sxu16)(zPtr - pPage->pRaw->zData); - } - /* Fix offsets */ - if( zPrev ){ - SyBigEndianPack16(zPrev,iNext); - }else{ - /* First block */ - pPage->sHdr.iFree = iNext; - /* Reflect on the page header */ - SyBigEndianPack16(&pPage->pRaw->zData[2/* Offset of the first cell1*/],iNext); - } - /* All done */ - pPage->nFree -= nByte; - return UNQLITE_OK; -} -/* - * Write the cell header into the corresponding offset. - */ -static int lhCellWriteHeader(lhcell *pCell) -{ - lhpage *pPage = pCell->pPage; - unsigned char *zRaw = pPage->pRaw->zData; - /* Seek to the desired location */ - zRaw += pCell->iStart; - /* 4 byte hash number */ - SyBigEndianPack32(zRaw,pCell->nHash); - zRaw += 4; - /* 4 byte key length */ - SyBigEndianPack32(zRaw,pCell->nKey); - zRaw += 4; - /* 8 byte data length */ - SyBigEndianPack64(zRaw,pCell->nData); - zRaw += 8; - /* 2 byte offset of the next cell */ - pCell->iNext = pPage->sHdr.iOfft; - SyBigEndianPack16(zRaw,pCell->iNext); - zRaw += 2; - /* 8 byte overflow page number */ - SyBigEndianPack64(zRaw,pCell->iOvfl); - /* Update the page header */ - pPage->sHdr.iOfft = pCell->iStart; - /* pEngine->pIo->xWrite() has been successfully called on this page */ - SyBigEndianPack16(pPage->pRaw->zData,pCell->iStart); - /* All done */ - return UNQLITE_OK; -} -/* - * Write local payload. - */ -static int lhCellWriteLocalPayload(lhcell *pCell, - const void *pKey,sxu32 nKeylen, - const void *pData,unqlite_int64 nDatalen - ) -{ - /* A writer lock have been acquired on this page */ - lhpage *pPage = pCell->pPage; - unsigned char *zRaw = pPage->pRaw->zData; - /* Seek to the desired location */ - zRaw += pCell->iStart + L_HASH_CELL_SZ; - /* Write the key */ - SyMemcpy(pKey,(void *)zRaw,nKeylen); - zRaw += nKeylen; - if( nDatalen > 0 ){ - /* Write the Data */ - SyMemcpy(pData,(void *)zRaw,(sxu32)nDatalen); - } - return UNQLITE_OK; -} -/* - * Allocate as much overflow page we need to store the cell payload. - */ -static int lhCellWriteOvflPayload(lhcell *pCell,const void *pKey,sxu32 nKeylen,...) -{ - lhpage *pPage = pCell->pPage; - lhash_kv_engine *pEngine = pPage->pHash; - unqlite_page *pOvfl,*pFirst,*pNew; - const unsigned char *zPtr,*zEnd; - unsigned char *zRaw,*zRawEnd; - sxu32 nAvail; - va_list ap; - int rc; - /* Acquire a new overflow page */ - rc = lhAcquirePage(pEngine,&pOvfl); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Acquire a writer lock */ - rc = pEngine->pIo->xWrite(pOvfl); - if( rc != UNQLITE_OK ){ - return rc; - } - pFirst = pOvfl; - /* Link */ - pCell->iOvfl = pOvfl->pgno; - /* Update the cell header */ - SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4/*Hash*/ + 4/*Key*/ + 8/*Data*/ + 2 /*Next cell*/],pCell->iOvfl); - /* Start the write process */ - zPtr = (const unsigned char *)pKey; - zEnd = &zPtr[nKeylen]; - SyBigEndianPack64(pOvfl->zData,0); /* Next overflow page on the chain */ - zRaw = &pOvfl->zData[8/* Next ovfl page*/ + 8 /* Data page */ + 2 /* Data offset*/]; - zRawEnd = &pOvfl->zData[pEngine->iPageSize]; - pNew = pOvfl; - /* Write the key */ - for(;;){ - if( zPtr >= zEnd ){ - break; - } - if( zRaw >= zRawEnd ){ - /* Acquire a new page */ - rc = lhAcquirePage(pEngine,&pNew); - if( rc != UNQLITE_OK ){ - return rc; - } - rc = pEngine->pIo->xWrite(pNew); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Link */ - SyBigEndianPack64(pOvfl->zData,pNew->pgno); - pEngine->pIo->xPageUnref(pOvfl); - SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */ - pOvfl = pNew; - zRaw = &pNew->zData[8]; - zRawEnd = &pNew->zData[pEngine->iPageSize]; - } - nAvail = (sxu32)(zRawEnd-zRaw); - nKeylen = (sxu32)(zEnd-zPtr); - if( nKeylen > nAvail ){ - nKeylen = nAvail; - } - SyMemcpy((const void *)zPtr,(void *)zRaw,nKeylen); - /* Synchronize pointers */ - zPtr += nKeylen; - zRaw += nKeylen; - } - rc = UNQLITE_OK; - va_start(ap,nKeylen); - pCell->iDataPage = pNew->pgno; - pCell->iDataOfft = (sxu16)(zRaw-pNew->zData); - /* Write the data page and its offset */ - SyBigEndianPack64(&pFirst->zData[8/*Next ovfl*/],pCell->iDataPage); - SyBigEndianPack16(&pFirst->zData[8/*Next ovfl*/+8/*Data page*/],pCell->iDataOfft); - /* Write data */ - for(;;){ - const void *pData; - sxu32 nDatalen; - sxu64 nData; - pData = va_arg(ap,const void *); - nData = va_arg(ap,sxu64); - if( pData == 0 ){ - /* No more chunks */ - break; - } - /* Write this chunk */ - zPtr = (const unsigned char *)pData; - zEnd = &zPtr[nData]; - for(;;){ - if( zPtr >= zEnd ){ - break; - } - if( zRaw >= zRawEnd ){ - /* Acquire a new page */ - rc = lhAcquirePage(pEngine,&pNew); - if( rc != UNQLITE_OK ){ - va_end(ap); - return rc; - } - rc = pEngine->pIo->xWrite(pNew); - if( rc != UNQLITE_OK ){ - va_end(ap); - return rc; - } - /* Link */ - SyBigEndianPack64(pOvfl->zData,pNew->pgno); - pEngine->pIo->xPageUnref(pOvfl); - SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */ - pOvfl = pNew; - zRaw = &pNew->zData[8]; - zRawEnd = &pNew->zData[pEngine->iPageSize]; - } - nAvail = (sxu32)(zRawEnd-zRaw); - nDatalen = (sxu32)(zEnd-zPtr); - if( nDatalen > nAvail ){ - nDatalen = nAvail; - } - SyMemcpy((const void *)zPtr,(void *)zRaw,nDatalen); - /* Synchronize pointers */ - zPtr += nDatalen; - zRaw += nDatalen; - } - } - /* Unref the overflow page */ - pEngine->pIo->xPageUnref(pOvfl); - va_end(ap); - return UNQLITE_OK; -} -/* - * Restore a page to the free list. - */ -static int lhRestorePage(lhash_kv_engine *pEngine,unqlite_page *pPage) -{ - int rc; - rc = pEngine->pIo->xWrite(pEngine->pHeader); - if( rc != UNQLITE_OK ){ - return rc; - } - rc = pEngine->pIo->xWrite(pPage); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Link to the list of free page */ - SyBigEndianPack64(pPage->zData,pEngine->nFreeList); - pEngine->nFreeList = pPage->pgno; - SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/],pEngine->nFreeList); - /* All done */ - return UNQLITE_OK; -} -/* - * Restore cell space and mark it as a free block. - */ -static int lhRestoreSpace(lhpage *pPage,sxu16 iOfft,sxu16 nByte) -{ - unsigned char *zRaw; - if( nByte < 4 ){ - /* At least 4 bytes of freespace must form a valid block */ - return UNQLITE_OK; - } - /* pEngine->pIo->xWrite() has been successfully called on this page */ - zRaw = &pPage->pRaw->zData[iOfft]; - /* Mark as a free block */ - SyBigEndianPack16(zRaw,pPage->sHdr.iFree); /* Offset of the next free block */ - zRaw += 2; - SyBigEndianPack16(zRaw,nByte); - /* Link */ - SyBigEndianPack16(&pPage->pRaw->zData[2/* offset of the first cell */],iOfft); - pPage->sHdr.iFree = iOfft; - pPage->nFree += nByte; - return UNQLITE_OK; -} -/* Forward declaration */ -static lhcell * lhFindSibeling(lhcell *pCell); -/* - * Unlink a cell. - */ -static int lhUnlinkCell(lhcell *pCell) -{ - lhash_kv_engine *pEngine = pCell->pPage->pHash; - lhpage *pPage = pCell->pPage; - sxu16 nByte = L_HASH_CELL_SZ; - lhcell *pPrev; - int rc; - rc = pEngine->pIo->xWrite(pPage->pRaw); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Bring the link */ - pPrev = lhFindSibeling(pCell); - if( pPrev ){ - pPrev->iNext = pCell->iNext; - /* Fix offsets in the page header */ - SyBigEndianPack16(&pPage->pRaw->zData[pPrev->iStart + 4/*Hash*/+4/*Key*/+8/*Data*/],pCell->iNext); - }else{ - /* First entry on this page (either master or slave) */ - pPage->sHdr.iOfft = pCell->iNext; - /* Update the page header */ - SyBigEndianPack16(pPage->pRaw->zData,pCell->iNext); - } - /* Restore cell space */ - if( pCell->iOvfl == 0 ){ - nByte += (sxu16)(pCell->nData + pCell->nKey); - } - lhRestoreSpace(pPage,pCell->iStart,nByte); - /* Discard the cell from the in-memory hashtable */ - lhCellDiscard(pCell); - return UNQLITE_OK; -} -/* - * Remove a cell and its paylod (key + data). - */ -static int lhRecordRemove(lhcell *pCell) -{ - lhash_kv_engine *pEngine = pCell->pPage->pHash; - int rc; - if( pCell->iOvfl > 0){ - /* Discard overflow pages */ - unqlite_page *pOvfl; - pgno iNext = pCell->iOvfl; - for(;;){ - /* Point to the overflow page */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iNext,&pOvfl); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Next page on the chain */ - SyBigEndianUnpack64(pOvfl->zData,&iNext); - /* Restore the page to the free list */ - rc = lhRestorePage(pEngine,pOvfl); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Unref */ - pEngine->pIo->xPageUnref(pOvfl); - if( iNext == 0 ){ - break; - } - } - } - /* Unlink the cell */ - rc = lhUnlinkCell(pCell); - return rc; -} -/* - * Find cell sibeling. - */ -static lhcell * lhFindSibeling(lhcell *pCell) -{ - lhpage *pPage = pCell->pPage->pMaster; - lhcell *pEntry; - pEntry = pPage->pFirst; - while( pEntry ){ - if( pEntry->pPage == pCell->pPage && pEntry->iNext == pCell->iStart ){ - /* Sibeling found */ - return pEntry; - } - /* Point to the previous entry */ - pEntry = pEntry->pPrev; - } - /* Last inserted cell */ - return 0; -} -/* - * Move a cell to a new location with its new data. - */ -static int lhMoveLocalCell( - lhcell *pCell, - sxu16 iOfft, - const void *pData, - unqlite_int64 nData - ) -{ - sxu16 iKeyOfft = pCell->iStart + L_HASH_CELL_SZ; - lhpage *pPage = pCell->pPage; - lhcell *pSibeling; - pSibeling = lhFindSibeling(pCell); - if( pSibeling ){ - /* Fix link */ - SyBigEndianPack16(&pPage->pRaw->zData[pSibeling->iStart + 4/*Hash*/+4/*Key*/+8/*Data*/],pCell->iNext); - pSibeling->iNext = pCell->iNext; - }else{ - /* First cell, update page header only */ - SyBigEndianPack16(pPage->pRaw->zData,pCell->iNext); - pPage->sHdr.iOfft = pCell->iNext; - } - /* Set the new offset */ - pCell->iStart = iOfft; - pCell->nData = (sxu64)nData; - /* Write the cell payload */ - lhCellWriteLocalPayload(pCell,(const void *)&pPage->pRaw->zData[iKeyOfft],pCell->nKey,pData,nData); - /* Finally write the cell header */ - lhCellWriteHeader(pCell); - /* All done */ - return UNQLITE_OK; -} -/* - * Overwrite an existing record. - */ -static int lhRecordOverwrite( - lhcell *pCell, - const void *pData,unqlite_int64 nByte - ) -{ - lhash_kv_engine *pEngine = pCell->pPage->pHash; - unsigned char *zRaw,*zRawEnd,*zPayload; - const unsigned char *zPtr,*zEnd; - unqlite_page *pOvfl,*pOld,*pNew; - lhpage *pPage = pCell->pPage; - sxu32 nAvail; - pgno iOvfl; - int rc; - /* Acquire a writer lock on this page */ - rc = pEngine->pIo->xWrite(pPage->pRaw); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pCell->iOvfl == 0 ){ - /* Local payload, try to deal with the free space issues */ - zPayload = &pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey]; - if( pCell->nData == (sxu64)nByte ){ - /* Best scenario, simply a memcpy operation */ - SyMemcpy(pData,(void *)zPayload,(sxu32)nByte); - }else if( (sxu64)nByte < pCell->nData ){ - /* Shorter data, not so ugly */ - SyMemcpy(pData,(void *)zPayload,(sxu32)nByte); - /* Update the cell header */ - SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],nByte); - /* Restore freespace */ - lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ + pCell->nKey + nByte),(sxu16)(pCell->nData - nByte)); - /* New data size */ - pCell->nData = (sxu64)nByte; - }else{ - sxu16 iOfft = 0; /* cc warning */ - /* Check if another chunk is available for this cell */ - rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ + pCell->nKey + nByte,&iOfft); - if( rc != UNQLITE_OK ){ - /* Transfer the payload to an overflow page */ - rc = lhCellWriteOvflPayload(pCell,&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ],pCell->nKey,pData,nByte,0); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Update the cell header */ - SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],(sxu64)nByte); - /* Restore freespace */ - lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ),(sxu16)(pCell->nKey + pCell->nData)); - /* New data size */ - pCell->nData = (sxu64)nByte; - }else{ - sxu16 iOldOfft = pCell->iStart; - sxu32 iOld = (sxu32)pCell->nData; - /* Space is available, transfer the cell */ - lhMoveLocalCell(pCell,iOfft,pData,nByte); - /* Restore cell space */ - lhRestoreSpace(pPage,iOldOfft,(sxu16)(L_HASH_CELL_SZ + pCell->nKey + iOld)); - } - } - return UNQLITE_OK; - } - /* Point to the overflow page */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pCell->iDataPage,&pOvfl); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Relase all old overflow pages first */ - SyBigEndianUnpack64(pOvfl->zData,&iOvfl); - pOld = pOvfl; - for(;;){ - if( iOvfl == 0 ){ - /* No more overflow pages on the chain */ - break; - } - /* Point to the target page */ - if( UNQLITE_OK != pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOld) ){ - /* Not so fatal if something goes wrong here */ - break; - } - /* Next overflow page to be released */ - SyBigEndianUnpack64(pOld->zData,&iOvfl); - if( pOld != pOvfl ){ /* xx: chm is maniac */ - /* Restore the page to the free list */ - lhRestorePage(pEngine,pOld); - /* Unref */ - pEngine->pIo->xPageUnref(pOld); - } - } - /* Point to the data offset */ - zRaw = &pOvfl->zData[pCell->iDataOfft]; - zRawEnd = &pOvfl->zData[pEngine->iPageSize]; - /* The data to be stored */ - zPtr = (const unsigned char *)pData; - zEnd = &zPtr[nByte]; - /* Start the overwrite process */ - /* Acquire a writer lock */ - rc = pEngine->pIo->xWrite(pOvfl); - if( rc != UNQLITE_OK ){ - return rc; - } - SyBigEndianPack64(pOvfl->zData,0); - for(;;){ - sxu32 nLen; - if( zPtr >= zEnd ){ - break; - } - if( zRaw >= zRawEnd ){ - /* Acquire a new page */ - rc = lhAcquirePage(pEngine,&pNew); - if( rc != UNQLITE_OK ){ - return rc; - } - rc = pEngine->pIo->xWrite(pNew); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Link */ - SyBigEndianPack64(pOvfl->zData,pNew->pgno); - pEngine->pIo->xPageUnref(pOvfl); - SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */ - pOvfl = pNew; - zRaw = &pNew->zData[8]; - zRawEnd = &pNew->zData[pEngine->iPageSize]; - } - nAvail = (sxu32)(zRawEnd-zRaw); - nLen = (sxu32)(zEnd-zPtr); - if( nLen > nAvail ){ - nLen = nAvail; - } - SyMemcpy((const void *)zPtr,(void *)zRaw,nLen); - /* Synchronize pointers */ - zPtr += nLen; - zRaw += nLen; - } - /* Unref the last overflow page */ - pEngine->pIo->xPageUnref(pOvfl); - /* Finally, update the cell header */ - pCell->nData = (sxu64)nByte; - SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData); - /* All done */ - return UNQLITE_OK; -} -/* - * Append data to an existing record. - */ -static int lhRecordAppend( - lhcell *pCell, - const void *pData,unqlite_int64 nByte - ) -{ - lhash_kv_engine *pEngine = pCell->pPage->pHash; - const unsigned char *zPtr,*zEnd; - lhpage *pPage = pCell->pPage; - unsigned char *zRaw,*zRawEnd; - unqlite_page *pOvfl,*pNew; - sxu64 nDatalen; - sxu32 nAvail; - pgno iOvfl; - int rc; - if( pCell->nData + nByte < pCell->nData ){ - /* Overflow */ - pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow"); - return UNQLITE_LIMIT; - } - /* Acquire a writer lock on this page */ - rc = pEngine->pIo->xWrite(pPage->pRaw); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pCell->iOvfl == 0 ){ - sxu16 iOfft = 0; /* cc warning */ - /* Local payload, check for a bigger place */ - rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ + pCell->nKey + pCell->nData + nByte,&iOfft); - if( rc != UNQLITE_OK ){ - /* Transfer the payload to an overflow page */ - rc = lhCellWriteOvflPayload(pCell, - &pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ],pCell->nKey, - (const void *)&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey],pCell->nData, - pData,nByte, - 0); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Update the cell header */ - SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData + nByte); - /* Restore freespace */ - lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ),(sxu16)(pCell->nKey + pCell->nData)); - /* New data size */ - pCell->nData += nByte; - }else{ - sxu16 iOldOfft = pCell->iStart; - sxu32 iOld = (sxu32)pCell->nData; - SyBlob sWorker; - SyBlobInit(&sWorker,&pEngine->sAllocator); - /* Copy the old data */ - rc = SyBlobAppend(&sWorker,(const void *)&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey],(sxu32)pCell->nData); - if( rc == SXRET_OK ){ - /* Append the new data */ - rc = SyBlobAppend(&sWorker,pData,(sxu32)nByte); - } - if( rc != UNQLITE_OK ){ - SyBlobRelease(&sWorker); - return rc; - } - /* Space is available, transfer the cell */ - lhMoveLocalCell(pCell,iOfft,SyBlobData(&sWorker),(unqlite_int64)SyBlobLength(&sWorker)); - /* Restore cell space */ - lhRestoreSpace(pPage,iOldOfft,(sxu16)(L_HASH_CELL_SZ + pCell->nKey + iOld)); - /* All done */ - SyBlobRelease(&sWorker); - } - return UNQLITE_OK; - } - /* Point to the overflow page which hold the data */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pCell->iDataPage,&pOvfl); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Next overflow page in the chain */ - SyBigEndianUnpack64(pOvfl->zData,&iOvfl); - /* Point to the end of the chunk */ - zRaw = &pOvfl->zData[pCell->iDataOfft]; - zRawEnd = &pOvfl->zData[pEngine->iPageSize]; - nDatalen = pCell->nData; - nAvail = (sxu32)(zRawEnd - zRaw); - for(;;){ - if( zRaw >= zRawEnd ){ - if( iOvfl == 0 ){ - /* Cant happen */ - pEngine->pIo->xErr(pEngine->pIo->pHandle,"Corrupt overflow page"); - return UNQLITE_CORRUPT; - } - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pNew); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Next overflow page on the chain */ - SyBigEndianUnpack64(pNew->zData,&iOvfl); - /* Unref the previous overflow page */ - pEngine->pIo->xPageUnref(pOvfl); - /* Point to the new chunk */ - zRaw = &pNew->zData[8]; - zRawEnd = &pNew->zData[pCell->pPage->pHash->iPageSize]; - nAvail = L_HASH_OVERFLOW_SIZE(pCell->pPage->pHash->iPageSize); - pOvfl = pNew; - } - if( (sxu64)nAvail > nDatalen ){ - zRaw += nDatalen; - break; - }else{ - nDatalen -= nAvail; - } - zRaw += nAvail; - } - /* Start the append process */ - zPtr = (const unsigned char *)pData; - zEnd = &zPtr[nByte]; - /* Acquire a writer lock */ - rc = pEngine->pIo->xWrite(pOvfl); - if( rc != UNQLITE_OK ){ - return rc; - } - for(;;){ - sxu32 nLen; - if( zPtr >= zEnd ){ - break; - } - if( zRaw >= zRawEnd ){ - /* Acquire a new page */ - rc = lhAcquirePage(pEngine,&pNew); - if( rc != UNQLITE_OK ){ - return rc; - } - rc = pEngine->pIo->xWrite(pNew); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Link */ - SyBigEndianPack64(pOvfl->zData,pNew->pgno); - pEngine->pIo->xPageUnref(pOvfl); - SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */ - pOvfl = pNew; - zRaw = &pNew->zData[8]; - zRawEnd = &pNew->zData[pEngine->iPageSize]; - } - nAvail = (sxu32)(zRawEnd-zRaw); - nLen = (sxu32)(zEnd-zPtr); - if( nLen > nAvail ){ - nLen = nAvail; - } - SyMemcpy((const void *)zPtr,(void *)zRaw,nLen); - /* Synchronize pointers */ - zPtr += nLen; - zRaw += nLen; - } - /* Unref the last overflow page */ - pEngine->pIo->xPageUnref(pOvfl); - /* Finally, update the cell header */ - pCell->nData += nByte; - SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData); - /* All done */ - return UNQLITE_OK; -} -/* - * A write privilege have been acquired on this page. - * Mark it as an empty page (No cells). - */ -static int lhSetEmptyPage(lhpage *pPage) -{ - unsigned char *zRaw = pPage->pRaw->zData; - lhphdr *pHeader = &pPage->sHdr; - sxu16 nByte; - int rc; - /* Acquire a writer lock */ - rc = pPage->pHash->pIo->xWrite(pPage->pRaw); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Offset of the first cell */ - SyBigEndianPack16(zRaw,0); - zRaw += 2; - /* Offset of the first free block */ - pHeader->iFree = L_HASH_PAGE_HDR_SZ; - SyBigEndianPack16(zRaw,L_HASH_PAGE_HDR_SZ); - zRaw += 2; - /* Slave page number */ - SyBigEndianPack64(zRaw,0); - zRaw += 8; - /* Fill the free block */ - SyBigEndianPack16(zRaw,0); /* Offset of the next free block */ - zRaw += 2; - nByte = (sxu16)L_HASH_MX_FREE_SPACE(pPage->pHash->iPageSize); - SyBigEndianPack16(zRaw,nByte); - pPage->nFree = nByte; - /* Do not add this page to the hot dirty list */ - pPage->pHash->pIo->xDontMkHot(pPage->pRaw); - return UNQLITE_OK; -} -/* Forward declaration */ -static int lhSlaveStore( - lhpage *pPage, - const void *pKey,sxu32 nKeyLen, - const void *pData,unqlite_int64 nDataLen, - sxu32 nHash - ); -/* - * Store a cell and its payload in a given page. - */ -static int lhStoreCell( - lhpage *pPage, /* Target page */ - const void *pKey,sxu32 nKeyLen, /* Payload: Key */ - const void *pData,unqlite_int64 nDataLen, /* Payload: Data */ - sxu32 nHash, /* Hash of the key */ - int auto_append /* Auto append a slave page if full */ - ) -{ - lhash_kv_engine *pEngine = pPage->pHash; - int iNeedOvfl = 0; /* Need overflow page for this cell and its payload*/ - lhcell *pCell; - sxu16 nOfft; - int rc; - /* Acquire a writer lock on this page first */ - rc = pEngine->pIo->xWrite(pPage->pRaw); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Check for a free block */ - rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ+nKeyLen+nDataLen,&nOfft); - if( rc != UNQLITE_OK ){ - /* Check for a free block to hold a single cell only (without payload) */ - rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ,&nOfft); - if( rc != UNQLITE_OK ){ - if( !auto_append ){ - /* A split must be done */ - return UNQLITE_FULL; - }else{ - /* Store this record in a slave page */ - rc = lhSlaveStore(pPage,pKey,nKeyLen,pData,nDataLen,nHash); - return rc; - } - } - iNeedOvfl = 1; - } - /* Allocate a new cell instance */ - pCell = lhNewCell(pEngine,pPage); - if( pCell == 0 ){ - pEngine->pIo->xErr(pEngine->pIo->pHandle,"KV store is running out of memory"); - return UNQLITE_NOMEM; - } - /* Fill-in the structure */ - pCell->iStart = nOfft; - pCell->nKey = nKeyLen; - pCell->nData = (sxu64)nDataLen; - pCell->nHash = nHash; - if( nKeyLen < 262144 /* 256 KB */ ){ - /* Keep the key in-memory for fast lookup */ - SyBlobAppend(&pCell->sKey,pKey,nKeyLen); - } - /* Link the cell */ - rc = lhInstallCell(pCell); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Write the payload */ - if( iNeedOvfl ){ - rc = lhCellWriteOvflPayload(pCell,pKey,nKeyLen,pData,nDataLen,0); - if( rc != UNQLITE_OK ){ - lhCellDiscard(pCell); - return rc; - } - }else{ - lhCellWriteLocalPayload(pCell,pKey,nKeyLen,pData,nDataLen); - } - /* Finally, Write the cell header */ - lhCellWriteHeader(pCell); - /* All done */ - return UNQLITE_OK; -} -/* - * Find a slave page capable of hosting the given amount. - */ -static int lhFindSlavePage(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft,lhpage **ppSlave) -{ - lhash_kv_engine *pEngine = pPage->pHash; - lhpage *pMaster = pPage->pMaster; - lhpage *pSlave = pMaster->pSlave; - unqlite_page *pRaw; - lhpage *pNew; - sxu16 iOfft; - sxi32 i; - int rc; - /* Look for an already attached slave page */ - for( i = 0 ; i < pMaster->iSlave ; ++i ){ - /* Find a free chunk big enough */ - rc = lhAllocateSpace(pSlave,L_HASH_CELL_SZ+nAmount,&iOfft); - if( rc != UNQLITE_OK ){ - /* A space for cell header only */ - rc = lhAllocateSpace(pSlave,L_HASH_CELL_SZ,&iOfft); - } - if( rc == UNQLITE_OK ){ - /* All done */ - if( pOfft ){ - *pOfft = iOfft; - } - *ppSlave = pSlave; - return UNQLITE_OK; - } - /* Point to the next slave page */ - pSlave = pSlave->pNextSlave; - } - /* Acquire a new slave page */ - rc = lhAcquirePage(pEngine,&pRaw); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Last slave page */ - pSlave = pMaster->pSlave; - if( pSlave == 0 ){ - /* First slave page */ - pSlave = pMaster; - } - /* Initialize the page */ - pNew = lhNewPage(pEngine,pRaw,pMaster); - if( pNew == 0 ){ - return UNQLITE_NOMEM; - } - /* Mark as an empty page */ - rc = lhSetEmptyPage(pNew); - if( rc != UNQLITE_OK ){ - goto fail; - } - if( pOfft ){ - /* Look for a free block */ - if( UNQLITE_OK != lhAllocateSpace(pNew,L_HASH_CELL_SZ+nAmount,&iOfft) ){ - /* Cell header only */ - lhAllocateSpace(pNew,L_HASH_CELL_SZ,&iOfft); /* Never fail */ - } - *pOfft = iOfft; - } - /* Link this page to the previous slave page */ - rc = pEngine->pIo->xWrite(pSlave->pRaw); - if( rc != UNQLITE_OK ){ - goto fail; - } - /* Reflect in the page header */ - SyBigEndianPack64(&pSlave->pRaw->zData[2/*Cell offset*/+2/*Free block offset*/],pRaw->pgno); - pSlave->sHdr.iSlave = pRaw->pgno; - /* All done */ - *ppSlave = pNew; - return UNQLITE_OK; -fail: - pEngine->pIo->xPageUnref(pNew->pRaw); /* pNew will be released in this call */ - return rc; - -} -/* - * Perform a store operation in a slave page. - */ -static int lhSlaveStore( - lhpage *pPage, /* Master page */ - const void *pKey,sxu32 nKeyLen, /* Payload: key */ - const void *pData,unqlite_int64 nDataLen, /* Payload: data */ - sxu32 nHash /* Hash of the key */ - ) -{ - lhpage *pSlave; - int rc; - /* Find a slave page */ - rc = lhFindSlavePage(pPage,nKeyLen + nDataLen,0,&pSlave); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Perform the insertion in the slave page */ - rc = lhStoreCell(pSlave,pKey,nKeyLen,pData,nDataLen,nHash,1); - return rc; -} -/* - * Transfer a cell to a new page (either a master or slave). - */ -static int lhTransferCell(lhcell *pTarget,lhpage *pPage) -{ - lhcell *pCell; - sxu16 nOfft; - int rc; - /* Check for a free block to hold a single cell only */ - rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ,&nOfft); - if( rc != UNQLITE_OK ){ - /* Store in a slave page */ - rc = lhFindSlavePage(pPage,L_HASH_CELL_SZ,&nOfft,&pPage); - if( rc != UNQLITE_OK ){ - return rc; - } - } - /* Allocate a new cell instance */ - pCell = lhNewCell(pPage->pHash,pPage); - if( pCell == 0 ){ - return UNQLITE_NOMEM; - } - /* Fill-in the structure */ - pCell->iStart = nOfft; - pCell->nData = pTarget->nData; - pCell->nKey = pTarget->nKey; - pCell->iOvfl = pTarget->iOvfl; - pCell->iDataOfft = pTarget->iDataOfft; - pCell->iDataPage = pTarget->iDataPage; - pCell->nHash = pTarget->nHash; - SyBlobDup(&pTarget->sKey,&pCell->sKey); - /* Link the cell */ - rc = lhInstallCell(pCell); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Finally, Write the cell header */ - lhCellWriteHeader(pCell); - /* All done */ - return UNQLITE_OK; -} -/* - * Perform a page split. - */ -static int lhPageSplit( - lhpage *pOld, /* Page to be split */ - lhpage *pNew, /* New page */ - pgno split_bucket, /* Current split bucket */ - pgno high_mask /* High mask (Max split bucket - 1) */ - ) -{ - lhcell *pCell,*pNext; - SyBlob sWorker; - pgno iBucket; - int rc; - SyBlobInit(&sWorker,&pOld->pHash->sAllocator); - /* Perform the split */ - pCell = pOld->pList; - for( ;; ){ - if( pCell == 0 ){ - /* No more cells */ - break; - } - /* Obtain the new logical bucket */ - iBucket = pCell->nHash & high_mask; - pNext = pCell->pNext; - if( iBucket != split_bucket){ - rc = UNQLITE_OK; - if( pCell->iOvfl ){ - /* Transfer the cell only */ - rc = lhTransferCell(pCell,pNew); - }else{ - /* Transfer the cell and its payload */ - SyBlobReset(&sWorker); - if( SyBlobLength(&pCell->sKey) < 1 ){ - /* Consume the key */ - rc = lhConsumeCellkey(pCell,unqliteDataConsumer,&pCell->sKey,0); - if( rc != UNQLITE_OK ){ - goto fail; - } - } - /* Consume the data (Very small data < 65k) */ - rc = lhConsumeCellData(pCell,unqliteDataConsumer,&sWorker); - if( rc != UNQLITE_OK ){ - goto fail; - } - /* Perform the transfer */ - rc = lhStoreCell( - pNew, - SyBlobData(&pCell->sKey),(int)SyBlobLength(&pCell->sKey), - SyBlobData(&sWorker),SyBlobLength(&sWorker), - pCell->nHash, - 1 - ); - } - if( rc != UNQLITE_OK ){ - goto fail; - } - /* Discard the cell from the old page */ - lhUnlinkCell(pCell); - } - /* Point to the next cell */ - pCell = pNext; - } - /* All done */ - rc = UNQLITE_OK; -fail: - SyBlobRelease(&sWorker); - return rc; -} -/* - * Perform the infamous linear hash split operation. - */ -static int lhSplit(lhash_kv_engine *pEngine) -{ - lhash_bmap_rec *pRec; - lhpage *pOld,*pNew; - unqlite_page *pRaw; - int rc; - /* Get the real page number of the bucket to split */ - pRec = lhMapFindBucket(pEngine,pEngine->split_bucket); - if( pRec == 0 ){ - /* Can't happen */ - return UNQLITE_CORRUPT; - } - /* Load the page to be split */ - rc = lhLoadPage(pEngine,pRec->iReal,0,&pOld,0); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Request a new page */ - rc = lhAcquirePage(pEngine,&pRaw); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Initialize the page */ - pNew = lhNewPage(pEngine,pRaw,0); - if( pNew == 0 ){ - return UNQLITE_NOMEM; - } - /* Mark as an empty page */ - rc = lhSetEmptyPage(pNew); - if( rc != UNQLITE_OK ){ - goto fail; - } - /* Install and write the logical map record */ - rc = lhMapWriteRecord(pEngine, - pEngine->split_bucket + pEngine->max_split_bucket, - pRaw->pgno - ); - if( rc != UNQLITE_OK ){ - goto fail; - } - /* Perform the split */ - rc = lhPageSplit(pOld,pNew,pEngine->split_bucket,pEngine->nmax_split_nucket - 1); - if( rc != UNQLITE_OK ){ - goto fail; - } - /* Update the database header */ - pEngine->split_bucket++; - /* Acquire a writer lock on the first page */ - rc = pEngine->pIo->xWrite(pEngine->pHeader); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pEngine->split_bucket >= pEngine->max_split_bucket ){ - /* Increment the generation number */ - pEngine->split_bucket = 0; - pEngine->max_split_bucket = pEngine->nmax_split_nucket; - pEngine->nmax_split_nucket <<= 1; - if( !pEngine->nmax_split_nucket ){ - /* If this happen to your installation, please tell us */ - pEngine->pIo->xErr(pEngine->pIo->pHandle,"Database page (64-bit integer) limit reached"); - return UNQLITE_LIMIT; - } - /* Reflect in the page header */ - SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/],pEngine->split_bucket); - SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/+8/*Split bucket*/],pEngine->max_split_bucket); - }else{ - /* Modify only the split bucket */ - SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/],pEngine->split_bucket); - } - /* All done */ - return UNQLITE_OK; -fail: - pEngine->pIo->xPageUnref(pNew->pRaw); - return rc; -} -/* - * Store a record in the target page. - */ -static int lhRecordInstall( - lhpage *pPage, /* Target page */ - sxu32 nHash, /* Hash of the key */ - const void *pKey,sxu32 nKeyLen, /* Payload: Key */ - const void *pData,unqlite_int64 nDataLen /* Payload: Data */ - ) -{ - int rc; - rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,0); - if( rc == UNQLITE_FULL ){ - /* Split */ - rc = lhSplit(pPage->pHash); - if( rc == UNQLITE_OK ){ - /* Perform the store */ - rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,1); - } - } - return rc; -} -/* - * Insert a record (Either overwrite or append operation) in our database. - */ -static int lh_record_insert( - unqlite_kv_engine *pKv, /* KV store */ - const void *pKey,sxu32 nKeyLen, /* Payload: Key */ - const void *pData,unqlite_int64 nDataLen, /* Payload: data */ - int is_append /* True for an append operation */ - ) -{ - lhash_kv_engine *pEngine = (lhash_kv_engine *)pKv; - lhash_bmap_rec *pRec; - unqlite_page *pRaw; - lhpage *pPage; - lhcell *pCell; - pgno iBucket; - sxu32 nHash; - int rc; - - /* Acquire the first page (DB hash Header) so that everything gets loaded autmatically */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Compute the hash of the key first */ - nHash = pEngine->xHash(pKey,(sxu32)nKeyLen); - /* Extract the logical bucket number */ - iBucket = nHash & (pEngine->nmax_split_nucket - 1); - if( iBucket >= pEngine->split_bucket + pEngine->max_split_bucket ){ - /* Low mask */ - iBucket = nHash & (pEngine->max_split_bucket - 1); - } - /* Map the logical bucket number to real page number */ - pRec = lhMapFindBucket(pEngine,iBucket); - if( pRec == 0 ){ - /* Request a new page */ - rc = lhAcquirePage(pEngine,&pRaw); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Initialize the page */ - pPage = lhNewPage(pEngine,pRaw,0); - if( pPage == 0 ){ - return UNQLITE_NOMEM; - } - /* Mark as an empty page */ - rc = lhSetEmptyPage(pPage); - if( rc != UNQLITE_OK ){ - pEngine->pIo->xPageUnref(pRaw); /* pPage will be released during this call */ - return rc; - } - /* Store the cell */ - rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,1); - if( rc == UNQLITE_OK ){ - /* Install and write the logical map record */ - rc = lhMapWriteRecord(pEngine,iBucket,pRaw->pgno); - } - pEngine->pIo->xPageUnref(pRaw); - return rc; - }else{ - /* Load the page */ - rc = lhLoadPage(pEngine,pRec->iReal,0,&pPage,0); - if( rc != UNQLITE_OK ){ - /* IO error, unlikely scenario */ - return rc; - } - /* Do not add this page to the hot dirty list */ - pEngine->pIo->xDontMkHot(pPage->pRaw); - /* Lookup for the cell */ - pCell = lhFindCell(pPage,pKey,(sxu32)nKeyLen,nHash); - if( pCell == 0 ){ - /* Create the record */ - rc = lhRecordInstall(pPage,nHash,pKey,nKeyLen,pData,nDataLen); - }else{ - if( is_append ){ - /* Append operation */ - rc = lhRecordAppend(pCell,pData,nDataLen); - }else{ - /* Overwrite old value */ - rc = lhRecordOverwrite(pCell,pData,nDataLen); - } - } - pEngine->pIo->xPageUnref(pPage->pRaw); - } - return rc; -} -/* - * Replace method. - */ -static int lhash_kv_replace( - unqlite_kv_engine *pKv, - const void *pKey,int nKeyLen, - const void *pData,unqlite_int64 nDataLen - ) -{ - int rc; - rc = lh_record_insert(pKv,pKey,(sxu32)nKeyLen,pData,nDataLen,0); - return rc; -} -/* - * Append method. - */ -static int lhash_kv_append( - unqlite_kv_engine *pKv, - const void *pKey,int nKeyLen, - const void *pData,unqlite_int64 nDataLen - ) -{ - int rc; - rc = lh_record_insert(pKv,pKey,(sxu32)nKeyLen,pData,nDataLen,1); - return rc; -} -/* - * Write the hash header (Page one). - */ -static int lhash_write_header(lhash_kv_engine *pEngine,unqlite_page *pHeader) -{ - unsigned char *zRaw = pHeader->zData; - lhash_bmap_page *pMap; - - pEngine->pHeader = pHeader; - /* 4 byte magic number */ - SyBigEndianPack32(zRaw,pEngine->nMagic); - zRaw += 4; - /* 4 byte hash value to identify a valid hash function */ - SyBigEndianPack32(zRaw,pEngine->xHash(L_HASH_WORD,sizeof(L_HASH_WORD)-1)); - zRaw += 4; - /* List of free pages: Empty */ - SyBigEndianPack64(zRaw,0); - zRaw += 8; - /* Current split bucket */ - SyBigEndianPack64(zRaw,pEngine->split_bucket); - zRaw += 8; - /* Maximum split bucket */ - SyBigEndianPack64(zRaw,pEngine->max_split_bucket); - zRaw += 8; - /* Initialiaze the bucket map */ - pMap = &pEngine->sPageMap; - /* Fill in the structure */ - pMap->iNum = pHeader->pgno; - /* Next page in the bucket map */ - SyBigEndianPack64(zRaw,0); - zRaw += 8; - /* Total number of records in the bucket map */ - SyBigEndianPack32(zRaw,0); - zRaw += 4; - pMap->iPtr = (sxu16)(zRaw - pHeader->zData); - /* All done */ - return UNQLITE_OK; - } -/* - * Exported: xOpen() method. - */ -static int lhash_kv_open(unqlite_kv_engine *pEngine,pgno dbSize) -{ - lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine; - unqlite_page *pHeader; - int rc; - if( dbSize < 1 ){ - /* A new database, create the header */ - rc = pEngine->pIo->xNew(pEngine->pIo->pHandle,&pHeader); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Acquire a writer lock */ - rc = pEngine->pIo->xWrite(pHeader); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Write the hash header */ - rc = lhash_write_header(pHash,pHeader); - if( rc != UNQLITE_OK ){ - return rc; - } - }else{ - /* Acquire the page one of the database */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,&pHeader); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Read the database header */ - rc = lhash_read_header(pHash,pHeader); - if( rc != UNQLITE_OK ){ - return rc; - } - } - return UNQLITE_OK; -} -/* - * Release a master or slave page. (xUnpin callback). - */ -static void lhash_page_release(void *pUserData) -{ - lhpage *pPage = (lhpage *)pUserData; - lhash_kv_engine *pEngine = pPage->pHash; - lhcell *pNext,*pCell = pPage->pList; - unqlite_page *pRaw = pPage->pRaw; - sxu32 n; - /* Drop in-memory cells */ - for( n = 0 ; n < pPage->nCell ; ++n ){ - pNext = pCell->pNext; - SyBlobRelease(&pCell->sKey); - /* Release the cell instance */ - SyMemBackendPoolFree(&pEngine->sAllocator,(void *)pCell); - /* Point to the next entry */ - pCell = pNext; - } - if( pPage->apCell ){ - /* Release the cell table */ - SyMemBackendFree(&pEngine->sAllocator,(void *)pPage->apCell); - } - /* Finally, release the whole page */ - SyMemBackendPoolFree(&pEngine->sAllocator,pPage); - pRaw->pUserData = 0; -} -/* - * Default hash function (DJB). - */ -static sxu32 lhash_bin_hash(const void *pSrc,sxu32 nLen) -{ - register unsigned char *zIn = (unsigned char *)pSrc; - unsigned char *zEnd; - sxu32 nH = 5381; - if( nLen > 2048 /* 2K */ ){ - nLen = 2048; - } - zEnd = &zIn[nLen]; - for(;;){ - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - } - return nH; -} -/* - * Exported: xInit() method. - * Initialize the Key value storage engine. - */ -static int lhash_kv_init(unqlite_kv_engine *pEngine,int iPageSize) -{ - lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine; - int rc; - - /* This structure is always zeroed, go to the initialization directly */ - SyMemBackendInitFromParent(&pHash->sAllocator,unqliteExportMemBackend()); -#if defined(UNQLITE_ENABLE_THREADS) - /* Already protected by the upper layers */ - SyMemBackendDisbaleMutexing(&pHash->sAllocator); -#endif - pHash->iPageSize = iPageSize; - /* Default hash function */ - pHash->xHash = lhash_bin_hash; - /* Default comparison function */ - pHash->xCmp = SyMemcmp; - /* Allocate a new record map */ - pHash->nBuckSize = 32; - pHash->apMap = (lhash_bmap_rec **)SyMemBackendAlloc(&pHash->sAllocator,pHash->nBuckSize *sizeof(lhash_bmap_rec *)); - if( pHash->apMap == 0 ){ - rc = UNQLITE_NOMEM; - goto err; - } - /* Zero the table */ - SyZero(pHash->apMap,pHash->nBuckSize * sizeof(lhash_bmap_rec *)); - /* Linear hashing components */ - pHash->split_bucket = 0; /* Logical not real bucket number */ - pHash->max_split_bucket = 1; - pHash->nmax_split_nucket = 2; - pHash->nMagic = L_HASH_MAGIC; - /* Install the cache unpin and reload callbacks */ - pHash->pIo->xSetUnpin(pHash->pIo->pHandle,lhash_page_release); - pHash->pIo->xSetReload(pHash->pIo->pHandle,lhash_page_release); - return UNQLITE_OK; -err: - SyMemBackendRelease(&pHash->sAllocator); - return rc; -} -/* - * Exported: xRelease() method. - * Release the Key value storage engine. - */ -static void lhash_kv_release(unqlite_kv_engine *pEngine) -{ - lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine; - /* Release the private memory backend */ - SyMemBackendRelease(&pHash->sAllocator); -} -/* - * Exported: xConfig() method. - * Configure the linear hash KV store. - */ -static int lhash_kv_config(unqlite_kv_engine *pEngine,int op,va_list ap) -{ - lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine; - int rc = UNQLITE_OK; - switch(op){ - case UNQLITE_KV_CONFIG_HASH_FUNC: { - /* Default hash function */ - if( pHash->nBuckRec > 0 ){ - /* Locked operation */ - rc = UNQLITE_LOCKED; - }else{ - ProcHash xHash = va_arg(ap,ProcHash); - if( xHash ){ - pHash->xHash = xHash; - } - } - break; - } - case UNQLITE_KV_CONFIG_CMP_FUNC: { - /* Default comparison function */ - ProcCmp xCmp = va_arg(ap,ProcCmp); - if( xCmp ){ - pHash->xCmp = xCmp; - } - break; - } - default: - /* Unknown OP */ - rc = UNQLITE_UNKNOWN; - break; - } - return rc; -} -/* - * Each public cursor is identified by an instance of this structure. - */ -typedef struct lhash_kv_cursor lhash_kv_cursor; -struct lhash_kv_cursor -{ - unqlite_kv_engine *pStore; /* Must be first */ - /* Private fields */ - int iState; /* Current state of the cursor */ - int is_first; /* True to read the database header */ - lhcell *pCell; /* Current cell we are processing */ - unqlite_page *pRaw; /* Raw disk page */ - lhash_bmap_rec *pRec; /* Logical to real bucket map */ -}; -/* - * Possible state of the cursor - */ -#define L_HASH_CURSOR_STATE_NEXT_PAGE 1 /* Next page in the list */ -#define L_HASH_CURSOR_STATE_CELL 2 /* Processing Cell */ -#define L_HASH_CURSOR_STATE_DONE 3 /* Cursor does not point to anything */ -/* - * Initialize the cursor. - */ -static void lhInitCursor(unqlite_kv_cursor *pPtr) -{ - lhash_kv_engine *pEngine = (lhash_kv_engine *)pPtr->pStore; - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr; - /* Init */ - pCur->iState = L_HASH_CURSOR_STATE_NEXT_PAGE; - pCur->pCell = 0; - pCur->pRec = pEngine->pFirst; - pCur->pRaw = 0; - pCur->is_first = 1; -} -/* - * Point to the next page on the database. - */ -static int lhCursorNextPage(lhash_kv_cursor *pPtr) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr; - lhash_bmap_rec *pRec; - lhpage *pPage; - int rc; - for(;;){ - pRec = pCur->pRec; - if( pRec == 0 ){ - pCur->iState = L_HASH_CURSOR_STATE_DONE; - return UNQLITE_DONE; - } - if( pPtr->iState == L_HASH_CURSOR_STATE_CELL && pPtr->pRaw ){ - /* Unref this page */ - pCur->pStore->pIo->xPageUnref(pPtr->pRaw); - pPtr->pRaw = 0; - } - /* Advance the map cursor */ - pCur->pRec = pRec->pPrev; /* Not a bug, reverse link */ - /* Load the next page on the list */ - rc = lhLoadPage((lhash_kv_engine *)pCur->pStore,pRec->iReal,0,&pPage,0); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pPage->pList ){ - /* Reflect the change */ - pCur->pCell = pPage->pList; - pCur->iState = L_HASH_CURSOR_STATE_CELL; - pCur->pRaw = pPage->pRaw; - break; - } - /* Empty page, discard this page and continue */ - pPage->pHash->pIo->xPageUnref(pPage->pRaw); - } - return UNQLITE_OK; -} -/* - * Point to the previous page on the database. - */ -static int lhCursorPrevPage(lhash_kv_cursor *pPtr) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr; - lhash_bmap_rec *pRec; - lhpage *pPage; - int rc; - for(;;){ - pRec = pCur->pRec; - if( pRec == 0 ){ - pCur->iState = L_HASH_CURSOR_STATE_DONE; - return UNQLITE_DONE; - } - if( pPtr->iState == L_HASH_CURSOR_STATE_CELL && pPtr->pRaw ){ - /* Unref this page */ - pCur->pStore->pIo->xPageUnref(pPtr->pRaw); - pPtr->pRaw = 0; - } - /* Advance the map cursor */ - pCur->pRec = pRec->pNext; /* Not a bug, reverse link */ - /* Load the previous page on the list */ - rc = lhLoadPage((lhash_kv_engine *)pCur->pStore,pRec->iReal,0,&pPage,0); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pPage->pFirst ){ - /* Reflect the change */ - pCur->pCell = pPage->pFirst; - pCur->iState = L_HASH_CURSOR_STATE_CELL; - pCur->pRaw = pPage->pRaw; - break; - } - /* Discard this page and continue */ - pPage->pHash->pIo->xPageUnref(pPage->pRaw); - } - return UNQLITE_OK; -} -/* - * Is a valid cursor. - */ -static int lhCursorValid(unqlite_kv_cursor *pPtr) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr; - return (pCur->iState == L_HASH_CURSOR_STATE_CELL) && pCur->pCell; -} -/* - * Point to the first record. - */ -static int lhCursorFirst(unqlite_kv_cursor *pCursor) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; - lhash_kv_engine *pEngine = (lhash_kv_engine *)pCursor->pStore; - int rc; - if( pCur->is_first ){ - /* Read the database header first */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0); - if( rc != UNQLITE_OK ){ - return rc; - } - pCur->is_first = 0; - } - /* Point to the first map record */ - pCur->pRec = pEngine->pFirst; - /* Load the cells */ - rc = lhCursorNextPage(pCur); - return rc; -} -/* - * Point to the last record. - */ -static int lhCursorLast(unqlite_kv_cursor *pCursor) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; - lhash_kv_engine *pEngine = (lhash_kv_engine *)pCursor->pStore; - int rc; - if( pCur->is_first ){ - /* Read the database header first */ - rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0); - if( rc != UNQLITE_OK ){ - return rc; - } - pCur->is_first = 0; - } - /* Point to the last map record */ - pCur->pRec = pEngine->pList; - /* Load the cells */ - rc = lhCursorPrevPage(pCur); - return rc; -} -/* - * Reset the cursor. - */ -static void lhCursorReset(unqlite_kv_cursor *pCursor) -{ - lhCursorFirst(pCursor); -} -/* - * Point to the next record. - */ -static int lhCursorNext(unqlite_kv_cursor *pCursor) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; - lhcell *pCell; - int rc; - if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){ - /* Load the cells of the next page */ - rc = lhCursorNextPage(pCur); - return rc; - } - pCell = pCur->pCell; - pCur->pCell = pCell->pNext; - if( pCur->pCell == 0 ){ - /* Load the cells of the next page */ - rc = lhCursorNextPage(pCur); - return rc; - } - return UNQLITE_OK; -} -/* - * Point to the previous record. - */ -static int lhCursorPrev(unqlite_kv_cursor *pCursor) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; - lhcell *pCell; - int rc; - if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){ - /* Load the cells of the previous page */ - rc = lhCursorPrevPage(pCur); - return rc; - } - pCell = pCur->pCell; - pCur->pCell = pCell->pPrev; - if( pCur->pCell == 0 ){ - /* Load the cells of the previous page */ - rc = lhCursorPrevPage(pCur); - return rc; - } - return UNQLITE_OK; -} -/* - * Return key length. - */ -static int lhCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; - lhcell *pCell; - - if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){ - /* Invalid state */ - return UNQLITE_INVALID; - } - /* Point to the target cell */ - pCell = pCur->pCell; - /* Return key length */ - *pLen = (int)pCell->nKey; - return UNQLITE_OK; -} -/* - * Return data length. - */ -static int lhCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; - lhcell *pCell; - - if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){ - /* Invalid state */ - return UNQLITE_INVALID; - } - /* Point to the target cell */ - pCell = pCur->pCell; - /* Return data length */ - *pLen = (unqlite_int64)pCell->nData; - return UNQLITE_OK; -} -/* - * Consume the key. - */ -static int lhCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; - lhcell *pCell; - int rc; - if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){ - /* Invalid state */ - return UNQLITE_INVALID; - } - /* Point to the target cell */ - pCell = pCur->pCell; - if( SyBlobLength(&pCell->sKey) > 0 ){ - /* Consume the key directly */ - rc = xConsumer(SyBlobData(&pCell->sKey),SyBlobLength(&pCell->sKey),pUserData); - }else{ - /* Very large key */ - rc = lhConsumeCellkey(pCell,xConsumer,pUserData,0); - } - return rc; -} -/* - * Consume the data. - */ -static int lhCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; - lhcell *pCell; - int rc; - if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){ - /* Invalid state */ - return UNQLITE_INVALID; - } - /* Point to the target cell */ - pCell = pCur->pCell; - /* Consume the data */ - rc = lhConsumeCellData(pCell,xConsumer,pUserData); - return rc; -} -/* - * Find a partiuclar record. - */ -static int lhCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; - int rc; - /* Perform a lookup */ - rc = lhRecordLookup((lhash_kv_engine *)pCur->pStore,pKey,nByte,&pCur->pCell); - if( rc != UNQLITE_OK ){ - SXUNUSED(iPos); - pCur->pCell = 0; - pCur->iState = L_HASH_CURSOR_STATE_DONE; - return rc; - } - pCur->iState = L_HASH_CURSOR_STATE_CELL; - return UNQLITE_OK; -} -/* - * Remove a particular record. - */ -static int lhCursorDelete(unqlite_kv_cursor *pCursor) -{ - lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; - lhcell *pCell; - int rc; - if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){ - /* Invalid state */ - return UNQLITE_INVALID; - } - /* Point to the target cell */ - pCell = pCur->pCell; - /* Point to the next entry */ - pCur->pCell = pCell->pNext; - /* Perform the deletion */ - rc = lhRecordRemove(pCell); - return rc; -} -/* - * Export the linear-hash storage engine. - */ -UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void) -{ - static const unqlite_kv_methods sDiskStore = { - "hash", /* zName */ - sizeof(lhash_kv_engine), /* szKv */ - sizeof(lhash_kv_cursor), /* szCursor */ - 1, /* iVersion */ - lhash_kv_init, /* xInit */ - lhash_kv_release, /* xRelease */ - lhash_kv_config, /* xConfig */ - lhash_kv_open, /* xOpen */ - lhash_kv_replace, /* xReplace */ - lhash_kv_append, /* xAppend */ - lhInitCursor, /* xCursorInit */ - lhCursorSeek, /* xSeek */ - lhCursorFirst, /* xFirst */ - lhCursorLast, /* xLast */ - lhCursorValid, /* xValid */ - lhCursorNext, /* xNext */ - lhCursorPrev, /* xPrev */ - lhCursorDelete, /* xDelete */ - lhCursorKeyLength, /* xKeyLength */ - lhCursorKey, /* xKey */ - lhCursorDataLength, /* xDataLength */ - lhCursorData, /* xData */ - lhCursorReset, /* xReset */ - 0 /* xRelease */ - }; - return &sDiskStore; -} -/* - * ---------------------------------------------------------- - * File: mem_kv.c - * MD5: 32e2610c95f53038114d9566f0d0489e - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: mem_kv.c v1.7 Win7 2012-11-28 01:41 stable $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif -/* - * This file implements an in-memory key value storage engine for unQLite. - * Note that this storage engine does not support transactions. - * - * Normaly, I (chm@symisc.net) planned to implement a red-black tree - * which is suitable for this kind of operation, but due to the lack - * of time, I decided to implement a tunned hashtable which everybody - * know works very well for this kind of operation. - * Again, I insist on a red-black tree implementation for future version - * of Unqlite. - */ -/* Forward declaration */ -typedef struct mem_hash_kv_engine mem_hash_kv_engine; -/* - * Each record is storead in an instance of the following structure. - */ -typedef struct mem_hash_record mem_hash_record; -struct mem_hash_record -{ - mem_hash_kv_engine *pEngine; /* Storage engine */ - sxu32 nHash; /* Hash of the key */ - const void *pKey; /* Key */ - sxu32 nKeyLen; /* Key size (Max 1GB) */ - const void *pData; /* Data */ - sxu32 nDataLen; /* Data length (Max 4GB) */ - mem_hash_record *pNext,*pPrev; /* Link to other records */ - mem_hash_record *pNextHash,*pPrevHash; /* Collision link */ -}; -/* - * Each in-memory KV engine is represented by an instance - * of the following structure. - */ -struct mem_hash_kv_engine -{ - const unqlite_kv_io *pIo; /* IO methods: MUST be first */ - /* Private data */ - SyMemBackend sAlloc; /* Private memory allocator */ - ProcHash xHash; /* Default hash function */ - ProcCmp xCmp; /* Default comparison function */ - sxu32 nRecord; /* Total number of records */ - sxu32 nBucket; /* Bucket size: Must be a power of two */ - mem_hash_record **apBucket; /* Hash bucket */ - mem_hash_record *pFirst; /* First inserted entry */ - mem_hash_record *pLast; /* Last inserted entry */ -}; -/* - * Allocate a new hash record. - */ -static mem_hash_record * MemHashNewRecord( - mem_hash_kv_engine *pEngine, - const void *pKey,int nKey, - const void *pData,unqlite_int64 nData, - sxu32 nHash - ) -{ - SyMemBackend *pAlloc = &pEngine->sAlloc; - mem_hash_record *pRecord; - void *pDupData; - sxu32 nByte; - char *zPtr; - - /* Total number of bytes to alloc */ - nByte = sizeof(mem_hash_record) + nKey; - /* Allocate a new instance */ - pRecord = (mem_hash_record *)SyMemBackendAlloc(pAlloc,nByte); - if( pRecord == 0 ){ - return 0; - } - pDupData = (void *)SyMemBackendAlloc(pAlloc,(sxu32)nData); - if( pDupData == 0 ){ - SyMemBackendFree(pAlloc,pRecord); - return 0; - } - zPtr = (char *)pRecord; - zPtr += sizeof(mem_hash_record); - /* Zero the structure */ - SyZero(pRecord,sizeof(mem_hash_record)); - /* Fill in the structure */ - pRecord->pEngine = pEngine; - pRecord->nDataLen = (sxu32)nData; - pRecord->nKeyLen = (sxu32)nKey; - pRecord->nHash = nHash; - SyMemcpy(pKey,zPtr,pRecord->nKeyLen); - pRecord->pKey = (const void *)zPtr; - SyMemcpy(pData,pDupData,pRecord->nDataLen); - pRecord->pData = pDupData; - /* All done */ - return pRecord; -} -/* - * Install a given record in the hashtable. - */ -static void MemHashLinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pRecord) -{ - sxu32 nBucket = pRecord->nHash & (pEngine->nBucket - 1); - pRecord->pNextHash = pEngine->apBucket[nBucket]; - if( pEngine->apBucket[nBucket] ){ - pEngine->apBucket[nBucket]->pPrevHash = pRecord; - } - pEngine->apBucket[nBucket] = pRecord; - if( pEngine->pFirst == 0 ){ - pEngine->pFirst = pEngine->pLast = pRecord; - }else{ - MACRO_LD_PUSH(pEngine->pLast,pRecord); - } - pEngine->nRecord++; -} -/* - * Unlink a given record from the hashtable. - */ -static void MemHashUnlinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pEntry) -{ - sxu32 nBucket = pEntry->nHash & (pEngine->nBucket - 1); - SyMemBackend *pAlloc = &pEngine->sAlloc; - if( pEntry->pPrevHash == 0 ){ - pEngine->apBucket[nBucket] = pEntry->pNextHash; - }else{ - pEntry->pPrevHash->pNextHash = pEntry->pNextHash; - } - if( pEntry->pNextHash ){ - pEntry->pNextHash->pPrevHash = pEntry->pPrevHash; - } - MACRO_LD_REMOVE(pEngine->pLast,pEntry); - if( pEntry == pEngine->pFirst ){ - pEngine->pFirst = pEntry->pPrev; - } - pEngine->nRecord--; - /* Release the entry */ - SyMemBackendFree(pAlloc,(void *)pEntry->pData); - SyMemBackendFree(pAlloc,pEntry); /* Key is also stored here */ -} -/* - * Perform a lookup for a given entry. - */ -static mem_hash_record * MemHashGetEntry( - mem_hash_kv_engine *pEngine, - const void *pKey,int nKeyLen - ) -{ - mem_hash_record *pEntry; - sxu32 nHash,nBucket; - /* Hash the entry */ - nHash = pEngine->xHash(pKey,(sxu32)nKeyLen); - nBucket = nHash & (pEngine->nBucket - 1); - pEntry = pEngine->apBucket[nBucket]; - for(;;){ - if( pEntry == 0 ){ - break; - } - if( pEntry->nHash == nHash && pEntry->nKeyLen == (sxu32)nKeyLen && - pEngine->xCmp(pEntry->pKey,pKey,pEntry->nKeyLen) == 0 ){ - return pEntry; - } - pEntry = pEntry->pNextHash; - } - /* No such entry */ - return 0; -} -/* - * Rehash all the entries in the given table. - */ -static int MemHashGrowTable(mem_hash_kv_engine *pEngine) -{ - sxu32 nNewSize = pEngine->nBucket << 1; - mem_hash_record *pEntry; - mem_hash_record **apNew; - sxu32 n,iBucket; - /* Allocate a new larger table */ - apNew = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc, nNewSize * sizeof(mem_hash_record *)); - if( apNew == 0 ){ - /* Not so fatal, simply a performance hit */ - return UNQLITE_OK; - } - /* Zero the new table */ - SyZero((void *)apNew, nNewSize * sizeof(mem_hash_record *)); - /* Rehash all entries */ - n = 0; - pEntry = pEngine->pLast; - for(;;){ - - /* Loop one */ - if( n >= pEngine->nRecord ){ - break; - } - pEntry->pNextHash = pEntry->pPrevHash = 0; - /* Install in the new bucket */ - iBucket = pEntry->nHash & (nNewSize - 1); - pEntry->pNextHash = apNew[iBucket]; - if( apNew[iBucket] ){ - apNew[iBucket]->pPrevHash = pEntry; - } - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - n++; - - /* Loop two */ - if( n >= pEngine->nRecord ){ - break; - } - pEntry->pNextHash = pEntry->pPrevHash = 0; - /* Install in the new bucket */ - iBucket = pEntry->nHash & (nNewSize - 1); - pEntry->pNextHash = apNew[iBucket]; - if( apNew[iBucket] ){ - apNew[iBucket]->pPrevHash = pEntry; - } - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - n++; - - /* Loop three */ - if( n >= pEngine->nRecord ){ - break; - } - pEntry->pNextHash = pEntry->pPrevHash = 0; - /* Install in the new bucket */ - iBucket = pEntry->nHash & (nNewSize - 1); - pEntry->pNextHash = apNew[iBucket]; - if( apNew[iBucket] ){ - apNew[iBucket]->pPrevHash = pEntry; - } - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - n++; - - /* Loop four */ - if( n >= pEngine->nRecord ){ - break; - } - pEntry->pNextHash = pEntry->pPrevHash = 0; - /* Install in the new bucket */ - iBucket = pEntry->nHash & (nNewSize - 1); - pEntry->pNextHash = apNew[iBucket]; - if( apNew[iBucket] ){ - apNew[iBucket]->pPrevHash = pEntry; - } - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - n++; - } - /* Release the old table and reflect the change */ - SyMemBackendFree(&pEngine->sAlloc,(void *)pEngine->apBucket); - pEngine->apBucket = apNew; - pEngine->nBucket = nNewSize; - return UNQLITE_OK; -} -/* - * Exported Interfaces. - */ -/* - * Each public cursor is identified by an instance of this structure. - */ -typedef struct mem_hash_cursor mem_hash_cursor; -struct mem_hash_cursor -{ - unqlite_kv_engine *pStore; /* Must be first */ - /* Private fields */ - mem_hash_record *pCur; /* Current hash record */ -}; -/* - * Initialize the cursor. - */ -static void MemHashInitCursor(unqlite_kv_cursor *pCursor) -{ - mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore; - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - /* Point to the first inserted entry */ - pMem->pCur = pEngine->pFirst; -} -/* - * Point to the first entry. - */ -static int MemHashCursorFirst(unqlite_kv_cursor *pCursor) -{ - mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore; - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - pMem->pCur = pEngine->pFirst; - return UNQLITE_OK; -} -/* - * Point to the last entry. - */ -static int MemHashCursorLast(unqlite_kv_cursor *pCursor) -{ - mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore; - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - pMem->pCur = pEngine->pLast; - return UNQLITE_OK; -} -/* - * is a Valid Cursor. - */ -static int MemHashCursorValid(unqlite_kv_cursor *pCursor) -{ - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - return pMem->pCur != 0 ? 1 : 0; -} -/* - * Point to the next entry. - */ -static int MemHashCursorNext(unqlite_kv_cursor *pCursor) -{ - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - if( pMem->pCur == 0){ - return UNQLITE_EOF; - } - pMem->pCur = pMem->pCur->pPrev; /* Reverse link: Not a Bug */ - return UNQLITE_OK; -} -/* - * Point to the previous entry. - */ -static int MemHashCursorPrev(unqlite_kv_cursor *pCursor) -{ - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - if( pMem->pCur == 0){ - return UNQLITE_EOF; - } - pMem->pCur = pMem->pCur->pNext; /* Reverse link: Not a Bug */ - return UNQLITE_OK; -} -/* - * Return key length. - */ -static int MemHashCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen) -{ - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - if( pMem->pCur == 0){ - return UNQLITE_EOF; - } - *pLen = (int)pMem->pCur->nKeyLen; - return UNQLITE_OK; -} -/* - * Return data length. - */ -static int MemHashCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen) -{ - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - if( pMem->pCur == 0 ){ - return UNQLITE_EOF; - } - *pLen = pMem->pCur->nDataLen; - return UNQLITE_OK; -} -/* - * Consume the key. - */ -static int MemHashCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData) -{ - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - int rc; - if( pMem->pCur == 0){ - return UNQLITE_EOF; - } - /* Invoke the callback */ - rc = xConsumer(pMem->pCur->pKey,pMem->pCur->nKeyLen,pUserData); - /* Callback result */ - return rc; -} -/* - * Consume the data. - */ -static int MemHashCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData) -{ - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - int rc; - if( pMem->pCur == 0){ - return UNQLITE_EOF; - } - /* Invoke the callback */ - rc = xConsumer(pMem->pCur->pData,pMem->pCur->nDataLen,pUserData); - /* Callback result */ - return rc; -} -/* - * Reset the cursor. - */ -static void MemHashCursorReset(unqlite_kv_cursor *pCursor) -{ - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - pMem->pCur = ((mem_hash_kv_engine *)pCursor->pStore)->pFirst; -} -/* - * Remove a particular record. - */ -static int MemHashCursorDelete(unqlite_kv_cursor *pCursor) -{ - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - mem_hash_record *pNext; - if( pMem->pCur == 0 ){ - /* Cursor does not point to anything */ - return UNQLITE_NOTFOUND; - } - pNext = pMem->pCur->pPrev; - /* Perform the deletion */ - MemHashUnlinkRecord(pMem->pCur->pEngine,pMem->pCur); - /* Point to the next entry */ - pMem->pCur = pNext; - return UNQLITE_OK; -} -/* - * Find a particular record. - */ -static int MemHashCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos) -{ - mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore; - mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor; - /* Perform the lookup */ - pMem->pCur = MemHashGetEntry(pEngine,pKey,nByte); - if( pMem->pCur == 0 ){ - if( iPos != UNQLITE_CURSOR_MATCH_EXACT ){ - /* noop; */ - } - /* No such record */ - return UNQLITE_NOTFOUND; - } - return UNQLITE_OK; -} -/* - * Builtin hash function. - */ -static sxu32 MemHashFunc(const void *pSrc,sxu32 nLen) -{ - register unsigned char *zIn = (unsigned char *)pSrc; - unsigned char *zEnd; - sxu32 nH = 5381; - zEnd = &zIn[nLen]; - for(;;){ - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - } - return nH; -} -/* Default bucket size */ -#define MEM_HASH_BUCKET_SIZE 64 -/* Default fill factor */ -#define MEM_HASH_FILL_FACTOR 4 /* or 3 */ -/* - * Initialize the in-memory storage engine. - */ -static int MemHashInit(unqlite_kv_engine *pKvEngine,int iPageSize) -{ - mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine; - /* Note that this instance is already zeroed */ - /* Memory backend */ - SyMemBackendInitFromParent(&pEngine->sAlloc,unqliteExportMemBackend()); -#if defined(UNQLITE_ENABLE_THREADS) - /* Already protected by the upper layers */ - SyMemBackendDisbaleMutexing(&pEngine->sAlloc); -#endif - /* Default hash & comparison function */ - pEngine->xHash = MemHashFunc; - pEngine->xCmp = SyMemcmp; - /* Allocate a new bucket */ - pEngine->apBucket = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *)); - if( pEngine->apBucket == 0 ){ - SXUNUSED(iPageSize); /* cc warning */ - return UNQLITE_NOMEM; - } - /* Zero the bucket */ - SyZero(pEngine->apBucket,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *)); - pEngine->nRecord = 0; - pEngine->nBucket = MEM_HASH_BUCKET_SIZE; - return UNQLITE_OK; -} -/* - * Release the in-memory storage engine. - */ -static void MemHashRelease(unqlite_kv_engine *pKvEngine) -{ - mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine; - /* Release the private memory backend */ - SyMemBackendRelease(&pEngine->sAlloc); -} -/* - * Configure the in-memory storage engine. - */ -static int MemHashConfigure(unqlite_kv_engine *pKvEngine,int iOp,va_list ap) -{ - mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine; - int rc = UNQLITE_OK; - switch(iOp){ - case UNQLITE_KV_CONFIG_HASH_FUNC:{ - /* Use a default hash function */ - if( pEngine->nRecord > 0 ){ - rc = UNQLITE_LOCKED; - }else{ - ProcHash xHash = va_arg(ap,ProcHash); - if( xHash ){ - pEngine->xHash = xHash; - } - } - break; - } - case UNQLITE_KV_CONFIG_CMP_FUNC: { - /* Default comparison function */ - ProcCmp xCmp = va_arg(ap,ProcCmp); - if( xCmp ){ - pEngine->xCmp = xCmp; - } - break; - } - default: - /* Unknown configuration option */ - rc = UNQLITE_UNKNOWN; - } - return rc; -} -/* - * Replace method. - */ -static int MemHashReplace( - unqlite_kv_engine *pKv, - const void *pKey,int nKeyLen, - const void *pData,unqlite_int64 nDataLen - ) -{ - mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv; - mem_hash_record *pRecord; - if( nDataLen > SXU32_HIGH ){ - /* Database limit */ - pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached"); - return UNQLITE_LIMIT; - } - /* Fetch the record first */ - pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen); - if( pRecord == 0 ){ - /* Allocate a new record */ - pRecord = MemHashNewRecord(pEngine, - pKey,nKeyLen, - pData,nDataLen, - pEngine->xHash(pKey,nKeyLen) - ); - if( pRecord == 0 ){ - return UNQLITE_NOMEM; - } - /* Link the entry */ - MemHashLinkRecord(pEngine,pRecord); - if( (pEngine->nRecord >= pEngine->nBucket * MEM_HASH_FILL_FACTOR) && pEngine->nRecord < 100000 ){ - /* Rehash the table */ - MemHashGrowTable(pEngine); - } - }else{ - sxu32 nData = (sxu32)nDataLen; - void *pNew; - /* Replace an existing record */ - if( nData == pRecord->nDataLen ){ - /* No need to free the old chunk */ - pNew = (void *)pRecord->pData; - }else{ - pNew = SyMemBackendAlloc(&pEngine->sAlloc,nData); - if( pNew == 0 ){ - return UNQLITE_NOMEM; - } - /* Release the old data */ - SyMemBackendFree(&pEngine->sAlloc,(void *)pRecord->pData); - } - /* Reflect the change */ - pRecord->nDataLen = nData; - SyMemcpy(pData,pNew,nData); - pRecord->pData = pNew; - } - return UNQLITE_OK; -} -/* - * Append method. - */ -static int MemHashAppend( - unqlite_kv_engine *pKv, - const void *pKey,int nKeyLen, - const void *pData,unqlite_int64 nDataLen - ) -{ - mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv; - mem_hash_record *pRecord; - if( nDataLen > SXU32_HIGH ){ - /* Database limit */ - pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached"); - return UNQLITE_LIMIT; - } - /* Fetch the record first */ - pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen); - if( pRecord == 0 ){ - /* Allocate a new record */ - pRecord = MemHashNewRecord(pEngine, - pKey,nKeyLen, - pData,nDataLen, - pEngine->xHash(pKey,nKeyLen) - ); - if( pRecord == 0 ){ - return UNQLITE_NOMEM; - } - /* Link the entry */ - MemHashLinkRecord(pEngine,pRecord); - if( pEngine->nRecord * MEM_HASH_FILL_FACTOR >= pEngine->nBucket && pEngine->nRecord < 100000 ){ - /* Rehash the table */ - MemHashGrowTable(pEngine); - } - }else{ - unqlite_int64 nNew = pRecord->nDataLen + nDataLen; - void *pOld = (void *)pRecord->pData; - sxu32 nData; - char *zNew; - /* Append data to the existing record */ - if( nNew > SXU32_HIGH ){ - /* Overflow */ - pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow"); - return UNQLITE_LIMIT; - } - nData = (sxu32)nNew; - /* Allocate bigger chunk */ - zNew = (char *)SyMemBackendRealloc(&pEngine->sAlloc,pOld,nData); - if( zNew == 0 ){ - return UNQLITE_NOMEM; - } - /* Reflect the change */ - SyMemcpy(pData,&zNew[pRecord->nDataLen],(sxu32)nDataLen); - pRecord->pData = (const void *)zNew; - pRecord->nDataLen = nData; - } - return UNQLITE_OK; -} -/* - * Export the in-memory storage engine. - */ -UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void) -{ - static const unqlite_kv_methods sMemStore = { - "mem", /* zName */ - sizeof(mem_hash_kv_engine), /* szKv */ - sizeof(mem_hash_cursor), /* szCursor */ - 1, /* iVersion */ - MemHashInit, /* xInit */ - MemHashRelease, /* xRelease */ - MemHashConfigure, /* xConfig */ - 0, /* xOpen */ - MemHashReplace, /* xReplace */ - MemHashAppend, /* xAppend */ - MemHashInitCursor, /* xCursorInit */ - MemHashCursorSeek, /* xSeek */ - MemHashCursorFirst, /* xFirst */ - MemHashCursorLast, /* xLast */ - MemHashCursorValid, /* xValid */ - MemHashCursorNext, /* xNext */ - MemHashCursorPrev, /* xPrev */ - MemHashCursorDelete, /* xDelete */ - MemHashCursorKeyLength, /* xKeyLength */ - MemHashCursorKey, /* xKey */ - MemHashCursorDataLength, /* xDataLength */ - MemHashCursorData, /* xData */ - MemHashCursorReset, /* xReset */ - 0 /* xRelease */ - }; - return &sMemStore; -} -/* - * ---------------------------------------------------------- - * File: os.c - * MD5: e7ad243c3cd9e6aac5fba406eedb7766 - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: os.c v1.0 FreeBSD 2012-11-12 21:27 devel $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif -/* OS interfaces abstraction layers: Mostly SQLite3 source tree */ -/* -** The following routines are convenience wrappers around methods -** of the unqlite_file object. This is mostly just syntactic sugar. All -** of this would be completely automatic if UnQLite were coded using -** C++ instead of plain old C. -*/ -UNQLITE_PRIVATE int unqliteOsRead(unqlite_file *id, void *pBuf, unqlite_int64 amt, unqlite_int64 offset) -{ - return id->pMethods->xRead(id, pBuf, amt, offset); -} -UNQLITE_PRIVATE int unqliteOsWrite(unqlite_file *id, const void *pBuf, unqlite_int64 amt, unqlite_int64 offset) -{ - return id->pMethods->xWrite(id, pBuf, amt, offset); -} -UNQLITE_PRIVATE int unqliteOsTruncate(unqlite_file *id, unqlite_int64 size) -{ - return id->pMethods->xTruncate(id, size); -} -UNQLITE_PRIVATE int unqliteOsSync(unqlite_file *id, int flags) -{ - return id->pMethods->xSync(id, flags); -} -UNQLITE_PRIVATE int unqliteOsFileSize(unqlite_file *id, unqlite_int64 *pSize) -{ - return id->pMethods->xFileSize(id, pSize); -} -UNQLITE_PRIVATE int unqliteOsLock(unqlite_file *id, int lockType) -{ - return id->pMethods->xLock(id, lockType); -} -UNQLITE_PRIVATE int unqliteOsUnlock(unqlite_file *id, int lockType) -{ - return id->pMethods->xUnlock(id, lockType); -} -UNQLITE_PRIVATE int unqliteOsCheckReservedLock(unqlite_file *id, int *pResOut) -{ - return id->pMethods->xCheckReservedLock(id, pResOut); -} -UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id) -{ - if( id->pMethods->xSectorSize ){ - return id->pMethods->xSectorSize(id); - } - return UNQLITE_DEFAULT_SECTOR_SIZE; -} -/* -** The next group of routines are convenience wrappers around the -** VFS methods. -*/ -UNQLITE_PRIVATE int unqliteOsOpen( - unqlite_vfs *pVfs, - SyMemBackend *pAlloc, - const char *zPath, - unqlite_file **ppOut, - unsigned int flags -) -{ - unqlite_file *pFile; - int rc; - *ppOut = 0; - if( zPath == 0 ){ - /* May happen if dealing with an in-memory database */ - return SXERR_EMPTY; - } - /* Allocate a new instance */ - pFile = (unqlite_file *)SyMemBackendAlloc(pAlloc,sizeof(unqlite_file)+pVfs->szOsFile); - if( pFile == 0 ){ - return UNQLITE_NOMEM; - } - /* Zero the structure */ - SyZero(pFile,sizeof(unqlite_file)+pVfs->szOsFile); - /* Invoke the xOpen method of the underlying VFS */ - rc = pVfs->xOpen(pVfs, zPath, pFile, flags); - if( rc != UNQLITE_OK ){ - SyMemBackendFree(pAlloc,pFile); - pFile = 0; - } - *ppOut = pFile; - return rc; -} -UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId) -{ - int rc = UNQLITE_OK; - if( pId ){ - rc = pId->pMethods->xClose(pId); - SyMemBackendFree(pAlloc,pId); - } - return rc; -} -UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int dirSync){ - return pVfs->xDelete(pVfs, zPath, dirSync); -} -UNQLITE_PRIVATE int unqliteOsAccess( - unqlite_vfs *pVfs, - const char *zPath, - int flags, - int *pResOut -){ - return pVfs->xAccess(pVfs, zPath, flags, pResOut); -} -/* - * ---------------------------------------------------------- - * File: os_unix.c - * MD5: 5efd57d03f8fb988d081c5bcf5cc2998 - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: os_unix.c v1.3 FreeBSD 2013-04-05 01:10 devel $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif -/* - * Omit the whole layer from the build if compiling for platforms other than Unix (Linux, BSD, Solaris, OS X, etc.). - * Note: Mostly SQLite3 source tree. - */ -#if defined(__UNIXES__) -/** This file contains the VFS implementation for unix-like operating systems -** include Linux, MacOSX, *BSD, QNX, VxWorks, AIX, HPUX, and others. -** -** There are actually several different VFS implementations in this file. -** The differences are in the way that file locking is done. The default -** implementation uses Posix Advisory Locks. Alternative implementations -** use flock(), dot-files, various proprietary locking schemas, or simply -** skip locking all together. -** -** This source file is organized into divisions where the logic for various -** subfunctions is contained within the appropriate division. PLEASE -** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed -** in the correct division and should be clearly labeled. -** -*/ -/* -** standard include files. -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) -# include -#endif -/* -** Allowed values of unixFile.fsFlags -*/ -#define UNQLITE_FSFLAGS_IS_MSDOS 0x1 - -/* -** Default permissions when creating a new file -*/ -#ifndef UNQLITE_DEFAULT_FILE_PERMISSIONS -# define UNQLITE_DEFAULT_FILE_PERMISSIONS 0644 -#endif -/* - ** Default permissions when creating auto proxy dir - */ -#ifndef UNQLITE_DEFAULT_PROXYDIR_PERMISSIONS -# define UNQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755 -#endif -/* -** Maximum supported path-length. -*/ -#define MAX_PATHNAME 512 -/* -** Only set the lastErrno if the error code is a real error and not -** a normal expected return code of UNQLITE_BUSY or UNQLITE_OK -*/ -#define IS_LOCK_ERROR(x) ((x != UNQLITE_OK) && (x != UNQLITE_BUSY)) -/* Forward references */ -typedef struct unixInodeInfo unixInodeInfo; /* An i-node */ -typedef struct UnixUnusedFd UnixUnusedFd; /* An unused file descriptor */ -/* -** Sometimes, after a file handle is closed by SQLite, the file descriptor -** cannot be closed immediately. In these cases, instances of the following -** structure are used to store the file descriptor while waiting for an -** opportunity to either close or reuse it. -*/ -struct UnixUnusedFd { - int fd; /* File descriptor to close */ - int flags; /* Flags this file descriptor was opened with */ - UnixUnusedFd *pNext; /* Next unused file descriptor on same file */ -}; -/* -** The unixFile structure is subclass of unqlite3_file specific to the unix -** VFS implementations. -*/ -typedef struct unixFile unixFile; -struct unixFile { - const unqlite_io_methods *pMethod; /* Always the first entry */ - unixInodeInfo *pInode; /* Info about locks on this inode */ - int h; /* The file descriptor */ - int dirfd; /* File descriptor for the directory */ - unsigned char eFileLock; /* The type of lock held on this fd */ - int lastErrno; /* The unix errno from last I/O error */ - void *lockingContext; /* Locking style specific state */ - UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ - int fileFlags; /* Miscellanous flags */ - const char *zPath; /* Name of the file */ - unsigned fsFlags; /* cached details from statfs() */ -}; -/* -** The following macros define bits in unixFile.fileFlags -*/ -#define UNQLITE_WHOLE_FILE_LOCKING 0x0001 /* Use whole-file locking */ -/* -** Define various macros that are missing from some systems. -*/ -#ifndef O_LARGEFILE -# define O_LARGEFILE 0 -#endif -#ifndef O_NOFOLLOW -# define O_NOFOLLOW 0 -#endif -#ifndef O_BINARY -# define O_BINARY 0 -#endif -/* -** Helper functions to obtain and relinquish the global mutex. The -** global mutex is used to protect the unixInodeInfo and -** vxworksFileId objects used by this file, all of which may be -** shared by multiple threads. -** -** Function unixMutexHeld() is used to assert() that the global mutex -** is held when required. This function is only used as part of assert() -** statements. e.g. -** -** unixEnterMutex() -** assert( unixMutexHeld() ); -** unixEnterLeave() -*/ -static void unixEnterMutex(void){ -#ifdef UNQLITE_ENABLE_THREADS - const SyMutexMethods *pMutexMethods = SyMutexExportMethods(); - if( pMutexMethods ){ - SyMutex *pMutex = pMutexMethods->xNew(SXMUTEX_TYPE_STATIC_2); /* pre-allocated, never fail */ - SyMutexEnter(pMutexMethods,pMutex); - } -#endif /* UNQLITE_ENABLE_THREADS */ -} -static void unixLeaveMutex(void){ -#ifdef UNQLITE_ENABLE_THREADS - const SyMutexMethods *pMutexMethods = SyMutexExportMethods(); - if( pMutexMethods ){ - SyMutex *pMutex = pMutexMethods->xNew(SXMUTEX_TYPE_STATIC_2); /* pre-allocated, never fail */ - SyMutexLeave(pMutexMethods,pMutex); - } -#endif /* UNQLITE_ENABLE_THREADS */ -} -/* -** This routine translates a standard POSIX errno code into something -** useful to the clients of the unqlite3 functions. Specifically, it is -** intended to translate a variety of "try again" errors into UNQLITE_BUSY -** and a variety of "please close the file descriptor NOW" errors into -** UNQLITE_IOERR -** -** Errors during initialization of locks, or file system support for locks, -** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately. -*/ -static int unqliteErrorFromPosixError(int posixError, int unqliteIOErr) { - switch (posixError) { - case 0: - return UNQLITE_OK; - - case EAGAIN: - case ETIMEDOUT: - case EBUSY: - case EINTR: - case ENOLCK: - /* random NFS retry error, unless during file system support - * introspection, in which it actually means what it says */ - return UNQLITE_BUSY; - - case EACCES: - /* EACCES is like EAGAIN during locking operations, but not any other time*/ - return UNQLITE_BUSY; - - case EPERM: - return UNQLITE_PERM; - - case EDEADLK: - return UNQLITE_IOERR; - -#if EOPNOTSUPP!=ENOTSUP - case EOPNOTSUPP: - /* something went terribly awry, unless during file system support - * introspection, in which it actually means what it says */ -#endif -#ifdef ENOTSUP - case ENOTSUP: - /* invalid fd, unless during file system support introspection, in which - * it actually means what it says */ -#endif - case EIO: - case EBADF: - case EINVAL: - case ENOTCONN: - case ENODEV: - case ENXIO: - case ENOENT: - case ESTALE: - case ENOSYS: - /* these should force the client to close the file and reconnect */ - - default: - return unqliteIOErr; - } -} -/****************************************************************************** -*************************** Posix Advisory Locking **************************** -** -** POSIX advisory locks are broken by design. ANSI STD 1003.1 (1996) -** section 6.5.2.2 lines 483 through 490 specify that when a process -** sets or clears a lock, that operation overrides any prior locks set -** by the same process. It does not explicitly say so, but this implies -** that it overrides locks set by the same process using a different -** file descriptor. Consider this test case: -** -** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644); -** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644); -** -** Suppose ./file1 and ./file2 are really the same file (because -** one is a hard or symbolic link to the other) then if you set -** an exclusive lock on fd1, then try to get an exclusive lock -** on fd2, it works. I would have expected the second lock to -** fail since there was already a lock on the file due to fd1. -** But not so. Since both locks came from the same process, the -** second overrides the first, even though they were on different -** file descriptors opened on different file names. -** -** This means that we cannot use POSIX locks to synchronize file access -** among competing threads of the same process. POSIX locks will work fine -** to synchronize access for threads in separate processes, but not -** threads within the same process. -** -** To work around the problem, SQLite has to manage file locks internally -** on its own. Whenever a new database is opened, we have to find the -** specific inode of the database file (the inode is determined by the -** st_dev and st_ino fields of the stat structure that fstat() fills in) -** and check for locks already existing on that inode. When locks are -** created or removed, we have to look at our own internal record of the -** locks to see if another thread has previously set a lock on that same -** inode. -** -** (Aside: The use of inode numbers as unique IDs does not work on VxWorks. -** For VxWorks, we have to use the alternative unique ID system based on -** canonical filename and implemented in the previous division.) -** -** There is one locking structure -** per inode, so if the same inode is opened twice, both unixFile structures -** point to the same locking structure. The locking structure keeps -** a reference count (so we will know when to delete it) and a "cnt" -** field that tells us its internal lock status. cnt==0 means the -** file is unlocked. cnt==-1 means the file has an exclusive lock. -** cnt>0 means there are cnt shared locks on the file. -** -** Any attempt to lock or unlock a file first checks the locking -** structure. The fcntl() system call is only invoked to set a -** POSIX lock if the internal lock structure transitions between -** a locked and an unlocked state. -** -** But wait: there are yet more problems with POSIX advisory locks. -** -** If you close a file descriptor that points to a file that has locks, -** all locks on that file that are owned by the current process are -** released. To work around this problem, each unixInodeInfo object -** maintains a count of the number of pending locks on that inode. -** When an attempt is made to close an unixFile, if there are -** other unixFile open on the same inode that are holding locks, the call -** to close() the file descriptor is deferred until all of the locks clear. -** The unixInodeInfo structure keeps a list of file descriptors that need to -** be closed and that list is walked (and cleared) when the last lock -** clears. -** -** Yet another problem: LinuxThreads do not play well with posix locks. -** -** Many older versions of linux use the LinuxThreads library which is -** not posix compliant. Under LinuxThreads, a lock created by thread -** A cannot be modified or overridden by a different thread B. -** Only thread A can modify the lock. Locking behavior is correct -** if the appliation uses the newer Native Posix Thread Library (NPTL) -** on linux - with NPTL a lock created by thread A can override locks -** in thread B. But there is no way to know at compile-time which -** threading library is being used. So there is no way to know at -** compile-time whether or not thread A can override locks on thread B. -** One has to do a run-time check to discover the behavior of the -** current process. -** -*/ - -/* -** An instance of the following structure serves as the key used -** to locate a particular unixInodeInfo object. -*/ -struct unixFileId { - dev_t dev; /* Device number */ - ino_t ino; /* Inode number */ -}; -/* -** An instance of the following structure is allocated for each open -** inode. Or, on LinuxThreads, there is one of these structures for -** each inode opened by each thread. -** -** A single inode can have multiple file descriptors, so each unixFile -** structure contains a pointer to an instance of this object and this -** object keeps a count of the number of unixFile pointing to it. -*/ -struct unixInodeInfo { - struct unixFileId fileId; /* The lookup key */ - int nShared; /* Number of SHARED locks held */ - int eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ - int nRef; /* Number of pointers to this structure */ - int nLock; /* Number of outstanding file locks */ - UnixUnusedFd *pUnused; /* Unused file descriptors to close */ - unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ - unixInodeInfo *pPrev; /* .... doubly linked */ -}; - -static unixInodeInfo *inodeList = 0; -/* - * Local memory allocation stuff. - */ -static void * unqlite_malloc(sxu32 nByte) -{ - SyMemBackend *pAlloc; - void *p; - pAlloc = (SyMemBackend *)unqliteExportMemBackend(); - p = SyMemBackendAlloc(pAlloc,nByte); - return p; -} -static void unqlite_free(void *p) -{ - SyMemBackend *pAlloc; - pAlloc = (SyMemBackend *)unqliteExportMemBackend(); - SyMemBackendFree(pAlloc,p); -} -/* -** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. -** If all such file descriptors are closed without error, the list is -** cleared and UNQLITE_OK returned. -** -** Otherwise, if an error occurs, then successfully closed file descriptor -** entries are removed from the list, and UNQLITE_IOERR_CLOSE returned. -** not deleted and UNQLITE_IOERR_CLOSE returned. -*/ -static int closePendingFds(unixFile *pFile){ - int rc = UNQLITE_OK; - unixInodeInfo *pInode = pFile->pInode; - UnixUnusedFd *pError = 0; - UnixUnusedFd *p; - UnixUnusedFd *pNext; - for(p=pInode->pUnused; p; p=pNext){ - pNext = p->pNext; - if( close(p->fd) ){ - pFile->lastErrno = errno; - rc = UNQLITE_IOERR; - p->pNext = pError; - pError = p; - }else{ - unqlite_free(p); - } - } - pInode->pUnused = pError; - return rc; -} -/* -** Release a unixInodeInfo structure previously allocated by findInodeInfo(). -** -** The mutex entered using the unixEnterMutex() function must be held -** when this function is called. -*/ -static void releaseInodeInfo(unixFile *pFile){ - unixInodeInfo *pInode = pFile->pInode; - if( pInode ){ - pInode->nRef--; - if( pInode->nRef==0 ){ - closePendingFds(pFile); - if( pInode->pPrev ){ - pInode->pPrev->pNext = pInode->pNext; - }else{ - inodeList = pInode->pNext; - } - if( pInode->pNext ){ - pInode->pNext->pPrev = pInode->pPrev; - } - unqlite_free(pInode); - } - } -} -/* -** Given a file descriptor, locate the unixInodeInfo object that -** describes that file descriptor. Create a new one if necessary. The -** return value might be uninitialized if an error occurs. -** -** The mutex entered using the unixEnterMutex() function must be held -** when this function is called. -** -** Return an appropriate error code. -*/ -static int findInodeInfo( - unixFile *pFile, /* Unix file with file desc used in the key */ - unixInodeInfo **ppInode /* Return the unixInodeInfo object here */ -){ - int rc; /* System call return code */ - int fd; /* The file descriptor for pFile */ - struct unixFileId fileId; /* Lookup key for the unixInodeInfo */ - struct stat statbuf; /* Low-level file information */ - unixInodeInfo *pInode = 0; /* Candidate unixInodeInfo object */ - - /* Get low-level information about the file that we can used to - ** create a unique name for the file. - */ - fd = pFile->h; - rc = fstat(fd, &statbuf); - if( rc!=0 ){ - pFile->lastErrno = errno; -#ifdef EOVERFLOW - if( pFile->lastErrno==EOVERFLOW ) return UNQLITE_NOTIMPLEMENTED; -#endif - return UNQLITE_IOERR; - } - -#ifdef __APPLE__ - /* On OS X on an msdos filesystem, the inode number is reported - ** incorrectly for zero-size files. See ticket #3260. To work - ** around this problem (we consider it a bug in OS X, not SQLite) - ** we always increase the file size to 1 by writing a single byte - ** prior to accessing the inode number. The one byte written is - ** an ASCII 'S' character which also happens to be the first byte - ** in the header of every SQLite database. In this way, if there - ** is a race condition such that another thread has already populated - ** the first page of the database, no damage is done. - */ - if( statbuf.st_size==0 && (pFile->fsFlags & UNQLITE_FSFLAGS_IS_MSDOS)!=0 ){ - rc = write(fd, "S", 1); - if( rc!=1 ){ - pFile->lastErrno = errno; - return UNQLITE_IOERR; - } - rc = fstat(fd, &statbuf); - if( rc!=0 ){ - pFile->lastErrno = errno; - return UNQLITE_IOERR; - } - } -#endif - SyZero(&fileId,sizeof(fileId)); - fileId.dev = statbuf.st_dev; - fileId.ino = statbuf.st_ino; - pInode = inodeList; - while( pInode && SyMemcmp((const void *)&fileId,(const void *)&pInode->fileId, sizeof(fileId)) ){ - pInode = pInode->pNext; - } - if( pInode==0 ){ - pInode = (unixInodeInfo *)unqlite_malloc( sizeof(*pInode) ); - if( pInode==0 ){ - return UNQLITE_NOMEM; - } - SyZero(pInode,sizeof(*pInode)); - SyMemcpy((const void *)&fileId,(void *)&pInode->fileId,sizeof(fileId)); - pInode->nRef = 1; - pInode->pNext = inodeList; - pInode->pPrev = 0; - if( inodeList ) inodeList->pPrev = pInode; - inodeList = pInode; - }else{ - pInode->nRef++; - } - *ppInode = pInode; - return UNQLITE_OK; -} -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to UNQLITE_OK unless an I/O error occurs during lock checking. -*/ -static int unixCheckReservedLock(unqlite_file *id, int *pResOut){ - int rc = UNQLITE_OK; - int reserved = 0; - unixFile *pFile = (unixFile*)id; - - - unixEnterMutex(); /* Because pFile->pInode is shared across threads */ - - /* Check if a thread in this process holds such a lock */ - if( pFile->pInode->eFileLock>SHARED_LOCK ){ - reserved = 1; - } - - /* Otherwise see if some other process holds it. - */ - if( !reserved ){ - struct flock lock; - lock.l_whence = SEEK_SET; - lock.l_start = RESERVED_BYTE; - lock.l_len = 1; - lock.l_type = F_WRLCK; - if (-1 == fcntl(pFile->h, F_GETLK, &lock)) { - int tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - pFile->lastErrno = tErrno; - } else if( lock.l_type!=F_UNLCK ){ - reserved = 1; - } - } - - unixLeaveMutex(); - - *pResOut = reserved; - return rc; -} -/* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. Use the unqliteOsUnlock() -** routine to lower a locking level. -*/ -static int unixLock(unqlite_file *id, int eFileLock){ - /* The following describes the implementation of the various locks and - ** lock transitions in terms of the POSIX advisory shared and exclusive - ** lock primitives (called read-locks and write-locks below, to avoid - ** confusion with SQLite lock names). The algorithms are complicated - ** slightly in order to be compatible with unixdows systems simultaneously - ** accessing the same database file, in case that is ever required. - ** - ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved - ** byte', each single bytes at well known offsets, and the 'shared byte - ** range', a range of 510 bytes at a well known offset. - ** - ** To obtain a SHARED lock, a read-lock is obtained on the 'pending - ** byte'. If this is successful, a random byte from the 'shared byte - ** range' is read-locked and the lock on the 'pending byte' released. - ** - ** A process may only obtain a RESERVED lock after it has a SHARED lock. - ** A RESERVED lock is implemented by grabbing a write-lock on the - ** 'reserved byte'. - ** - ** A process may only obtain a PENDING lock after it has obtained a - ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock - ** on the 'pending byte'. This ensures that no new SHARED locks can be - ** obtained, but existing SHARED locks are allowed to persist. A process - ** does not have to obtain a RESERVED lock on the way to a PENDING lock. - ** This property is used by the algorithm for rolling back a journal file - ** after a crash. - ** - ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is - ** implemented by obtaining a write-lock on the entire 'shared byte - ** range'. Since all other locks require a read-lock on one of the bytes - ** within this range, this ensures that no other locks are held on the - ** database. - ** - ** The reason a single byte cannot be used instead of the 'shared byte - ** range' is that some versions of unixdows do not support read-locks. By - ** locking a random byte from a range, concurrent SHARED locks may exist - ** even if the locking primitive used is always a write-lock. - */ - int rc = UNQLITE_OK; - unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode = pFile->pInode; - struct flock lock; - int s = 0; - int tErrno = 0; - - /* If there is already a lock of this type or more restrictive on the - ** unixFile, do nothing. Don't use the end_lock: exit path, as - ** unixEnterMutex() hasn't been called yet. - */ - if( pFile->eFileLock>=eFileLock ){ - return UNQLITE_OK; - } - /* This mutex is needed because pFile->pInode is shared across threads - */ - unixEnterMutex(); - pInode = pFile->pInode; - - /* If some thread using this PID has a lock via a different unixFile* - ** handle that precludes the requested lock, return BUSY. - */ - if( (pFile->eFileLock!=pInode->eFileLock && - (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) - ){ - rc = UNQLITE_BUSY; - goto end_lock; - } - - /* If a SHARED lock is requested, and some thread using this PID already - ** has a SHARED or RESERVED lock, then increment reference counts and - ** return UNQLITE_OK. - */ - if( eFileLock==SHARED_LOCK && - (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ - pFile->eFileLock = SHARED_LOCK; - pInode->nShared++; - pInode->nLock++; - goto end_lock; - } - /* A PENDING lock is needed before acquiring a SHARED lock and before - ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will - ** be released. - */ - lock.l_len = 1L; - lock.l_whence = SEEK_SET; - if( eFileLock==SHARED_LOCK - || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLockh, F_SETLK, &lock); - if( s==(-1) ){ - tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_lock; - } - } - /* If control gets to this point, then actually go ahead and make - ** operating system calls for the specified lock. - */ - if( eFileLock==SHARED_LOCK ){ - /* Now get the read-lock */ - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){ - tErrno = errno; - } - /* Drop the temporary PENDING lock */ - lock.l_start = PENDING_BYTE; - lock.l_len = 1L; - lock.l_type = F_UNLCK; - if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){ - if( s != -1 ){ - /* This could happen with a network mount */ - tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_lock; - } - } - if( s==(-1) ){ - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - }else{ - pFile->eFileLock = SHARED_LOCK; - pInode->nLock++; - pInode->nShared = 1; - } - }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ - /* We are trying for an exclusive lock but another thread in this - ** same process is still holding a shared lock. */ - rc = UNQLITE_BUSY; - }else{ - /* The request was for a RESERVED or EXCLUSIVE lock. It is - ** assumed that there is a SHARED or greater lock on the file - ** already. - */ - lock.l_type = F_WRLCK; - switch( eFileLock ){ - case RESERVED_LOCK: - lock.l_start = RESERVED_BYTE; - break; - case EXCLUSIVE_LOCK: - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - break; - default: - /* Can't happen */ - break; - } - s = fcntl(pFile->h, F_SETLK, &lock); - if( s==(-1) ){ - tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - } - } - if( rc==UNQLITE_OK ){ - pFile->eFileLock = eFileLock; - pInode->eFileLock = eFileLock; - }else if( eFileLock==EXCLUSIVE_LOCK ){ - pFile->eFileLock = PENDING_LOCK; - pInode->eFileLock = PENDING_LOCK; - } -end_lock: - unixLeaveMutex(); - return rc; -} -/* -** Add the file descriptor used by file handle pFile to the corresponding -** pUnused list. -*/ -static void setPendingFd(unixFile *pFile){ - unixInodeInfo *pInode = pFile->pInode; - UnixUnusedFd *p = pFile->pUnused; - p->pNext = pInode->pUnused; - pInode->pUnused = p; - pFile->h = -1; - pFile->pUnused = 0; -} -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -** -** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED -** the byte range is divided into 2 parts and the first part is unlocked then -** set to a read lock, then the other part is simply unlocked. This works -** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to -** remove the write lock on a region when a read lock is set. -*/ -static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){ - unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode; - struct flock lock; - int rc = UNQLITE_OK; - int h; - int tErrno; /* Error code from system call errors */ - - if( pFile->eFileLock<=eFileLock ){ - return UNQLITE_OK; - } - unixEnterMutex(); - - h = pFile->h; - pInode = pFile->pInode; - - if( pFile->eFileLock>SHARED_LOCK ){ - /* downgrading to a shared lock on NFS involves clearing the write lock - ** before establishing the readlock - to avoid a race condition we downgrade - ** the lock in 2 blocks, so that part of the range will be covered by a - ** write lock until the rest is covered by a read lock: - ** 1: [WWWWW] - ** 2: [....W] - ** 3: [RRRRW] - ** 4: [RRRR.] - */ - if( eFileLock==SHARED_LOCK ){ - if( handleNFSUnlock ){ - off_t divSize = SHARED_SIZE - 1; - - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ - tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_unlock; - } - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ - tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_unlock; - } - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST+divSize; - lock.l_len = SHARED_SIZE-divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ - tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_unlock; - } - }else{ - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ - tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_unlock; - } - } - } - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = PENDING_BYTE; - lock.l_len = 2L; - if( fcntl(h, F_SETLK, &lock)!=(-1) ){ - pInode->eFileLock = SHARED_LOCK; - }else{ - tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_unlock; - } - } - if( eFileLock==NO_LOCK ){ - /* Decrement the shared lock counter. Release the lock using an - ** OS call only when all threads in this same process have released - ** the lock. - */ - pInode->nShared--; - if( pInode->nShared==0 ){ - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = lock.l_len = 0L; - - if( fcntl(h, F_SETLK, &lock)!=(-1) ){ - pInode->eFileLock = NO_LOCK; - }else{ - tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - pInode->eFileLock = NO_LOCK; - pFile->eFileLock = NO_LOCK; - } - } - - /* Decrement the count of locks against this same file. When the - ** count reaches zero, close any other file descriptors whose close - ** was deferred because of outstanding locks. - */ - pInode->nLock--; - - if( pInode->nLock==0 ){ - int rc2 = closePendingFds(pFile); - if( rc==UNQLITE_OK ){ - rc = rc2; - } - } - } - -end_unlock: - - unixLeaveMutex(); - - if( rc==UNQLITE_OK ) pFile->eFileLock = eFileLock; - return rc; -} -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -*/ -static int unixUnlock(unqlite_file *id, int eFileLock){ - return _posixUnlock(id, eFileLock, 0); -} -/* -** This function performs the parts of the "close file" operation -** common to all locking schemes. It closes the directory and file -** handles, if they are valid, and sets all fields of the unixFile -** structure to 0. -** -*/ -static int closeUnixFile(unqlite_file *id){ - unixFile *pFile = (unixFile*)id; - if( pFile ){ - if( pFile->dirfd>=0 ){ - int err = close(pFile->dirfd); - if( err ){ - pFile->lastErrno = errno; - return UNQLITE_IOERR; - }else{ - pFile->dirfd=-1; - } - } - if( pFile->h>=0 ){ - int err = close(pFile->h); - if( err ){ - pFile->lastErrno = errno; - return UNQLITE_IOERR; - } - } - unqlite_free(pFile->pUnused); - SyZero(pFile,sizeof(unixFile)); - } - return UNQLITE_OK; -} -/* -** Close a file. -*/ -static int unixClose(unqlite_file *id){ - int rc = UNQLITE_OK; - if( id ){ - unixFile *pFile = (unixFile *)id; - unixUnlock(id, NO_LOCK); - unixEnterMutex(); - if( pFile->pInode && pFile->pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->pUnused list. It will be automatically closed - ** when the last lock is cleared. - */ - setPendingFd(pFile); - } - releaseInodeInfo(pFile); - rc = closeUnixFile(id); - unixLeaveMutex(); - } - return rc; -} -/************** End of the posix advisory lock implementation ***************** -******************************************************************************/ -/* -** -** The next division contains implementations for all methods of the -** unqlite_file object other than the locking methods. The locking -** methods were defined in divisions above (one locking method per -** division). Those methods that are common to all locking modes -** are gather together into this division. -*/ -/* -** Seek to the offset passed as the second argument, then read cnt -** bytes into pBuf. Return the number of bytes actually read. -** -** NB: If you define USE_PREAD or USE_PREAD64, then it might also -** be necessary to define _XOPEN_SOURCE to be 500. This varies from -** one system to another. Since SQLite does not define USE_PREAD -** any form by default, we will not attempt to define _XOPEN_SOURCE. -** See tickets #2741 and #2681. -** -** To avoid stomping the errno value on a failed read the lastErrno value -** is set before returning. -*/ -static int seekAndRead(unixFile *id, unqlite_int64 offset, void *pBuf, int cnt){ - int got; -#if (!defined(USE_PREAD) && !defined(USE_PREAD64)) - unqlite_int64 newOffset; -#endif - -#if defined(USE_PREAD) - got = pread(id->h, pBuf, cnt, offset); -#elif defined(USE_PREAD64) - got = pread64(id->h, pBuf, cnt, offset); -#else - newOffset = lseek(id->h, offset, SEEK_SET); - - if( newOffset!=offset ){ - if( newOffset == -1 ){ - ((unixFile*)id)->lastErrno = errno; - }else{ - ((unixFile*)id)->lastErrno = 0; - } - return -1; - } - got = read(id->h, pBuf, cnt); -#endif - if( got<0 ){ - ((unixFile*)id)->lastErrno = errno; - } - return got; -} -/* -** Read data from a file into a buffer. Return UNQLITE_OK if all -** bytes were read successfully and UNQLITE_IOERR if anything goes -** wrong. -*/ -static int unixRead( - unqlite_file *id, - void *pBuf, - unqlite_int64 amt, - unqlite_int64 offset -){ - unixFile *pFile = (unixFile *)id; - int got; - - got = seekAndRead(pFile, offset, pBuf, (int)amt); - if( got==(int)amt ){ - return UNQLITE_OK; - }else if( got<0 ){ - /* lastErrno set by seekAndRead */ - return UNQLITE_IOERR; - }else{ - pFile->lastErrno = 0; /* not a system error */ - /* Unread parts of the buffer must be zero-filled */ - SyZero(&((char*)pBuf)[got],(sxu32)amt-got); - return UNQLITE_IOERR; - } -} -/* -** Seek to the offset in id->offset then read cnt bytes into pBuf. -** Return the number of bytes actually read. Update the offset. -** -** To avoid stomping the errno value on a failed write the lastErrno value -** is set before returning. -*/ -static int seekAndWrite(unixFile *id, unqlite_int64 offset, const void *pBuf, unqlite_int64 cnt){ - int got; -#if (!defined(USE_PREAD) && !defined(USE_PREAD64)) - unqlite_int64 newOffset; -#endif - -#if defined(USE_PREAD) - got = pwrite(id->h, pBuf, cnt, offset); -#elif defined(USE_PREAD64) - got = pwrite64(id->h, pBuf, cnt, offset); -#else - newOffset = lseek(id->h, offset, SEEK_SET); - if( newOffset!=offset ){ - if( newOffset == -1 ){ - ((unixFile*)id)->lastErrno = errno; - }else{ - ((unixFile*)id)->lastErrno = 0; - } - return -1; - } - got = write(id->h, pBuf, cnt); -#endif - if( got<0 ){ - ((unixFile*)id)->lastErrno = errno; - } - return got; -} -/* -** Write data from a buffer into a file. Return UNQLITE_OK on success -** or some other error code on failure. -*/ -static int unixWrite( - unqlite_file *id, - const void *pBuf, - unqlite_int64 amt, - unqlite_int64 offset -){ - unixFile *pFile = (unixFile*)id; - int wrote = 0; - - while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){ - amt -= wrote; - offset += wrote; - pBuf = &((char*)pBuf)[wrote]; - } - - if( amt>0 ){ - if( wrote<0 ){ - /* lastErrno set by seekAndWrite */ - return UNQLITE_IOERR; - }else{ - pFile->lastErrno = 0; /* not a system error */ - return UNQLITE_FULL; - } - } - return UNQLITE_OK; -} -/* -** We do not trust systems to provide a working fdatasync(). Some do. -** Others do no. To be safe, we will stick with the (slower) fsync(). -** If you know that your system does support fdatasync() correctly, -** then simply compile with -Dfdatasync=fdatasync -*/ -#if !defined(fdatasync) && !defined(__linux__) -# define fdatasync fsync -#endif - -/* -** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not -** the F_FULLFSYNC macro is defined. F_FULLFSYNC is currently -** only available on Mac OS X. But that could change. -*/ -#ifdef F_FULLFSYNC -# define HAVE_FULLFSYNC 1 -#else -# define HAVE_FULLFSYNC 0 -#endif -/* -** The fsync() system call does not work as advertised on many -** unix systems. The following procedure is an attempt to make -** it work better. -** -** -** SQLite sets the dataOnly flag if the size of the file is unchanged. -** The idea behind dataOnly is that it should only write the file content -** to disk, not the inode. We only set dataOnly if the file size is -** unchanged since the file size is part of the inode. However, -** Ted Ts'o tells us that fdatasync() will also write the inode if the -** file size has changed. The only real difference between fdatasync() -** and fsync(), Ted tells us, is that fdatasync() will not flush the -** inode if the mtime or owner or other inode attributes have changed. -** We only care about the file size, not the other file attributes, so -** as far as SQLite is concerned, an fdatasync() is always adequate. -** So, we always use fdatasync() if it is available, regardless of -** the value of the dataOnly flag. -*/ -static int full_fsync(int fd, int fullSync, int dataOnly){ - int rc; -#if HAVE_FULLFSYNC - SXUNUSED(dataOnly); -#else - SXUNUSED(fullSync); - SXUNUSED(dataOnly); -#endif - - /* If we compiled with the UNQLITE_NO_SYNC flag, then syncing is a - ** no-op - */ -#if HAVE_FULLFSYNC - if( fullSync ){ - rc = fcntl(fd, F_FULLFSYNC, 0); - }else{ - rc = 1; - } - /* If the FULLFSYNC failed, fall back to attempting an fsync(). - ** It shouldn't be possible for fullfsync to fail on the local - ** file system (on OSX), so failure indicates that FULLFSYNC - ** isn't supported for this file system. So, attempt an fsync - ** and (for now) ignore the overhead of a superfluous fcntl call. - ** It'd be better to detect fullfsync support once and avoid - ** the fcntl call every time sync is called. - */ - if( rc ) rc = fsync(fd); - -#elif defined(__APPLE__) - /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly - ** so currently we default to the macro that redefines fdatasync to fsync - */ - rc = fsync(fd); -#else - rc = fdatasync(fd); -#endif /* ifdef UNQLITE_NO_SYNC elif HAVE_FULLFSYNC */ - if( rc!= -1 ){ - rc = 0; - } - return rc; -} -/* -** Make sure all writes to a particular file are committed to disk. -** -** If dataOnly==0 then both the file itself and its metadata (file -** size, access time, etc) are synced. If dataOnly!=0 then only the -** file data is synced. -** -** Under Unix, also make sure that the directory entry for the file -** has been created by fsync-ing the directory that contains the file. -** If we do not do this and we encounter a power failure, the directory -** entry for the journal might not exist after we reboot. The next -** SQLite to access the file will not know that the journal exists (because -** the directory entry for the journal was never created) and the transaction -** will not roll back - possibly leading to database corruption. -*/ -static int unixSync(unqlite_file *id, int flags){ - int rc; - unixFile *pFile = (unixFile*)id; - - int isDataOnly = (flags&UNQLITE_SYNC_DATAONLY); - int isFullsync = (flags&0x0F)==UNQLITE_SYNC_FULL; - - rc = full_fsync(pFile->h, isFullsync, isDataOnly); - - if( rc ){ - pFile->lastErrno = errno; - return UNQLITE_IOERR; - } - if( pFile->dirfd>=0 ){ - int err; -#ifndef UNQLITE_DISABLE_DIRSYNC - /* The directory sync is only attempted if full_fsync is - ** turned off or unavailable. If a full_fsync occurred above, - ** then the directory sync is superfluous. - */ - if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){ - /* - ** We have received multiple reports of fsync() returning - ** errors when applied to directories on certain file systems. - ** A failed directory sync is not a big deal. So it seems - ** better to ignore the error. Ticket #1657 - */ - /* pFile->lastErrno = errno; */ - /* return UNQLITE_IOERR; */ - } -#endif - err = close(pFile->dirfd); /* Only need to sync once, so close the */ - if( err==0 ){ /* directory when we are done */ - pFile->dirfd = -1; - }else{ - pFile->lastErrno = errno; - rc = UNQLITE_IOERR; - } - } - return rc; -} -/* -** Truncate an open file to a specified size -*/ -static int unixTruncate(unqlite_file *id, sxi64 nByte){ - unixFile *pFile = (unixFile *)id; - int rc; - - rc = ftruncate(pFile->h, (off_t)nByte); - if( rc ){ - pFile->lastErrno = errno; - return UNQLITE_IOERR; - }else{ - return UNQLITE_OK; - } -} -/* -** Determine the current size of a file in bytes -*/ -static int unixFileSize(unqlite_file *id,sxi64 *pSize){ - int rc; - struct stat buf; - - rc = fstat(((unixFile*)id)->h, &buf); - - if( rc!=0 ){ - ((unixFile*)id)->lastErrno = errno; - return UNQLITE_IOERR; - } - *pSize = buf.st_size; - - /* When opening a zero-size database, the findInodeInfo() procedure - ** writes a single byte into that file in order to work around a bug - ** in the OS-X msdos filesystem. In order to avoid problems with upper - ** layers, we need to report this file size as zero even though it is - ** really 1. Ticket #3260. - */ - if( *pSize==1 ) *pSize = 0; - - return UNQLITE_OK; -} -/* -** Return the sector size in bytes of the underlying block device for -** the specified file. This is almost always 512 bytes, but may be -** larger for some devices. -** -** SQLite code assumes this function cannot fail. It also assumes that -** if two files are created in the same file-system directory (i.e. -** a database and its journal file) that the sector size will be the -** same for both. -*/ -static int unixSectorSize(unqlite_file *NotUsed){ - SXUNUSED(NotUsed); - return UNQLITE_DEFAULT_SECTOR_SIZE; -} -/* -** This vector defines all the methods that can operate on an -** unqlite_file for Windows systems. -*/ -static const unqlite_io_methods unixIoMethod = { - 1, /* iVersion */ - unixClose, /* xClose */ - unixRead, /* xRead */ - unixWrite, /* xWrite */ - unixTruncate, /* xTruncate */ - unixSync, /* xSync */ - unixFileSize, /* xFileSize */ - unixLock, /* xLock */ - unixUnlock, /* xUnlock */ - unixCheckReservedLock, /* xCheckReservedLock */ - unixSectorSize, /* xSectorSize */ -}; -/**************************************************************************** -**************************** unqlite_vfs methods **************************** -** -** This division contains the implementation of methods on the -** unqlite_vfs object. -*/ -/* -** Initialize the contents of the unixFile structure pointed to by pId. -*/ -static int fillInUnixFile( - unqlite_vfs *pVfs, /* Pointer to vfs object */ - int h, /* Open file descriptor of file being opened */ - int dirfd, /* Directory file descriptor */ - unqlite_file *pId, /* Write to the unixFile structure here */ - const char *zFilename, /* Name of the file being opened */ - int noLock, /* Omit locking if true */ - int isDelete /* Delete on close if true */ -){ - const unqlite_io_methods *pLockingStyle = &unixIoMethod; - unixFile *pNew = (unixFile *)pId; - int rc = UNQLITE_OK; - - /* Parameter isDelete is only used on vxworks. Express this explicitly - ** here to prevent compiler warnings about unused parameters. - */ - SXUNUSED(isDelete); - SXUNUSED(noLock); - SXUNUSED(pVfs); - - pNew->h = h; - pNew->dirfd = dirfd; - pNew->fileFlags = 0; - pNew->zPath = zFilename; - - unixEnterMutex(); - rc = findInodeInfo(pNew, &pNew->pInode); - if( rc!=UNQLITE_OK ){ - /* If an error occured in findInodeInfo(), close the file descriptor - ** immediately, before releasing the mutex. findInodeInfo() may fail - ** in two scenarios: - ** - ** (a) A call to fstat() failed. - ** (b) A malloc failed. - ** - ** Scenario (b) may only occur if the process is holding no other - ** file descriptors open on the same file. If there were other file - ** descriptors on this file, then no malloc would be required by - ** findInodeInfo(). If this is the case, it is quite safe to close - ** handle h - as it is guaranteed that no posix locks will be released - ** by doing so. - ** - ** If scenario (a) caused the error then things are not so safe. The - ** implicit assumption here is that if fstat() fails, things are in - ** such bad shape that dropping a lock or two doesn't matter much. - */ - close(h); - h = -1; - } - unixLeaveMutex(); - - pNew->lastErrno = 0; - if( rc!=UNQLITE_OK ){ - if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ - if( h>=0 ) close(h); - }else{ - pNew->pMethod = pLockingStyle; - } - return rc; -} -/* -** Open a file descriptor to the directory containing file zFilename. -** If successful, *pFd is set to the opened file descriptor and -** UNQLITE_OK is returned. If an error occurs, either UNQLITE_NOMEM -** or UNQLITE_CANTOPEN is returned and *pFd is set to an undefined -** value. -** -** If UNQLITE_OK is returned, the caller is responsible for closing -** the file descriptor *pFd using close(). -*/ -static int openDirectory(const char *zFilename, int *pFd){ - sxu32 ii; - int fd = -1; - char zDirname[MAX_PATHNAME+1]; - sxu32 n; - n = Systrcpy(zDirname,sizeof(zDirname),zFilename,0); - for(ii=n; ii>1 && zDirname[ii]!='/'; ii--); - if( ii>0 ){ - zDirname[ii] = '\0'; - fd = open(zDirname, O_RDONLY|O_BINARY, 0); - if( fd>=0 ){ -#ifdef FD_CLOEXEC - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); -#endif - } - } - *pFd = fd; - return (fd>=0?UNQLITE_OK: UNQLITE_IOERR ); -} -/* -** Search for an unused file descriptor that was opened on the database -** file (not a journal or master-journal file) identified by pathname -** zPath with UNQLITE_OPEN_XXX flags matching those passed as the second -** argument to this function. -** -** Such a file descriptor may exist if a database connection was closed -** but the associated file descriptor could not be closed because some -** other file descriptor open on the same file is holding a file-lock. -** Refer to comments in the unixClose() function and the lengthy comment -** describing "Posix Advisory Locking" at the start of this file for -** further details. Also, ticket #4018. -** -** If a suitable file descriptor is found, then it is returned. If no -** such file descriptor is located, -1 is returned. -*/ -static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ - UnixUnusedFd *pUnused = 0; - struct stat sStat; /* Results of stat() call */ - /* A stat() call may fail for various reasons. If this happens, it is - ** almost certain that an open() call on the same path will also fail. - ** For this reason, if an error occurs in the stat() call here, it is - ** ignored and -1 is returned. The caller will try to open a new file - ** descriptor on the same path, fail, and return an error to SQLite. - ** - ** Even if a subsequent open() call does succeed, the consequences of - ** not searching for a resusable file descriptor are not dire. */ - if( 0==stat(zPath, &sStat) ){ - unixInodeInfo *pInode; - - unixEnterMutex(); - pInode = inodeList; - while( pInode && (pInode->fileId.dev!=sStat.st_dev - || pInode->fileId.ino!=sStat.st_ino) ){ - pInode = pInode->pNext; - } - if( pInode ){ - UnixUnusedFd **pp; - for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); - pUnused = *pp; - if( pUnused ){ - *pp = pUnused->pNext; - } - } - unixLeaveMutex(); - } - return pUnused; -} -/* -** This function is called by unixOpen() to determine the unix permissions -** to create new files with. If no error occurs, then UNQLITE_OK is returned -** and a value suitable for passing as the third argument to open(2) is -** written to *pMode. If an IO error occurs, an SQLite error code is -** returned and the value of *pMode is not modified. -** -** If the file being opened is a temporary file, it is always created with -** the octal permissions 0600 (read/writable by owner only). If the file -** is a database or master journal file, it is created with the permissions -** mask UNQLITE_DEFAULT_FILE_PERMISSIONS. -** -** Finally, if the file being opened is a WAL or regular journal file, then -** this function queries the file-system for the permissions on the -** corresponding database file and sets *pMode to this value. Whenever -** possible, WAL and journal files are created using the same permissions -** as the associated database file. -*/ -static int findCreateFileMode( - const char *zPath, /* Path of file (possibly) being created */ - int flags, /* Flags passed as 4th argument to xOpen() */ - mode_t *pMode /* OUT: Permissions to open file with */ -){ - int rc = UNQLITE_OK; /* Return Code */ - if( flags & UNQLITE_OPEN_TEMP_DB ){ - *pMode = 0600; - SXUNUSED(zPath); - }else{ - *pMode = UNQLITE_DEFAULT_FILE_PERMISSIONS; - } - return rc; -} -/* -** Open the file zPath. -** -** Previously, the SQLite OS layer used three functions in place of this -** one: -** -** unqliteOsOpenReadWrite(); -** unqliteOsOpenReadOnly(); -** unqliteOsOpenExclusive(); -** -** These calls correspond to the following combinations of flags: -** -** ReadWrite() -> (READWRITE | CREATE) -** ReadOnly() -> (READONLY) -** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE) -** -** The old OpenExclusive() accepted a boolean argument - "delFlag". If -** true, the file was configured to be automatically deleted when the -** file handle closed. To achieve the same effect using this new -** interface, add the DELETEONCLOSE flag to those specified above for -** OpenExclusive(). -*/ -static int unixOpen( - unqlite_vfs *pVfs, /* The VFS for which this is the xOpen method */ - const char *zPath, /* Pathname of file to be opened */ - unqlite_file *pFile, /* The file descriptor to be filled in */ - unsigned int flags /* Input flags to control the opening */ -){ - unixFile *p = (unixFile *)pFile; - int fd = -1; /* File descriptor returned by open() */ - int dirfd = -1; /* Directory file descriptor */ - int openFlags = 0; /* Flags to pass to open() */ - int noLock; /* True to omit locking primitives */ - int rc = UNQLITE_OK; /* Function Return Code */ - UnixUnusedFd *pUnused; - int isExclusive = (flags & UNQLITE_OPEN_EXCLUSIVE); - int isDelete = (flags & UNQLITE_OPEN_TEMP_DB); - int isCreate = (flags & UNQLITE_OPEN_CREATE); - int isReadonly = (flags & UNQLITE_OPEN_READONLY); - int isReadWrite = (flags & UNQLITE_OPEN_READWRITE); - /* If creating a master or main-file journal, this function will open - ** a file-descriptor on the directory too. The first time unixSync() - ** is called the directory file descriptor will be fsync()ed and close()d. - */ - int isOpenDirectory = isCreate ; - const char *zName = zPath; - - SyZero(p,sizeof(unixFile)); - - pUnused = findReusableFd(zName, flags); - if( pUnused ){ - fd = pUnused->fd; - }else{ - pUnused = unqlite_malloc(sizeof(*pUnused)); - if( !pUnused ){ - return UNQLITE_NOMEM; - } - } - p->pUnused = pUnused; - - /* Determine the value of the flags parameter passed to POSIX function - ** open(). These must be calculated even if open() is not called, as - ** they may be stored as part of the file handle and used by the - ** 'conch file' locking functions later on. */ - if( isReadonly ) openFlags |= O_RDONLY; - if( isReadWrite ) openFlags |= O_RDWR; - if( isCreate ) openFlags |= O_CREAT; - if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); - openFlags |= (O_LARGEFILE|O_BINARY); - - if( fd<0 ){ - mode_t openMode; /* Permissions to create file with */ - rc = findCreateFileMode(zName, flags, &openMode); - if( rc!=UNQLITE_OK ){ - return rc; - } - fd = open(zName, openFlags, openMode); - if( fd<0 ){ - rc = UNQLITE_IOERR; - goto open_finished; - } - } - - if( p->pUnused ){ - p->pUnused->fd = fd; - p->pUnused->flags = flags; - } - - if( isDelete ){ - unlink(zName); - } - - if( isOpenDirectory ){ - rc = openDirectory(zPath, &dirfd); - if( rc!=UNQLITE_OK ){ - /* It is safe to close fd at this point, because it is guaranteed not - ** to be open on a database file. If it were open on a database file, - ** it would not be safe to close as this would release any locks held - ** on the file by this process. */ - close(fd); /* silently leak if fail, already in error */ - goto open_finished; - } - } - -#ifdef FD_CLOEXEC - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); -#endif - - noLock = 0; - -#if defined(__APPLE__) - struct statfs fsInfo; - if( fstatfs(fd, &fsInfo) == -1 ){ - ((unixFile*)pFile)->lastErrno = errno; - if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */ - close(fd); /* silently leak if fail, in error */ - return UNQLITE_IOERR; - } - if (0 == SyStrncmp("msdos", fsInfo.f_fstypename, 5)) { - ((unixFile*)pFile)->fsFlags |= UNQLITE_FSFLAGS_IS_MSDOS; - } -#endif - - rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); -open_finished: - if( rc!=UNQLITE_OK ){ - unqlite_free(p->pUnused); - } - return rc; -} -/* -** Delete the file at zPath. If the dirSync argument is true, fsync() -** the directory after deleting the file. -*/ -static int unixDelete( - unqlite_vfs *NotUsed, /* VFS containing this as the xDelete method */ - const char *zPath, /* Name of file to be deleted */ - int dirSync /* If true, fsync() directory after deleting file */ -){ - int rc = UNQLITE_OK; - SXUNUSED(NotUsed); - - if( unlink(zPath)==(-1) && errno!=ENOENT ){ - return UNQLITE_IOERR; - } -#ifndef UNQLITE_DISABLE_DIRSYNC - if( dirSync ){ - int fd; - rc = openDirectory(zPath, &fd); - if( rc==UNQLITE_OK ){ - if( fsync(fd) ) - { - rc = UNQLITE_IOERR; - } - if( close(fd) && !rc ){ - rc = UNQLITE_IOERR; - } - } - } -#endif - return rc; -} -/* -** Sleep for a little while. Return the amount of time slept. -** The argument is the number of microseconds we want to sleep. -** The return value is the number of microseconds of sleep actually -** requested from the underlying operating system, a number which -** might be greater than or equal to the argument, but not less -** than the argument. -*/ -static int unixSleep(unqlite_vfs *NotUsed, int microseconds) -{ -#if defined(HAVE_USLEEP) && HAVE_USLEEP - usleep(microseconds); - SXUNUSED(NotUsed); - return microseconds; -#else - int seconds = (microseconds+999999)/1000000; - SXUNUSED(NotUsed); - sleep(seconds); - return seconds*1000000; -#endif -} -/* - * Export the current system time. - */ -static int unixCurrentTime(unqlite_vfs *pVfs,Sytm *pOut) -{ - struct tm *pTm; - time_t tt; - SXUNUSED(pVfs); - time(&tt); - pTm = gmtime(&tt); - if( pTm ){ /* Yes, it can fail */ - STRUCT_TM_TO_SYTM(pTm,pOut); - } - return UNQLITE_OK; -} -/* -** Test the existance of or access permissions of file zPath. The -** test performed depends on the value of flags: -** -** UNQLITE_ACCESS_EXISTS: Return 1 if the file exists -** UNQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable. -** UNQLITE_ACCESS_READONLY: Return 1 if the file is readable. -** -** Otherwise return 0. -*/ -static int unixAccess( - unqlite_vfs *NotUsed, /* The VFS containing this xAccess method */ - const char *zPath, /* Path of the file to examine */ - int flags, /* What do we want to learn about the zPath file? */ - int *pResOut /* Write result boolean here */ -){ - int amode = 0; - SXUNUSED(NotUsed); - switch( flags ){ - case UNQLITE_ACCESS_EXISTS: - amode = F_OK; - break; - case UNQLITE_ACCESS_READWRITE: - amode = W_OK|R_OK; - break; - case UNQLITE_ACCESS_READ: - amode = R_OK; - break; - default: - /* Can't happen */ - break; - } - *pResOut = (access(zPath, amode)==0); - if( flags==UNQLITE_ACCESS_EXISTS && *pResOut ){ - struct stat buf; - if( 0==stat(zPath, &buf) && buf.st_size==0 ){ - *pResOut = 0; - } - } - return UNQLITE_OK; -} -/* -** Turn a relative pathname into a full pathname. The relative path -** is stored as a nul-terminated string in the buffer pointed to by -** zPath. -** -** zOut points to a buffer of at least unqlite_vfs.mxPathname bytes -** (in this case, MAX_PATHNAME bytes). The full-path is written to -** this buffer before returning. -*/ -static int unixFullPathname( - unqlite_vfs *pVfs, /* Pointer to vfs object */ - const char *zPath, /* Possibly relative input path */ - int nOut, /* Size of output buffer in bytes */ - char *zOut /* Output buffer */ -){ - if( zPath[0]=='/' ){ - Systrcpy(zOut,(sxu32)nOut,zPath,0); - SXUNUSED(pVfs); - }else{ - sxu32 nCwd; - zOut[nOut-1] = '\0'; - if( getcwd(zOut, nOut-1)==0 ){ - return UNQLITE_IOERR; - } - nCwd = SyStrlen(zOut); - SyBufferFormat(&zOut[nCwd],(sxu32)nOut-nCwd,"/%s",zPath); - } - return UNQLITE_OK; -} -/* - * Export the Unix Vfs. - */ -UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) -{ - static const unqlite_vfs sUnixvfs = { - "Unix", /* Vfs name */ - 1, /* Vfs structure version */ - sizeof(unixFile), /* szOsFile */ - MAX_PATHNAME, /* mxPathName */ - unixOpen, /* xOpen */ - unixDelete, /* xDelete */ - unixAccess, /* xAccess */ - unixFullPathname, /* xFullPathname */ - 0, /* xTmp */ - unixSleep, /* xSleep */ - unixCurrentTime, /* xCurrentTime */ - 0, /* xGetLastError */ - }; - return &sUnixvfs; -} - -#endif /* __UNIXES__ */ - -/* - * ---------------------------------------------------------- - * File: os_win.c - * MD5: ab70fb386c21b39a08b0eb776a8391ab - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: os_win.c v1.2 Win7 2012-11-10 12:10 devel $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif -/* Omit the whole layer from the build if compiling for platforms other than Windows */ -#ifdef __WINNT__ -/* This file contains code that is specific to windows. (Mostly SQLite3 source tree) */ -#include -/* -** Some microsoft compilers lack this definition. -*/ -#ifndef INVALID_FILE_ATTRIBUTES -# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) -#endif -/* -** WinCE lacks native support for file locking so we have to fake it -** with some code of our own. -*/ -#ifdef __WIN_CE__ -typedef struct winceLock { - int nReaders; /* Number of reader locks obtained */ - BOOL bPending; /* Indicates a pending lock has been obtained */ - BOOL bReserved; /* Indicates a reserved lock has been obtained */ - BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ -} winceLock; -#define AreFileApisANSI() 1 -#define FormatMessageW(a,b,c,d,e,f,g) 0 -#endif - -/* -** The winFile structure is a subclass of unqlite_file* specific to the win32 -** portability layer. -*/ -typedef struct winFile winFile; -struct winFile { - const unqlite_io_methods *pMethod; /*** Must be first ***/ - unqlite_vfs *pVfs; /* The VFS used to open this file */ - HANDLE h; /* Handle for accessing the file */ - sxu8 locktype; /* Type of lock currently held on this file */ - short sharedLockByte; /* Randomly chosen byte used as a shared lock */ - DWORD lastErrno; /* The Windows errno from the last I/O error */ - DWORD sectorSize; /* Sector size of the device file is on */ - int szChunk; /* Chunk size */ -#ifdef __WIN_CE__ - WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ - HANDLE hMutex; /* Mutex used to control access to shared lock */ - HANDLE hShared; /* Shared memory segment used for locking */ - winceLock local; /* Locks obtained by this instance of winFile */ - winceLock *shared; /* Global shared lock memory for the file */ -#endif -}; -/* -** Convert a UTF-8 string to microsoft unicode (UTF-16?). -** -** Space to hold the returned string is obtained from HeapAlloc(). -*/ -static WCHAR *utf8ToUnicode(const char *zFilename){ - int nChar; - WCHAR *zWideFilename; - - nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0); - zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nChar*sizeof(zWideFilename[0]) ); - if( zWideFilename==0 ){ - return 0; - } - nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar); - if( nChar==0 ){ - HeapFree(GetProcessHeap(),0,zWideFilename); - zWideFilename = 0; - } - return zWideFilename; -} - -/* -** Convert microsoft unicode to UTF-8. Space to hold the returned string is -** obtained from malloc(). -*/ -static char *unicodeToUtf8(const WCHAR *zWideFilename){ - int nByte; - char *zFilename; - - nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); - zFilename = (char *)HeapAlloc(GetProcessHeap(),0,nByte ); - if( zFilename==0 ){ - return 0; - } - nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, - 0, 0); - if( nByte == 0 ){ - HeapFree(GetProcessHeap(),0,zFilename); - zFilename = 0; - } - return zFilename; -} - -/* -** Convert an ansi string to microsoft unicode, based on the -** current codepage settings for file apis. -** -** Space to hold the returned string is obtained -** from malloc. -*/ -static WCHAR *mbcsToUnicode(const char *zFilename){ - int nByte; - WCHAR *zMbcsFilename; - int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; - - nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, 0,0)*sizeof(WCHAR); - zMbcsFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zMbcsFilename[0]) ); - if( zMbcsFilename==0 ){ - return 0; - } - nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte); - if( nByte==0 ){ - HeapFree(GetProcessHeap(),0,zMbcsFilename); - zMbcsFilename = 0; - } - return zMbcsFilename; -} -/* -** Convert multibyte character string to UTF-8. Space to hold the -** returned string is obtained from malloc(). -*/ -char *unqlite_win32_mbcs_to_utf8(const char *zFilename){ - char *zFilenameUtf8; - WCHAR *zTmpWide; - - zTmpWide = mbcsToUnicode(zFilename); - if( zTmpWide==0 ){ - return 0; - } - zFilenameUtf8 = unicodeToUtf8(zTmpWide); - HeapFree(GetProcessHeap(),0,zTmpWide); - return zFilenameUtf8; -} -/* -** Some microsoft compilers lack this definition. -*/ -#ifndef INVALID_SET_FILE_POINTER -# define INVALID_SET_FILE_POINTER ((DWORD)-1) -#endif - -/* -** Move the current position of the file handle passed as the first -** argument to offset iOffset within the file. If successful, return 0. -** Otherwise, set pFile->lastErrno and return non-zero. -*/ -static int seekWinFile(winFile *pFile, unqlite_int64 iOffset){ - LONG upperBits; /* Most sig. 32 bits of new offset */ - LONG lowerBits; /* Least sig. 32 bits of new offset */ - DWORD dwRet; /* Value returned by SetFilePointer() */ - - upperBits = (LONG)((iOffset>>32) & 0x7fffffff); - lowerBits = (LONG)(iOffset & 0xffffffff); - - /* API oddity: If successful, SetFilePointer() returns a dword - ** containing the lower 32-bits of the new file-offset. Or, if it fails, - ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, - ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine - ** whether an error has actually occured, it is also necessary to call - ** GetLastError(). - */ - dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); - if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){ - pFile->lastErrno = GetLastError(); - return 1; - } - return 0; -} -/* -** Close a file. -** -** It is reported that an attempt to close a handle might sometimes -** fail. This is a very unreasonable result, but windows is notorious -** for being unreasonable so I do not doubt that it might happen. If -** the close fails, we pause for 100 milliseconds and try again. As -** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before -** giving up and returning an error. -*/ -#define MX_CLOSE_ATTEMPT 3 -static int winClose(unqlite_file *id) -{ - int rc, cnt = 0; - winFile *pFile = (winFile*)id; - do{ - rc = CloseHandle(pFile->h); - }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) ); - - return rc ? UNQLITE_OK : UNQLITE_IOERR; -} -/* -** Read data from a file into a buffer. Return UNQLITE_OK if all -** bytes were read successfully and UNQLITE_IOERR if anything goes -** wrong. -*/ -static int winRead( - unqlite_file *id, /* File to read from */ - void *pBuf, /* Write content into this buffer */ - unqlite_int64 amt, /* Number of bytes to read */ - unqlite_int64 offset /* Begin reading at this offset */ -){ - winFile *pFile = (winFile*)id; /* file handle */ - DWORD nRead; /* Number of bytes actually read from file */ - - if( seekWinFile(pFile, offset) ){ - return UNQLITE_FULL; - } - if( !ReadFile(pFile->h, pBuf, (DWORD)amt, &nRead, 0) ){ - pFile->lastErrno = GetLastError(); - return UNQLITE_IOERR; - } - if( nRead<(DWORD)amt ){ - /* Unread parts of the buffer must be zero-filled */ - SyZero(&((char*)pBuf)[nRead],(sxu32)(amt-nRead)); - return UNQLITE_IOERR; - } - - return UNQLITE_OK; -} - -/* -** Write data from a buffer into a file. Return UNQLITE_OK on success -** or some other error code on failure. -*/ -static int winWrite( - unqlite_file *id, /* File to write into */ - const void *pBuf, /* The bytes to be written */ - unqlite_int64 amt, /* Number of bytes to write */ - unqlite_int64 offset /* Offset into the file to begin writing at */ -){ - int rc; /* True if error has occured, else false */ - winFile *pFile = (winFile*)id; /* File handle */ - - rc = seekWinFile(pFile, offset); - if( rc==0 ){ - sxu8 *aRem = (sxu8 *)pBuf; /* Data yet to be written */ - unqlite_int64 nRem = amt; /* Number of bytes yet to be written */ - DWORD nWrite; /* Bytes written by each WriteFile() call */ - - while( nRem>0 && WriteFile(pFile->h, aRem, (DWORD)nRem, &nWrite, 0) && nWrite>0 ){ - aRem += nWrite; - nRem -= nWrite; - } - if( nRem>0 ){ - pFile->lastErrno = GetLastError(); - rc = 1; - } - } - if( rc ){ - if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){ - return UNQLITE_FULL; - } - return UNQLITE_IOERR; - } - return UNQLITE_OK; -} - -/* -** Truncate an open file to a specified size -*/ -static int winTruncate(unqlite_file *id, unqlite_int64 nByte){ - winFile *pFile = (winFile*)id; /* File handle object */ - int rc = UNQLITE_OK; /* Return code for this function */ - - - /* If the user has configured a chunk-size for this file, truncate the - ** file so that it consists of an integer number of chunks (i.e. the - ** actual file size after the operation may be larger than the requested - ** size). - */ - if( pFile->szChunk ){ - nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; - } - - /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ - if( seekWinFile(pFile, nByte) ){ - rc = UNQLITE_IOERR; - }else if( 0==SetEndOfFile(pFile->h) ){ - pFile->lastErrno = GetLastError(); - rc = UNQLITE_IOERR; - } - return rc; -} -/* -** Make sure all writes to a particular file are committed to disk. -*/ -static int winSync(unqlite_file *id, int flags){ - winFile *pFile = (winFile*)id; - SXUNUSED(flags); /* MSVC warning */ - if( FlushFileBuffers(pFile->h) ){ - return UNQLITE_OK; - }else{ - pFile->lastErrno = GetLastError(); - return UNQLITE_IOERR; - } -} -/* -** Determine the current size of a file in bytes -*/ -static int winFileSize(unqlite_file *id, unqlite_int64 *pSize){ - DWORD upperBits; - DWORD lowerBits; - winFile *pFile = (winFile*)id; - DWORD error; - lowerBits = GetFileSize(pFile->h, &upperBits); - if( (lowerBits == INVALID_FILE_SIZE) - && ((error = GetLastError()) != NO_ERROR) ) - { - pFile->lastErrno = error; - return UNQLITE_IOERR; - } - *pSize = (((unqlite_int64)upperBits)<<32) + lowerBits; - return UNQLITE_OK; -} -/* -** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. -*/ -#ifndef LOCKFILE_FAIL_IMMEDIATELY -# define LOCKFILE_FAIL_IMMEDIATELY 1 -#endif - -/* -** Acquire a reader lock. -*/ -static int getReadLock(winFile *pFile){ - int res; - OVERLAPPED ovlp; - ovlp.Offset = SHARED_FIRST; - ovlp.OffsetHigh = 0; - ovlp.hEvent = 0; - res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,0, SHARED_SIZE, 0, &ovlp); - if( res == 0 ){ - pFile->lastErrno = GetLastError(); - } - return res; -} -/* -** Undo a readlock -*/ -static int unlockReadLock(winFile *pFile){ - int res; - res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - if( res == 0 ){ - pFile->lastErrno = GetLastError(); - } - return res; -} -/* -** Lock the file with the lock specified by parameter locktype - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. The winUnlock() routine -** erases all locks at once and returns us immediately to locking level 0. -** It is not possible to lower the locking level one step at a time. You -** must go straight to locking level 0. -*/ -static int winLock(unqlite_file *id, int locktype){ - int rc = UNQLITE_OK; /* Return code from subroutines */ - int res = 1; /* Result of a windows lock call */ - int newLocktype; /* Set pFile->locktype to this value before exiting */ - int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ - winFile *pFile = (winFile*)id; - DWORD error = NO_ERROR; - - /* If there is already a lock of this type or more restrictive on the - ** OsFile, do nothing. - */ - if( pFile->locktype>=locktype ){ - return UNQLITE_OK; - } - - /* Make sure the locking sequence is correct - assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); - assert( locktype!=PENDING_LOCK ); - assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); - */ - /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or - ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of - ** the PENDING_LOCK byte is temporary. - */ - newLocktype = pFile->locktype; - if( (pFile->locktype==NO_LOCK) - || ( (locktype==EXCLUSIVE_LOCK) - && (pFile->locktype==RESERVED_LOCK)) - ){ - int cnt = 3; - while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ - /* Try 3 times to get the pending lock. The pending lock might be - ** held by another reader process who will release it momentarily. - */ - Sleep(1); - } - gotPendingLock = res; - if( !res ){ - error = GetLastError(); - } - } - - /* Acquire a shared lock - */ - if( locktype==SHARED_LOCK && res ){ - /* assert( pFile->locktype==NO_LOCK ); */ - res = getReadLock(pFile); - if( res ){ - newLocktype = SHARED_LOCK; - }else{ - error = GetLastError(); - } - } - - /* Acquire a RESERVED lock - */ - if( locktype==RESERVED_LOCK && res ){ - /* assert( pFile->locktype==SHARED_LOCK ); */ - res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); - if( res ){ - newLocktype = RESERVED_LOCK; - }else{ - error = GetLastError(); - } - } - - /* Acquire a PENDING lock - */ - if( locktype==EXCLUSIVE_LOCK && res ){ - newLocktype = PENDING_LOCK; - gotPendingLock = 0; - } - - /* Acquire an EXCLUSIVE lock - */ - if( locktype==EXCLUSIVE_LOCK && res ){ - /* assert( pFile->locktype>=SHARED_LOCK ); */ - res = unlockReadLock(pFile); - res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - if( res ){ - newLocktype = EXCLUSIVE_LOCK; - }else{ - error = GetLastError(); - getReadLock(pFile); - } - } - - /* If we are holding a PENDING lock that ought to be released, then - ** release it now. - */ - if( gotPendingLock && locktype==SHARED_LOCK ){ - UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); - } - - /* Update the state of the lock has held in the file descriptor then - ** return the appropriate result code. - */ - if( res ){ - rc = UNQLITE_OK; - }else{ - pFile->lastErrno = error; - rc = UNQLITE_BUSY; - } - pFile->locktype = (sxu8)newLocktype; - return rc; -} -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, return -** non-zero, otherwise zero. -*/ -static int winCheckReservedLock(unqlite_file *id, int *pResOut){ - int rc; - winFile *pFile = (winFile*)id; - if( pFile->locktype>=RESERVED_LOCK ){ - rc = 1; - }else{ - rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); - if( rc ){ - UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); - } - rc = !rc; - } - *pResOut = rc; - return UNQLITE_OK; -} -/* -** Lower the locking level on file descriptor id to locktype. locktype -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -** -** It is not possible for this routine to fail if the second argument -** is NO_LOCK. If the second argument is SHARED_LOCK then this routine -** might return UNQLITE_IOERR; -*/ -static int winUnlock(unqlite_file *id, int locktype){ - int type; - winFile *pFile = (winFile*)id; - int rc = UNQLITE_OK; - - type = pFile->locktype; - if( type>=EXCLUSIVE_LOCK ){ - UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ - /* This should never happen. We should always be able to - ** reacquire the read lock */ - rc = UNQLITE_IOERR; - } - } - if( type>=RESERVED_LOCK ){ - UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); - } - if( locktype==NO_LOCK && type>=SHARED_LOCK ){ - unlockReadLock(pFile); - } - if( type>=PENDING_LOCK ){ - UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); - } - pFile->locktype = (sxu8)locktype; - return rc; -} -/* -** Return the sector size in bytes of the underlying block device for -** the specified file. This is almost always 512 bytes, but may be -** larger for some devices. -** -*/ -static int winSectorSize(unqlite_file *id){ - return (int)(((winFile*)id)->sectorSize); -} -/* -** This vector defines all the methods that can operate on an -** unqlite_file for Windows systems. -*/ -static const unqlite_io_methods winIoMethod = { - 1, /* iVersion */ - winClose, /* xClose */ - winRead, /* xRead */ - winWrite, /* xWrite */ - winTruncate, /* xTruncate */ - winSync, /* xSync */ - winFileSize, /* xFileSize */ - winLock, /* xLock */ - winUnlock, /* xUnlock */ - winCheckReservedLock, /* xCheckReservedLock */ - winSectorSize, /* xSectorSize */ -}; -/* - * Windows VFS Methods. - */ -/* -** Convert a UTF-8 filename into whatever form the underlying -** operating system wants filenames in. Space to hold the result -** is obtained from malloc and must be freed by the calling -** function. -*/ -static void *convertUtf8Filename(const char *zFilename) -{ - void *zConverted; - zConverted = utf8ToUnicode(zFilename); - /* caller will handle out of memory */ - return zConverted; -} -/* -** Delete the named file. -** -** Note that windows does not allow a file to be deleted if some other -** process has it open. Sometimes a virus scanner or indexing program -** will open a journal file shortly after it is created in order to do -** whatever it does. While this other process is holding the -** file open, we will be unable to delete it. To work around this -** problem, we delay 100 milliseconds and try to delete again. Up -** to MX_DELETION_ATTEMPTs deletion attempts are run before giving -** up and returning an error. -*/ -#define MX_DELETION_ATTEMPTS 5 -static int winDelete( - unqlite_vfs *pVfs, /* Not used on win32 */ - const char *zFilename, /* Name of file to delete */ - int syncDir /* Not used on win32 */ -){ - int cnt = 0; - DWORD rc; - DWORD error = 0; - void *zConverted; - zConverted = convertUtf8Filename(zFilename); - if( zConverted==0 ){ - SXUNUSED(pVfs); - SXUNUSED(syncDir); - return UNQLITE_NOMEM; - } - do{ - DeleteFileW((LPCWSTR)zConverted); - }while( ( ((rc = GetFileAttributesW((LPCWSTR)zConverted)) != INVALID_FILE_ATTRIBUTES) - || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) - && (++cnt < MX_DELETION_ATTEMPTS) - && (Sleep(100), 1) - ); - HeapFree(GetProcessHeap(),0,zConverted); - - return ( (rc == INVALID_FILE_ATTRIBUTES) - && (error == ERROR_FILE_NOT_FOUND)) ? UNQLITE_OK : UNQLITE_IOERR; -} -/* -** Check the existance and status of a file. -*/ -static int winAccess( - unqlite_vfs *pVfs, /* Not used */ - const char *zFilename, /* Name of file to check */ - int flags, /* Type of test to make on this file */ - int *pResOut /* OUT: Result */ -){ - WIN32_FILE_ATTRIBUTE_DATA sAttrData; - DWORD attr; - int rc = 0; - void *zConverted; - SXUNUSED(pVfs); - - zConverted = convertUtf8Filename(zFilename); - if( zConverted==0 ){ - return UNQLITE_NOMEM; - } - SyZero(&sAttrData,sizeof(sAttrData)); - if( GetFileAttributesExW((WCHAR*)zConverted, - GetFileExInfoStandard, - &sAttrData) ){ - /* For an UNQLITE_ACCESS_EXISTS query, treat a zero-length file - ** as if it does not exist. - */ - if( flags==UNQLITE_ACCESS_EXISTS - && sAttrData.nFileSizeHigh==0 - && sAttrData.nFileSizeLow==0 ){ - attr = INVALID_FILE_ATTRIBUTES; - }else{ - attr = sAttrData.dwFileAttributes; - } - }else{ - if( GetLastError()!=ERROR_FILE_NOT_FOUND ){ - HeapFree(GetProcessHeap(),0,zConverted); - return UNQLITE_IOERR; - }else{ - attr = INVALID_FILE_ATTRIBUTES; - } - } - HeapFree(GetProcessHeap(),0,zConverted); - switch( flags ){ - case UNQLITE_ACCESS_READWRITE: - rc = (attr & FILE_ATTRIBUTE_READONLY)==0; - break; - case UNQLITE_ACCESS_READ: - case UNQLITE_ACCESS_EXISTS: - default: - rc = attr!=INVALID_FILE_ATTRIBUTES; - break; - } - *pResOut = rc; - return UNQLITE_OK; -} -/* -** Turn a relative pathname into a full pathname. Write the full -** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname -** bytes in size. -*/ -static int winFullPathname( - unqlite_vfs *pVfs, /* Pointer to vfs object */ - const char *zRelative, /* Possibly relative input path */ - int nFull, /* Size of output buffer in bytes */ - char *zFull /* Output buffer */ -){ - int nByte; - void *zConverted; - WCHAR *zTemp; - char *zOut; - SXUNUSED(nFull); - zConverted = convertUtf8Filename(zRelative); - if( zConverted == 0 ){ - return UNQLITE_NOMEM; - } - nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3; - zTemp = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zTemp[0]) ); - if( zTemp==0 ){ - HeapFree(GetProcessHeap(),0,zConverted); - return UNQLITE_NOMEM; - } - GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0); - HeapFree(GetProcessHeap(),0,zConverted); - zOut = unicodeToUtf8(zTemp); - HeapFree(GetProcessHeap(),0,zTemp); - if( zOut == 0 ){ - return UNQLITE_NOMEM; - } - Systrcpy(zFull,(sxu32)pVfs->mxPathname,zOut,0); - HeapFree(GetProcessHeap(),0,zOut); - return UNQLITE_OK; -} -/* -** Get the sector size of the device used to store -** file. -*/ -static int getSectorSize( - unqlite_vfs *pVfs, - const char *zRelative /* UTF-8 file name */ -){ - DWORD bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE; - char zFullpath[MAX_PATH+1]; - int rc; - DWORD dwRet = 0; - DWORD dwDummy; - /* - ** We need to get the full path name of the file - ** to get the drive letter to look up the sector - ** size. - */ - rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath); - if( rc == UNQLITE_OK ) - { - void *zConverted = convertUtf8Filename(zFullpath); - if( zConverted ){ - /* trim path to just drive reference */ - WCHAR *p = (WCHAR *)zConverted; - for(;*p;p++){ - if( *p == '\\' ){ - *p = '\0'; - break; - } - } - dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted, - &dwDummy, - &bytesPerSector, - &dwDummy, - &dwDummy); - HeapFree(GetProcessHeap(),0,zConverted); - } - if( !dwRet ){ - bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE; - } - } - return (int) bytesPerSector; -} -/* -** Sleep for a little while. Return the amount of time slept. -*/ -static int winSleep(unqlite_vfs *pVfs, int microsec){ - Sleep((microsec+999)/1000); - SXUNUSED(pVfs); - return ((microsec+999)/1000)*1000; -} -/* - * Export the current system time. - */ -static int winCurrentTime(unqlite_vfs *pVfs,Sytm *pOut) -{ - SYSTEMTIME sSys; - SXUNUSED(pVfs); - GetSystemTime(&sSys); - SYSTEMTIME_TO_SYTM(&sSys,pOut); - return UNQLITE_OK; -} -/* -** The idea is that this function works like a combination of -** GetLastError() and FormatMessage() on windows (or errno and -** strerror_r() on unix). After an error is returned by an OS -** function, UnQLite calls this function with zBuf pointing to -** a buffer of nBuf bytes. The OS layer should populate the -** buffer with a nul-terminated UTF-8 encoded error message -** describing the last IO error to have occurred within the calling -** thread. -** -** If the error message is too large for the supplied buffer, -** it should be truncated. The return value of xGetLastError -** is zero if the error message fits in the buffer, or non-zero -** otherwise (if the message was truncated). If non-zero is returned, -** then it is not necessary to include the nul-terminator character -** in the output buffer. -*/ -static int winGetLastError(unqlite_vfs *pVfs, int nBuf, char *zBuf) -{ - /* FormatMessage returns 0 on failure. Otherwise it - ** returns the number of TCHARs written to the output - ** buffer, excluding the terminating null char. - */ - DWORD error = GetLastError(); - WCHAR *zTempWide = 0; - DWORD dwLen; - char *zOut = 0; - - SXUNUSED(pVfs); - dwLen = FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - 0, - error, - 0, - (LPWSTR) &zTempWide, - 0, - 0 - ); - if( dwLen > 0 ){ - /* allocate a buffer and convert to UTF8 */ - zOut = unicodeToUtf8(zTempWide); - /* free the system buffer allocated by FormatMessage */ - LocalFree(zTempWide); - } - if( 0 == dwLen ){ - Systrcpy(zBuf,(sxu32)nBuf,"OS Error",sizeof("OS Error")-1); - }else{ - /* copy a maximum of nBuf chars to output buffer */ - Systrcpy(zBuf,(sxu32)nBuf,zOut,0 /* Compute input length automatically */); - /* free the UTF8 buffer */ - HeapFree(GetProcessHeap(),0,zOut); - } - return 0; -} -/* -** Open a file. -*/ -static int winOpen( - unqlite_vfs *pVfs, /* Not used */ - const char *zName, /* Name of the file (UTF-8) */ - unqlite_file *id, /* Write the UnQLite file handle here */ - unsigned int flags /* Open mode flags */ -){ - HANDLE h; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - DWORD dwFlagsAndAttributes = 0; - winFile *pFile = (winFile*)id; - void *zConverted; /* Filename in OS encoding */ - const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ - int isExclusive = (flags & UNQLITE_OPEN_EXCLUSIVE); - int isDelete = (flags & UNQLITE_OPEN_TEMP_DB); - int isCreate = (flags & UNQLITE_OPEN_CREATE); - int isReadWrite = (flags & UNQLITE_OPEN_READWRITE); - - pFile->h = INVALID_HANDLE_VALUE; - /* Convert the filename to the system encoding. */ - zConverted = convertUtf8Filename(zUtf8Name); - if( zConverted==0 ){ - return UNQLITE_NOMEM; - } - if( isReadWrite ){ - dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; - }else{ - dwDesiredAccess = GENERIC_READ; - } - /* UNQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is - ** created. - */ - if( isExclusive ){ - /* Creates a new file, only if it does not already exist. */ - /* If the file exists, it fails. */ - dwCreationDisposition = CREATE_NEW; - }else if( isCreate ){ - /* Open existing file, or create if it doesn't exist */ - dwCreationDisposition = OPEN_ALWAYS; - }else{ - /* Opens a file, only if it exists. */ - dwCreationDisposition = OPEN_EXISTING; - } - - dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; - - if( isDelete ){ - dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY - | FILE_ATTRIBUTE_HIDDEN - | FILE_FLAG_DELETE_ON_CLOSE; - }else{ - dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; - } - h = CreateFileW((WCHAR*)zConverted, - dwDesiredAccess, - dwShareMode, - NULL, - dwCreationDisposition, - dwFlagsAndAttributes, - NULL - ); - if( h==INVALID_HANDLE_VALUE ){ - pFile->lastErrno = GetLastError(); - HeapFree(GetProcessHeap(),0,zConverted); - return UNQLITE_IOERR; - } - SyZero(pFile,sizeof(*pFile)); - pFile->pMethod = &winIoMethod; - pFile->h = h; - pFile->lastErrno = NO_ERROR; - pFile->pVfs = pVfs; - pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); - HeapFree(GetProcessHeap(),0,zConverted); - return UNQLITE_OK; -} -/* - * Export the Windows Vfs. - */ -UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) -{ - static const unqlite_vfs sWinvfs = { - "Windows", /* Vfs name */ - 1, /* Vfs structure version */ - sizeof(winFile), /* szOsFile */ - MAX_PATH, /* mxPathName */ - winOpen, /* xOpen */ - winDelete, /* xDelete */ - winAccess, /* xAccess */ - winFullPathname, /* xFullPathname */ - 0, /* xTmp */ - winSleep, /* xSleep */ - winCurrentTime, /* xCurrentTime */ - winGetLastError, /* xGetLastError */ - }; - return &sWinvfs; -} -#endif /* __WINNT__ */ -/* - * ---------------------------------------------------------- - * File: pager.c - * MD5: 57ff77347402fbf6892af589ff8a5df7 - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: pager.c v1.1 Win7 2012-11-29 03:46 stable $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif -/* -** This file implements the pager and the transaction manager for UnQLite (Mostly inspired from the SQLite3 Source tree). -** -** The Pager.eState variable stores the current 'state' of a pager. A -** pager may be in any one of the seven states shown in the following -** state diagram. -** -** OPEN <------+------+ -** | | | -** V | | -** +---------> READER-------+ | -** | | | -** | V | -** |<-------WRITER_LOCKED--------->| -** | | | -** | V | -** |<------WRITER_CACHEMOD-------->| -** | | | -** | V | -** |<-------WRITER_DBMOD---------->| -** | | | -** | V | -** +<------WRITER_FINISHED-------->+ -** -** OPEN: -** -** The pager starts up in this state. Nothing is guaranteed in this -** state - the file may or may not be locked and the database size is -** unknown. The database may not be read or written. -** -** * No read or write transaction is active. -** * Any lock, or no lock at all, may be held on the database file. -** * The dbSize and dbOrigSize variables may not be trusted. -** -** READER: -** -** In this state all the requirements for reading the database in -** rollback mode are met. Unless the pager is (or recently -** was) in exclusive-locking mode, a user-level read transaction is -** open. The database size is known in this state. -** -** * A read transaction may be active (but a write-transaction cannot). -** * A SHARED or greater lock is held on the database file. -** * The dbSize variable may be trusted (even if a user-level read -** transaction is not active). The dbOrigSize variables -** may not be trusted at this point. -** * Even if a read-transaction is not open, it is guaranteed that -** there is no hot-journal in the file-system. -** -** WRITER_LOCKED: -** -** The pager moves to this state from READER when a write-transaction -** is first opened on the database. In WRITER_LOCKED state, all locks -** required to start a write-transaction are held, but no actual -** modifications to the cache or database have taken place. -** -** In rollback mode, a RESERVED or (if the transaction was opened with -** EXCLUSIVE flag) EXCLUSIVE lock is obtained on the database file when -** moving to this state, but the journal file is not written to or opened -** to in this state. If the transaction is committed or rolled back while -** in WRITER_LOCKED state, all that is required is to unlock the database -** file. -** -** * A write transaction is active. -** * If the connection is open in rollback-mode, a RESERVED or greater -** lock is held on the database file. -** * The dbSize and dbOrigSize variables are all valid. -** * The contents of the pager cache have not been modified. -** * The journal file may or may not be open. -** * Nothing (not even the first header) has been written to the journal. -** -** WRITER_CACHEMOD: -** -** A pager moves from WRITER_LOCKED state to this state when a page is -** first modified by the upper layer. In rollback mode the journal file -** is opened (if it is not already open) and a header written to the -** start of it. The database file on disk has not been modified. -** -** * A write transaction is active. -** * A RESERVED or greater lock is held on the database file. -** * The journal file is open and the first header has been written -** to it, but the header has not been synced to disk. -** * The contents of the page cache have been modified. -** -** WRITER_DBMOD: -** -** The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state -** when it modifies the contents of the database file. -** -** * A write transaction is active. -** * An EXCLUSIVE or greater lock is held on the database file. -** * The journal file is open and the first header has been written -** and synced to disk. -** * The contents of the page cache have been modified (and possibly -** written to disk). -** -** WRITER_FINISHED: -** -** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD -** state after the entire transaction has been successfully written into the -** database file. In this state the transaction may be committed simply -** by finalizing the journal file. Once in WRITER_FINISHED state, it is -** not possible to modify the database further. At this point, the upper -** layer must either commit or rollback the transaction. -** -** * A write transaction is active. -** * An EXCLUSIVE or greater lock is held on the database file. -** * All writing and syncing of journal and database data has finished. -** If no error occured, all that remains is to finalize the journal to -** commit the transaction. If an error did occur, the caller will need -** to rollback the transaction. -** -** -*/ -#define PAGER_OPEN 0 -#define PAGER_READER 1 -#define PAGER_WRITER_LOCKED 2 -#define PAGER_WRITER_CACHEMOD 3 -#define PAGER_WRITER_DBMOD 4 -#define PAGER_WRITER_FINISHED 5 -/* -** Journal files begin with the following magic string. The data -** was obtained from /dev/random. It is used only as a sanity check. -** -** NOTE: These values must be different from the one used by SQLite3 -** to avoid journal file collision. -** -*/ -static const unsigned char aJournalMagic[] = { - 0xa6, 0xe8, 0xcd, 0x2b, 0x1c, 0x92, 0xdb, 0x9f, -}; -/* -** The journal header size for this pager. This is usually the same -** size as a single disk sector. See also setSectorSize(). -*/ -#define JOURNAL_HDR_SZ(pPager) (pPager->iSectorSize) -/* - * Database page handle. - * Each raw disk page is represented in memory by an instance - * of the following structure. - */ -typedef struct Page Page; -struct Page { - /* Must correspond to unqlite_page */ - unsigned char *zData; /* Content of this page */ - void *pUserData; /* Extra content */ - pgno pgno; /* Page number for this page */ - /********************************************************************** - ** Elements above are public. All that follows is private to pcache.c - ** and should not be accessed by other modules. - */ - Pager *pPager; /* The pager this page is part of */ - int flags; /* Page flags defined below */ - int nRef; /* Number of users of this page */ - Page *pNext, *pPrev; /* A list of all pages */ - Page *pDirtyNext; /* Next element in list of dirty pages */ - Page *pDirtyPrev; /* Previous element in list of dirty pages */ - Page *pNextCollide,*pPrevCollide; /* Collission chain */ - Page *pNextHot,*pPrevHot; /* Hot dirty pages chain */ -}; -/* Bit values for Page.flags */ -#define PAGE_DIRTY 0x002 /* Page has changed */ -#define PAGE_NEED_SYNC 0x004 /* fsync the rollback journal before - ** writing this page to the database */ -#define PAGE_DONT_WRITE 0x008 /* Dont write page content to disk */ -#define PAGE_NEED_READ 0x010 /* Content is unread */ -#define PAGE_IN_JOURNAL 0x020 /* Page written to the journal */ -#define PAGE_HOT_DIRTY 0x040 /* Hot dirty page */ -#define PAGE_DONT_MAKE_HOT 0x080 /* Dont make this page Hot. In other words, - * do not link it to the hot dirty list. - */ -/* - * Each active database pager is represented by an instance of - * the following structure. - */ -struct Pager -{ - SyMemBackend *pAllocator; /* Memory backend */ - unqlite *pDb; /* DB handle that own this instance */ - unqlite_kv_engine *pEngine; /* Underlying KV storage engine */ - char *zFilename; /* Name of the database file */ - char *zJournal; /* Name of the journal file */ - unqlite_vfs *pVfs; /* Underlying virtual file system */ - unqlite_file *pfd,*pjfd; /* File descriptors for database and journal */ - pgno dbSize; /* Number of pages in the file */ - pgno dbOrigSize; /* dbSize before the current change */ - sxi64 dbByteSize; /* Database size in bytes */ - void *pMmap; /* Read-only Memory view (mmap) of the whole file if requested (UNQLITE_OPEN_MMAP). */ - sxu32 nRec; /* Number of pages written to the journal */ - SyPRNGCtx sPrng; /* PRNG Context */ - sxu32 cksumInit; /* Quasi-random value added to every checksum */ - sxu32 iOpenFlags; /* Flag passed to unqlite_open() after processing */ - sxi64 iJournalOfft; /* Journal offset we are reading from */ - int (*xBusyHandler)(void *); /* Busy handler */ - void *pBusyHandlerArg; /* First arg to xBusyHandler() */ - void (*xPageUnpin)(void *); /* Page Unpin callback */ - void (*xPageReload)(void *); /* Page Reload callback */ - Bitvec *pVec; /* Bitmap */ - Page *pHeader; /* Page one of the database (Unqlite header) */ - Sytm tmCreate; /* Database creation time */ - SyString sKv; /* Underlying Key/Value storage engine name */ - int iState; /* Pager state */ - int iLock; /* Lock state */ - sxi32 iFlags; /* Control flags (see below) */ - int is_mem; /* True for an in-memory database */ - int is_rdonly; /* True for a read-only database */ - int no_jrnl; /* TRUE to omit journaling */ - int iPageSize; /* Page size in bytes (default 4K) */ - int iSectorSize; /* Size of a single sector on disk */ - unsigned char *zTmpPage; /* Temporary page */ - Page *pFirstDirty; /* First dirty pages */ - Page *pDirty; /* Transient list of dirty pages */ - Page *pAll; /* List of all pages */ - Page *pHotDirty; /* List of hot dirty pages */ - Page *pFirstHot; /* First hot dirty page */ - sxu32 nHot; /* Total number of hot dirty pages */ - Page **apHash; /* Page table */ - sxu32 nSize; /* apHash[] size: Must be a power of two */ - sxu32 nPage; /* Total number of page loaded in memory */ - sxu32 nCacheMax; /* Maximum page to cache*/ -}; -/* Control flags */ -#define PAGER_CTRL_COMMIT_ERR 0x001 /* Commit error */ -#define PAGER_CTRL_DIRTY_COMMIT 0x002 /* Dirty commit has been applied */ -/* -** Read a 32-bit integer from the given file descriptor. -** All values are stored on disk as big-endian. -*/ -static int ReadInt32(unqlite_file *pFd,sxu32 *pOut,sxi64 iOfft) -{ - unsigned char zBuf[4]; - int rc; - rc = unqliteOsRead(pFd,zBuf,sizeof(zBuf),iOfft); - if( rc != UNQLITE_OK ){ - return rc; - } - SyBigEndianUnpack32(zBuf,pOut); - return UNQLITE_OK; -} -/* -** Read a 64-bit integer from the given file descriptor. -** All values are stored on disk as big-endian. -*/ -static int ReadInt64(unqlite_file *pFd,sxu64 *pOut,sxi64 iOfft) -{ - unsigned char zBuf[8]; - int rc; - rc = unqliteOsRead(pFd,zBuf,sizeof(zBuf),iOfft); - if( rc != UNQLITE_OK ){ - return rc; - } - SyBigEndianUnpack64(zBuf,pOut); - return UNQLITE_OK; -} -/* -** Write a 32-bit integer into the given file descriptor. -*/ -static int WriteInt32(unqlite_file *pFd,sxu32 iNum,sxi64 iOfft) -{ - unsigned char zBuf[4]; - int rc; - SyBigEndianPack32(zBuf,iNum); - rc = unqliteOsWrite(pFd,zBuf,sizeof(zBuf),iOfft); - return rc; -} -/* -** Write a 64-bit integer into the given file descriptor. -*/ -static int WriteInt64(unqlite_file *pFd,sxu64 iNum,sxi64 iOfft) -{ - unsigned char zBuf[8]; - int rc; - SyBigEndianPack64(zBuf,iNum); - rc = unqliteOsWrite(pFd,zBuf,sizeof(zBuf),iOfft); - return rc; -} -/* -** The maximum allowed sector size. 64KiB. If the xSectorsize() method -** returns a value larger than this, then MAX_SECTOR_SIZE is used instead. -** This could conceivably cause corruption following a power failure on -** such a system. This is currently an undocumented limit. -*/ -#define MAX_SECTOR_SIZE 0x10000 -/* -** Get the size of a single sector on disk. -** The sector size will be used used to determine the size -** and alignment of journal header and within created journal files. -** -** The default sector size is set to 512. -*/ -static int GetSectorSize(unqlite_file *pFd) -{ - int iSectorSize = UNQLITE_DEFAULT_SECTOR_SIZE; - if( pFd ){ - iSectorSize = unqliteOsSectorSize(pFd); - if( iSectorSize < 32 ){ - iSectorSize = 512; - } - if( iSectorSize > MAX_SECTOR_SIZE ){ - iSectorSize = MAX_SECTOR_SIZE; - } - } - return iSectorSize; -} -/* Hash function for page number */ -#define PAGE_HASH(PNUM) (PNUM) -/* - * Fetch a page from the cache. - */ -static Page * pager_fetch_page(Pager *pPager,pgno page_num) -{ - Page *pEntry; - if( pPager->nPage < 1 ){ - /* Don't bother hashing */ - return 0; - } - /* Perform the lookup */ - pEntry = pPager->apHash[PAGE_HASH(page_num) & (pPager->nSize - 1)]; - for(;;){ - if( pEntry == 0 ){ - break; - } - if( pEntry->pgno == page_num ){ - return pEntry; - } - /* Point to the next entry in the colission chain */ - pEntry = pEntry->pNextCollide; - } - /* No such page */ - return 0; -} -/* - * Allocate and initialize a new page. - */ -static Page * pager_alloc_page(Pager *pPager,pgno num_page) -{ - Page *pNew; - - pNew = (Page *)SyMemBackendPoolAlloc(pPager->pAllocator,sizeof(Page)+pPager->iPageSize); - if( pNew == 0 ){ - return 0; - } - /* Zero the structure */ - SyZero(pNew,sizeof(Page)+pPager->iPageSize); - /* Page data */ - pNew->zData = (unsigned char *)&pNew[1]; - /* Fill in the structure */ - pNew->pPager = pPager; - pNew->nRef = 1; - pNew->pgno = num_page; - return pNew; -} -/* - * Increment the reference count of a given page. - */ -static void page_ref(Page *pPage) -{ - pPage->nRef++; -} -/* - * Release an in-memory page after its reference count reach zero. - */ -static int pager_release_page(Pager *pPager,Page *pPage) -{ - int rc = UNQLITE_OK; - if( !(pPage->flags & PAGE_DIRTY)){ - /* Invoke the unpin callback if available */ - if( pPager->xPageUnpin && pPage->pUserData ){ - pPager->xPageUnpin(pPage->pUserData); - } - pPage->pUserData = 0; - SyMemBackendPoolFree(pPager->pAllocator,pPage); - }else{ - /* Dirty page, it will be released later when a dirty commit - * or the final commit have been applied. - */ - rc = UNQLITE_LOCKED; - } - return rc; -} -/* Forward declaration */ -static int pager_unlink_page(Pager *pPager,Page *pPage); -/* - * Decrement the reference count of a given page. - */ -static void page_unref(Page *pPage) -{ - pPage->nRef--; - if( pPage->nRef < 1 ){ - Pager *pPager = pPage->pPager; - if( !(pPage->flags & PAGE_DIRTY) ){ - pager_unlink_page(pPager,pPage); - /* Release the page */ - pager_release_page(pPager,pPage); - }else{ - if( pPage->flags & PAGE_DONT_MAKE_HOT ){ - /* Do not add this page to the hot dirty list */ - return; - } - if( !(pPage->flags & PAGE_HOT_DIRTY) ){ - /* Add to the hot dirty list */ - pPage->pPrevHot = 0; - if( pPager->pFirstHot == 0 ){ - pPager->pFirstHot = pPager->pHotDirty = pPage; - }else{ - pPage->pNextHot = pPager->pHotDirty; - if( pPager->pHotDirty ){ - pPager->pHotDirty->pPrevHot = pPage; - } - pPager->pHotDirty = pPage; - } - pPager->nHot++; - pPage->flags |= PAGE_HOT_DIRTY; - } - } - } -} -/* - * Link a freshly created page to the list of active page. - */ -static int pager_link_page(Pager *pPager,Page *pPage) -{ - sxu32 nBucket; - /* Install in the corresponding bucket */ - nBucket = PAGE_HASH(pPage->pgno) & (pPager->nSize - 1); - pPage->pNextCollide = pPager->apHash[nBucket]; - if( pPager->apHash[nBucket] ){ - pPager->apHash[nBucket]->pPrevCollide = pPage; - } - pPager->apHash[nBucket] = pPage; - /* Link to the list of active pages */ - MACRO_LD_PUSH(pPager->pAll,pPage); - pPager->nPage++; - if( (pPager->nPage >= pPager->nSize * 4) && pPager->nPage < 100000 ){ - /* Grow the hashtable */ - sxu32 nNewSize = pPager->nSize << 1; - Page *pEntry,**apNew; - sxu32 n; - apNew = (Page **)SyMemBackendAlloc(pPager->pAllocator, nNewSize * sizeof(Page *)); - if( apNew ){ - sxu32 iBucket; - /* Zero the new table */ - SyZero((void *)apNew, nNewSize * sizeof(Page *)); - /* Rehash all entries */ - n = 0; - pEntry = pPager->pAll; - for(;;){ - /* Loop one */ - if( n >= pPager->nPage ){ - break; - } - pEntry->pNextCollide = pEntry->pPrevCollide = 0; - /* Install in the new bucket */ - iBucket = PAGE_HASH(pEntry->pgno) & (nNewSize - 1); - pEntry->pNextCollide = apNew[iBucket]; - if( apNew[iBucket] ){ - apNew[iBucket]->pPrevCollide = pEntry; - } - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - n++; - } - /* Release the old table and reflect the change */ - SyMemBackendFree(pPager->pAllocator,(void *)pPager->apHash); - pPager->apHash = apNew; - pPager->nSize = nNewSize; - } - } - return UNQLITE_OK; -} -/* - * Unlink a page from the list of active pages. - */ -static int pager_unlink_page(Pager *pPager,Page *pPage) -{ - if( pPage->pNextCollide ){ - pPage->pNextCollide->pPrevCollide = pPage->pPrevCollide; - } - if( pPage->pPrevCollide ){ - pPage->pPrevCollide->pNextCollide = pPage->pNextCollide; - }else{ - sxu32 nBucket = PAGE_HASH(pPage->pgno) & (pPager->nSize - 1); - pPager->apHash[nBucket] = pPage->pNextCollide; - } - MACRO_LD_REMOVE(pPager->pAll,pPage); - pPager->nPage--; - return UNQLITE_OK; -} -/* - * Update the content of a cached page. - */ -static int pager_fill_page(Pager *pPager,pgno iNum,void *pContents) -{ - Page *pPage; - /* Fetch the page from the catch */ - pPage = pager_fetch_page(pPager,iNum); - if( pPage == 0 ){ - return SXERR_NOTFOUND; - } - /* Reflect the change */ - SyMemcpy(pContents,pPage->zData,pPager->iPageSize); - - return UNQLITE_OK; -} -/* - * Read the content of a page from disk. - */ -static int pager_get_page_contents(Pager *pPager,Page *pPage,int noContent) -{ - int rc = UNQLITE_OK; - if( pPager->is_mem || noContent || pPage->pgno >= pPager->dbSize ){ - /* Do not bother reading, zero the page contents only */ - SyZero(pPage->zData,pPager->iPageSize); - return UNQLITE_OK; - } - if( (pPager->iOpenFlags & UNQLITE_OPEN_MMAP) && (pPager->pMmap /* Paranoid edition */) ){ - unsigned char *zMap = (unsigned char *)pPager->pMmap; - pPage->zData = &zMap[pPage->pgno * pPager->iPageSize]; - }else{ - /* Read content */ - rc = unqliteOsRead(pPager->pfd,pPage->zData,pPager->iPageSize,pPage->pgno * pPager->iPageSize); - } - return rc; -} -/* - * Add a page to the dirty list. - */ -static void pager_page_to_dirty_list(Pager *pPager,Page *pPage) -{ - if( pPage->flags & PAGE_DIRTY ){ - /* Already set */ - return; - } - /* Mark the page as dirty */ - pPage->flags |= PAGE_DIRTY|PAGE_NEED_SYNC|PAGE_IN_JOURNAL; - /* Link to the list */ - pPage->pDirtyPrev = 0; - pPage->pDirtyNext = pPager->pDirty; - if( pPager->pDirty ){ - pPager->pDirty->pDirtyPrev = pPage; - } - pPager->pDirty = pPage; - if( pPager->pFirstDirty == 0 ){ - pPager->pFirstDirty = pPage; - } -} -/* - * Merge sort. - * The merge sort implementation is based on the one used by - * the PH7 Embeddable PHP Engine (http://ph7.symisc.net/). - */ -/* -** Inputs: -** a: A sorted, null-terminated linked list. (May be null). -** b: A sorted, null-terminated linked list. (May be null). -** cmp: A pointer to the comparison function. -** -** Return Value: -** A pointer to the head of a sorted list containing the elements -** of both a and b. -** -** Side effects: -** The "next", "prev" pointers for elements in the lists a and b are -** changed. -*/ -static Page * page_merge_dirty(Page *pA, Page *pB) -{ - Page result, *pTail; - /* Prevent compiler warning */ - result.pDirtyNext = result.pDirtyPrev = 0; - pTail = &result; - while( pA && pB ){ - if( pA->pgno < pB->pgno ){ - pTail->pDirtyPrev = pA; - pA->pDirtyNext = pTail; - pTail = pA; - pA = pA->pDirtyPrev; - }else{ - pTail->pDirtyPrev = pB; - pB->pDirtyNext = pTail; - pTail = pB; - pB = pB->pDirtyPrev; - } - } - if( pA ){ - pTail->pDirtyPrev = pA; - pA->pDirtyNext = pTail; - }else if( pB ){ - pTail->pDirtyPrev = pB; - pB->pDirtyNext = pTail; - }else{ - pTail->pDirtyPrev = pTail->pDirtyNext = 0; - } - return result.pDirtyPrev; -} -/* -** Inputs: -** Map: Input hashmap -** cmp: A comparison function. -** -** Return Value: -** Sorted hashmap. -** -** Side effects: -** The "next" pointers for elements in list are changed. -*/ -#define N_SORT_BUCKET 32 -static Page * pager_get_dirty_pages(Pager *pPager) -{ - Page *a[N_SORT_BUCKET], *p, *pIn; - sxu32 i; - if( pPager->pFirstDirty == 0 ){ - /* Don't bother sorting, the list is already empty */ - return 0; - } - SyZero(a, sizeof(a)); - /* Point to the first inserted entry */ - pIn = pPager->pFirstDirty; - while( pIn ){ - p = pIn; - pIn = p->pDirtyPrev; - p->pDirtyPrev = 0; - for(i=0; ipDirtyNext = 0; - return p; -} -/* - * See block comment above. - */ -static Page * page_merge_hot(Page *pA, Page *pB) -{ - Page result, *pTail; - /* Prevent compiler warning */ - result.pNextHot = result.pPrevHot = 0; - pTail = &result; - while( pA && pB ){ - if( pA->pgno < pB->pgno ){ - pTail->pPrevHot = pA; - pA->pNextHot = pTail; - pTail = pA; - pA = pA->pPrevHot; - }else{ - pTail->pPrevHot = pB; - pB->pNextHot = pTail; - pTail = pB; - pB = pB->pPrevHot; - } - } - if( pA ){ - pTail->pPrevHot = pA; - pA->pNextHot = pTail; - }else if( pB ){ - pTail->pPrevHot = pB; - pB->pNextHot = pTail; - }else{ - pTail->pPrevHot = pTail->pNextHot = 0; - } - return result.pPrevHot; -} -/* -** Inputs: -** Map: Input hashmap -** cmp: A comparison function. -** -** Return Value: -** Sorted hashmap. -** -** Side effects: -** The "next" pointers for elements in list are changed. -*/ -#define N_SORT_BUCKET 32 -static Page * pager_get_hot_pages(Pager *pPager) -{ - Page *a[N_SORT_BUCKET], *p, *pIn; - sxu32 i; - if( pPager->pFirstHot == 0 ){ - /* Don't bother sorting, the list is already empty */ - return 0; - } - SyZero(a, sizeof(a)); - /* Point to the first inserted entry */ - pIn = pPager->pFirstHot; - while( pIn ){ - p = pIn; - pIn = p->pPrevHot; - p->pPrevHot = 0; - for(i=0; ipNextHot = 0; - return p; -} -/* -** The format for the journal header is as follows: -** - 8 bytes: Magic identifying journal format. -** - 4 bytes: Number of records in journal. -** - 4 bytes: Random number used for page hash. -** - 8 bytes: Initial database page count. -** - 4 bytes: Sector size used by the process that wrote this journal. -** - 4 bytes: Database page size. -** -** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space. -*/ -/* -** Open the journal file and extract its header information. -** -** If the header is read successfully, *pNRec is set to the number of -** page records following this header and *pDbSize is set to the size of the -** database before the transaction began, in pages. Also, pPager->cksumInit -** is set to the value read from the journal header. UNQLITE_OK is returned -** in this case. -** -** If the journal header file appears to be corrupted, UNQLITE_DONE is -** returned and *pNRec and *PDbSize are undefined. If JOURNAL_HDR_SZ bytes -** cannot be read from the journal file an error code is returned. -*/ -static int pager_read_journal_header( - Pager *pPager, /* Pager object */ - sxu32 *pNRec, /* OUT: Value read from the nRec field */ - pgno *pDbSize /* OUT: Value of original database size field */ -) -{ - sxu32 iPageSize,iSectorSize; - unsigned char zMagic[8]; - sxi64 iHdrOfft; - sxi64 iSize; - int rc; - /* Offset to start reading from */ - iHdrOfft = 0; - /* Get the size of the journal */ - rc = unqliteOsFileSize(pPager->pjfd,&iSize); - if( rc != UNQLITE_OK ){ - return UNQLITE_DONE; - } - /* If the journal file is too small, return UNQLITE_DONE. */ - if( 32 /* Minimum sector size */> iSize ){ - return UNQLITE_DONE; - } - /* Make sure we are dealing with a valid journal */ - rc = unqliteOsRead(pPager->pjfd,zMagic,sizeof(zMagic),iHdrOfft); - if( rc != UNQLITE_OK ){ - return rc; - } - if( SyMemcmp(zMagic,aJournalMagic,sizeof(zMagic)) != 0 ){ - return UNQLITE_DONE; - } - iHdrOfft += sizeof(zMagic); - /* Read the first three 32-bit fields of the journal header: The nRec - ** field, the checksum-initializer and the database size at the start - ** of the transaction. Return an error code if anything goes wrong. - */ - rc = ReadInt32(pPager->pjfd,pNRec,iHdrOfft); - if( rc != UNQLITE_OK ){ - return rc; - } - iHdrOfft += 4; - rc = ReadInt32(pPager->pjfd,&pPager->cksumInit,iHdrOfft); - if( rc != UNQLITE_OK ){ - return rc; - } - iHdrOfft += 4; - rc = ReadInt64(pPager->pjfd,pDbSize,iHdrOfft); - if( rc != UNQLITE_OK ){ - return rc; - } - iHdrOfft += 8; - /* Read the page-size and sector-size journal header fields. */ - rc = ReadInt32(pPager->pjfd,&iSectorSize,iHdrOfft); - if( rc != UNQLITE_OK ){ - return rc; - } - iHdrOfft += 4; - rc = ReadInt32(pPager->pjfd,&iPageSize,iHdrOfft); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Check that the values read from the page-size and sector-size fields - ** are within range. To be 'in range', both values need to be a power - ** of two greater than or equal to 512 or 32, and not greater than their - ** respective compile time maximum limits. - */ - if( iPageSize < UNQLITE_MIN_PAGE_SIZE || iSectorSize<32 - || iPageSize > UNQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE - || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0 - ){ - /* If the either the page-size or sector-size in the journal-header is - ** invalid, then the process that wrote the journal-header must have - ** crashed before the header was synced. In this case stop reading - ** the journal file here. - */ - return UNQLITE_DONE; - } - /* Update the assumed sector-size to match the value used by - ** the process that created this journal. If this journal was - ** created by a process other than this one, then this routine - ** is being called from within pager_playback(). The local value - ** of Pager.sectorSize is restored at the end of that routine. - */ - pPager->iSectorSize = iSectorSize; - pPager->iPageSize = iPageSize; - /* Ready to rollback */ - pPager->iJournalOfft = JOURNAL_HDR_SZ(pPager); - /* All done */ - return UNQLITE_OK; -} -/* - * Write the journal header in the given memory buffer. - * The given buffer is big enough to hold the whole header. - */ -static int pager_write_journal_header(Pager *pPager,unsigned char *zBuf) -{ - unsigned char *zPtr = zBuf; - /* 8 bytes magic number */ - SyMemcpy(aJournalMagic,zPtr,sizeof(aJournalMagic)); - zPtr += sizeof(aJournalMagic); - /* 4 bytes: Number of records in journal. */ - SyBigEndianPack32(zPtr,0); - zPtr += 4; - /* 4 bytes: Random number used to compute page checksum. */ - SyBigEndianPack32(zPtr,pPager->cksumInit); - zPtr += 4; - /* 8 bytes: Initial database page count. */ - SyBigEndianPack64(zPtr,pPager->dbOrigSize); - zPtr += 8; - /* 4 bytes: Sector size used by the process that wrote this journal. */ - SyBigEndianPack32(zPtr,(sxu32)pPager->iSectorSize); - zPtr += 4; - /* 4 bytes: Database page size. */ - SyBigEndianPack32(zPtr,(sxu32)pPager->iPageSize); - return UNQLITE_OK; -} -/* -** Parameter aData must point to a buffer of pPager->pageSize bytes -** of data. Compute and return a checksum based ont the contents of the -** page of data and the current value of pPager->cksumInit. -** -** This is not a real checksum. It is really just the sum of the -** random initial value (pPager->cksumInit) and every 200th byte -** of the page data, starting with byte offset (pPager->pageSize%200). -** Each byte is interpreted as an 8-bit unsigned integer. -** -** Changing the formula used to compute this checksum results in an -** incompatible journal file format. -** -** If journal corruption occurs due to a power failure, the most likely -** scenario is that one end or the other of the record will be changed. -** It is much less likely that the two ends of the journal record will be -** correct and the middle be corrupt. Thus, this "checksum" scheme, -** though fast and simple, catches the mostly likely kind of corruption. -*/ -static sxu32 pager_cksum(Pager *pPager,const unsigned char *zData) -{ - sxu32 cksum = pPager->cksumInit; /* Checksum value to return */ - int i = pPager->iPageSize-200; /* Loop counter */ - while( i>0 ){ - cksum += zData[i]; - i -= 200; - } - return cksum; -} -/* -** Read a single page from the journal file opened on file descriptor -** jfd. Playback this one page. Update the offset to read from. -*/ -static int pager_play_back_one_page(Pager *pPager,sxi64 *pOfft,unsigned char *zTmp) -{ - unsigned char *zData = zTmp; - sxi64 iOfft; /* Offset to read from */ - pgno iNum; /* Pager number */ - sxu32 ckSum; /* Sanity check */ - int rc; - /* Offset to start reading from */ - iOfft = *pOfft; - /* Database page number */ - rc = ReadInt64(pPager->pjfd,&iNum,iOfft); - if( rc != UNQLITE_OK ){ return rc; } - iOfft += 8; - /* Page data */ - rc = unqliteOsRead(pPager->pjfd,zData,pPager->iPageSize,iOfft); - if( rc != UNQLITE_OK ){ return rc; } - iOfft += pPager->iPageSize; - /* Page cksum */ - rc = ReadInt32(pPager->pjfd,&ckSum,iOfft); - if( rc != UNQLITE_OK ){ return rc; } - iOfft += 4; - /* Synchronize pointers */ - *pOfft = iOfft; - /* Make sure we are dealing with a valid page */ - if( ckSum != pager_cksum(pPager,zData) ){ - /* Ignore that page */ - return SXERR_IGNORE; - } - if( iNum >= pPager->dbSize ){ - /* Ignore that page */ - return UNQLITE_OK; - } - /* playback */ - rc = unqliteOsWrite(pPager->pfd,zData,pPager->iPageSize,iNum * pPager->iPageSize); - if( rc == UNQLITE_OK ){ - /* Flush the cache */ - pager_fill_page(pPager,iNum,zData); - } - return rc; -} -/* -** Playback the journal and thus restore the database file to -** the state it was in before we started making changes. -** -** The journal file format is as follows: -** -** (1) 8 byte prefix. A copy of aJournalMagic[]. -** (2) 4 byte big-endian integer which is the number of valid page records -** in the journal. -** (3) 4 byte big-endian integer which is the initial value for the -** sanity checksum. -** (4) 8 byte integer which is the number of pages to truncate the -** database to during a rollback. -** (5) 4 byte big-endian integer which is the sector size. The header -** is this many bytes in size. -** (6) 4 byte big-endian integer which is the page size. -** (7) zero padding out to the next sector size. -** (8) Zero or more pages instances, each as follows: -** + 4 byte page number. -** + pPager->pageSize bytes of data. -** + 4 byte checksum -** -** When we speak of the journal header, we mean the first 7 items above. -** Each entry in the journal is an instance of the 8th item. -** -** Call the value from the second bullet "nRec". nRec is the number of -** valid page entries in the journal. In most cases, you can compute the -** value of nRec from the size of the journal file. But if a power -** failure occurred while the journal was being written, it could be the -** case that the size of the journal file had already been increased but -** the extra entries had not yet made it safely to disk. In such a case, -** the value of nRec computed from the file size would be too large. For -** that reason, we always use the nRec value in the header. -** -** If the file opened as the journal file is not a well-formed -** journal file then all pages up to the first corrupted page are rolled -** back (or no pages if the journal header is corrupted). The journal file -** is then deleted and SQLITE_OK returned, just as if no corruption had -** been encountered. -** -** If an I/O or malloc() error occurs, the journal-file is not deleted -** and an error code is returned. -** -*/ -static int pager_playback(Pager *pPager) -{ - unsigned char *zTmp = 0; /* cc warning */ - sxu32 n,nRec; - sxi64 iOfft; - int rc; - /* Read the journal header*/ - rc = pager_read_journal_header(pPager,&nRec,&pPager->dbSize); - if( rc != UNQLITE_OK ){ - if( rc == UNQLITE_DONE ){ - goto end_playback; - } - unqliteGenErrorFormat(pPager->pDb,"IO error while reading journal file '%s' header",pPager->zJournal); - return rc; - } - /* Truncate the database back to its original size */ - rc = unqliteOsTruncate(pPager->pfd,pPager->iPageSize * pPager->dbSize); - if( rc != UNQLITE_OK ){ - unqliteGenError(pPager->pDb,"IO error while truncating database file"); - return rc; - } - /* Allocate a temporary page */ - zTmp = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iPageSize); - if( zTmp == 0 ){ - unqliteGenOutofMem(pPager->pDb); - return UNQLITE_NOMEM; - } - SyZero((void *)zTmp,(sxu32)pPager->iPageSize); - /* Copy original pages out of the journal and back into the - ** database file and/or page cache. - */ - iOfft = pPager->iJournalOfft; - for( n = 0 ; n < nRec ; ++n ){ - rc = pager_play_back_one_page(pPager,&iOfft,zTmp); - if( rc != UNQLITE_OK ){ - if( rc != SXERR_IGNORE ){ - unqliteGenError(pPager->pDb,"Page playback error"); - goto end_playback; - } - } - } -end_playback: - /* Release the temp page */ - SyMemBackendFree(pPager->pAllocator,(void *)zTmp); - if( rc == UNQLITE_OK ){ - /* Sync the database file */ - unqliteOsSync(pPager->pfd,UNQLITE_SYNC_FULL); - } - if( rc == UNQLITE_DONE ){ - rc = UNQLITE_OK; - } - /* Return to the caller */ - return rc; -} -/* -** Unlock the database file to level eLock, which must be either NO_LOCK -** or SHARED_LOCK. Regardless of whether or not the call to xUnlock() -** succeeds, set the Pager.iLock variable to match the (attempted) new lock. -** -** Except, if Pager.iLock is set to NO_LOCK when this function is -** called, do not modify it. See the comment above the #define of -** NO_LOCK for an explanation of this. -*/ -static int pager_unlock_db(Pager *pPager, int eLock) -{ - int rc = UNQLITE_OK; - if( pPager->iLock != NO_LOCK ){ - rc = unqliteOsUnlock(pPager->pfd,eLock); - pPager->iLock = eLock; - } - return rc; -} -/* -** Lock the database file to level eLock, which must be either SHARED_LOCK, -** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the -** Pager.eLock variable to the new locking state. -** -** Except, if Pager.eLock is set to NO_LOCK when this function is -** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. -** See the comment above the #define of NO_LOCK for an explanation -** of this. -*/ -static int pager_lock_db(Pager *pPager, int eLock){ - int rc = UNQLITE_OK; - if( pPager->iLock < eLock || pPager->iLock == NO_LOCK ){ - rc = unqliteOsLock(pPager->pfd, eLock); - if( rc==UNQLITE_OK ){ - pPager->iLock = eLock; - }else{ - unqliteGenError(pPager->pDb, - rc == UNQLITE_BUSY ? "Another process or thread hold the requested lock" : "Error while requesting database lock" - ); - } - } - return rc; -} -/* -** Try to obtain a lock of type locktype on the database file. If -** a similar or greater lock is already held, this function is a no-op -** (returning UNQLITE_OK immediately). -** -** Otherwise, attempt to obtain the lock using unqliteOsLock(). Invoke -** the busy callback if the lock is currently not available. Repeat -** until the busy callback returns false or until the attempt to -** obtain the lock succeeds. -** -** Return UNQLITE_OK on success and an error code if we cannot obtain -** the lock. If the lock is obtained successfully, set the Pager.state -** variable to locktype before returning. -*/ -static int pager_wait_on_lock(Pager *pPager, int locktype){ - int rc; /* Return code */ - do { - rc = pager_lock_db(pPager,locktype); - }while( rc==UNQLITE_BUSY && pPager->xBusyHandler && pPager->xBusyHandler(pPager->pBusyHandlerArg) ); - return rc; -} -/* -** This function is called after transitioning from PAGER_OPEN to -** PAGER_SHARED state. It tests if there is a hot journal present in -** the file-system for the given pager. A hot journal is one that -** needs to be played back. According to this function, a hot-journal -** file exists if the following criteria are met: -** -** * The journal file exists in the file system, and -** * No process holds a RESERVED or greater lock on the database file, and -** * The database file itself is greater than 0 bytes in size, and -** * The first byte of the journal file exists and is not 0x00. -** -** If the current size of the database file is 0 but a journal file -** exists, that is probably an old journal left over from a prior -** database with the same name. In this case the journal file is -** just deleted using OsDelete, *pExists is set to 0 and UNQLITE_OK -** is returned. -** -** If a hot-journal file is found to exist, *pExists is set to 1 and -** UNQLITE_OK returned. If no hot-journal file is present, *pExists is -** set to 0 and UNQLITE_OK returned. If an IO error occurs while trying -** to determine whether or not a hot-journal file exists, the IO error -** code is returned and the value of *pExists is undefined. -*/ -static int pager_has_hot_journal(Pager *pPager, int *pExists) -{ - unqlite_vfs *pVfs = pPager->pVfs; - int rc = UNQLITE_OK; /* Return code */ - int exists = 1; /* True if a journal file is present */ - - *pExists = 0; - rc = unqliteOsAccess(pVfs, pPager->zJournal, UNQLITE_ACCESS_EXISTS, &exists); - if( rc==UNQLITE_OK && exists ){ - int locked = 0; /* True if some process holds a RESERVED lock */ - - /* Race condition here: Another process might have been holding the - ** the RESERVED lock and have a journal open at the unqliteOsAccess() - ** call above, but then delete the journal and drop the lock before - ** we get to the following unqliteOsCheckReservedLock() call. If that - ** is the case, this routine might think there is a hot journal when - ** in fact there is none. This results in a false-positive which will - ** be dealt with by the playback routine. - */ - rc = unqliteOsCheckReservedLock(pPager->pfd, &locked); - if( rc==UNQLITE_OK && !locked ){ - sxi64 n = 0; /* Size of db file in bytes */ - - /* Check the size of the database file. If it consists of 0 pages, - ** then delete the journal file. See the header comment above for - ** the reasoning here. Delete the obsolete journal file under - ** a RESERVED lock to avoid race conditions. - */ - rc = unqliteOsFileSize(pPager->pfd,&n); - if( rc==UNQLITE_OK ){ - if( n < 1 ){ - if( pager_lock_db(pPager, RESERVED_LOCK)==UNQLITE_OK ){ - unqliteOsDelete(pVfs, pPager->zJournal, 0); - pager_unlock_db(pPager, SHARED_LOCK); - } - }else{ - /* The journal file exists and no other connection has a reserved - ** or greater lock on the database file. */ - *pExists = 1; - } - } - } - } - return rc; -} -/* - * Rollback a journal file. (See block-comment above). - */ -static int pager_journal_rollback(Pager *pPager,int check_hot) -{ - int rc; - if( check_hot ){ - int iExists = 0; /* cc warning */ - /* Check if the journal file exists */ - rc = pager_has_hot_journal(pPager,&iExists); - if( rc != UNQLITE_OK ){ - /* IO error */ - return rc; - } - if( !iExists ){ - /* Journal file does not exists */ - return UNQLITE_OK; - } - } - if( pPager->is_rdonly ){ - unqliteGenErrorFormat(pPager->pDb, - "Cannot rollback journal file '%s' due to a read-only database handle",pPager->zJournal); - return UNQLITE_READ_ONLY; - } - /* Get an EXCLUSIVE lock on the database file. At this point it is - ** important that a RESERVED lock is not obtained on the way to the - ** EXCLUSIVE lock. If it were, another process might open the - ** database file, detect the RESERVED lock, and conclude that the - ** database is safe to read while this process is still rolling the - ** hot-journal back. - ** - ** Because the intermediate RESERVED lock is not requested, any - ** other process attempting to access the database file will get to - ** this point in the code and fail to obtain its own EXCLUSIVE lock - ** on the database file. - ** - ** Unless the pager is in locking_mode=exclusive mode, the lock is - ** downgraded to SHARED_LOCK before this function returns. - */ - /* Open the journal file */ - rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zJournal,&pPager->pjfd,UNQLITE_OPEN_READWRITE); - if( rc != UNQLITE_OK ){ - unqliteGenErrorFormat(pPager->pDb,"IO error while opening journal file: '%s'",pPager->zJournal); - goto fail; - } - rc = pager_lock_db(pPager,EXCLUSIVE_LOCK); - if( rc != UNQLITE_OK ){ - unqliteGenError(pPager->pDb,"Cannot acquire an exclusive lock on the database while journal rollback"); - goto fail; - } - /* Sync the journal file */ - unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL); - /* Finally rollback the database */ - rc = pager_playback(pPager); - /* Switch back to shared lock */ - pager_unlock_db(pPager,SHARED_LOCK); -fail: - /* Close the journal handle */ - unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd); - pPager->pjfd = 0; - if( rc == UNQLITE_OK ){ - /* Delete the journal file */ - unqliteOsDelete(pPager->pVfs,pPager->zJournal,TRUE); - } - return rc; -} -/* - * Write the unqlite header (First page). (Big-Endian) - */ -static int pager_write_db_header(Pager *pPager) -{ - unsigned char *zRaw = pPager->pHeader->zData; - unqlite_kv_engine *pEngine = pPager->pEngine; - sxu32 nDos; - sxu16 nLen; - /* Database signature */ - SyMemcpy(UNQLITE_DB_SIG,zRaw,sizeof(UNQLITE_DB_SIG)-1); - zRaw += sizeof(UNQLITE_DB_SIG)-1; - /* Database magic number */ - SyBigEndianPack32(zRaw,UNQLITE_DB_MAGIC); - zRaw += 4; /* 4 byte magic number */ - /* Database creation time */ - SyZero(&pPager->tmCreate,sizeof(Sytm)); - if( pPager->pVfs->xCurrentTime ){ - pPager->pVfs->xCurrentTime(pPager->pVfs,&pPager->tmCreate); - } - /* DOS time format (4 bytes) */ - SyTimeFormatToDos(&pPager->tmCreate,&nDos); - SyBigEndianPack32(zRaw,nDos); - zRaw += 4; /* 4 byte DOS time */ - /* Sector size */ - SyBigEndianPack32(zRaw,(sxu32)pPager->iSectorSize); - zRaw += 4; /* 4 byte sector size */ - /* Page size */ - SyBigEndianPack32(zRaw,(sxu32)pPager->iPageSize); - zRaw += 4; /* 4 byte page size */ - /* Key value storage engine */ - nLen = (sxu16)SyStrlen(pEngine->pIo->pMethods->zName); - SyBigEndianPack16(zRaw,nLen); /* 2 byte storage engine name */ - zRaw += 2; - SyMemcpy((const void *)pEngine->pIo->pMethods->zName,(void *)zRaw,nLen); - zRaw += nLen; - /* All rest are meta-data available to the host application */ - return UNQLITE_OK; -} -/* - * Read the unqlite header (first page). (Big-Endian) - */ -static int pager_extract_header(Pager *pPager,const unsigned char *zRaw,sxu32 nByte) -{ - const unsigned char *zEnd = &zRaw[nByte]; - sxu32 nDos,iMagic; - sxu16 nLen; - char *zKv; - /* Database signature */ - if( SyMemcmp(UNQLITE_DB_SIG,zRaw,sizeof(UNQLITE_DB_SIG)-1) != 0 ){ - /* Corrupt database */ - return UNQLITE_CORRUPT; - } - zRaw += sizeof(UNQLITE_DB_SIG)-1; - /* Database magic number */ - SyBigEndianUnpack32(zRaw,&iMagic); - zRaw += 4; /* 4 byte magic number */ - if( iMagic != UNQLITE_DB_MAGIC ){ - /* Corrupt database */ - return UNQLITE_CORRUPT; - } - /* Database creation time */ - SyBigEndianUnpack32(zRaw,&nDos); - zRaw += 4; /* 4 byte DOS time format */ - SyDosTimeFormat(nDos,&pPager->tmCreate); - /* Sector size */ - SyBigEndianUnpack32(zRaw,(sxu32 *)&pPager->iSectorSize); - zRaw += 4; /* 4 byte sector size */ - /* Page size */ - SyBigEndianUnpack32(zRaw,(sxu32 *)&pPager->iPageSize); - zRaw += 4; /* 4 byte page size */ - /* Check that the values read from the page-size and sector-size fields - ** are within range. To be 'in range', both values need to be a power - ** of two greater than or equal to 512 or 32, and not greater than their - ** respective compile time maximum limits. - */ - if( pPager->iPageSizeiSectorSize<32 - || pPager->iPageSize>UNQLITE_MAX_PAGE_SIZE || pPager->iSectorSize>MAX_SECTOR_SIZE - || ((pPager->iPageSize<-1)&pPager->iPageSize)!=0 || ((pPager->iSectorSize-1)&pPager->iSectorSize)!=0 - ){ - return UNQLITE_CORRUPT; - } - /* Key value storage engine */ - SyBigEndianUnpack16(zRaw,&nLen); /* 2 byte storage engine length */ - zRaw += 2; - if( nLen > (sxu16)(zEnd - zRaw) ){ - nLen = (sxu16)(zEnd - zRaw); - } - zKv = (char *)SyMemBackendDup(pPager->pAllocator,(const char *)zRaw,nLen); - if( zKv == 0 ){ - return UNQLITE_NOMEM; - } - SyStringInitFromBuf(&pPager->sKv,zKv,nLen); - return UNQLITE_OK; -} -/* - * Read the database header. - */ -static int pager_read_db_header(Pager *pPager) -{ - unsigned char zRaw[UNQLITE_MIN_PAGE_SIZE]; /* Minimum page size */ - sxi64 n = 0; /* Size of db file in bytes */ - int rc; - /* Get the file size first */ - rc = unqliteOsFileSize(pPager->pfd,&n); - if( rc != UNQLITE_OK ){ - return rc; - } - pPager->dbByteSize = n; - if( n > 0 ){ - unqlite_kv_methods *pMethods; - SyString *pKv; - pgno nPage; - if( n < UNQLITE_MIN_PAGE_SIZE ){ - /* A valid unqlite database must be at least 512 bytes long */ - unqliteGenError(pPager->pDb,"Malformed database image"); - return UNQLITE_CORRUPT; - } - /* Read the database header */ - rc = unqliteOsRead(pPager->pfd,zRaw,sizeof(zRaw),0); - if( rc != UNQLITE_OK ){ - unqliteGenError(pPager->pDb,"IO error while reading database header"); - return rc; - } - /* Extract the header */ - rc = pager_extract_header(pPager,zRaw,sizeof(zRaw)); - if( rc != UNQLITE_OK ){ - unqliteGenError(pPager->pDb,rc == UNQLITE_NOMEM ? "Unqlite is running out of memory" : "Malformed database image"); - return rc; - } - /* Update pager state */ - nPage = (pgno)(n / pPager->iPageSize); - if( nPage==0 && n>0 ){ - nPage = 1; - } - pPager->dbSize = nPage; - /* Laod the target Key/Value storage engine */ - pKv = &pPager->sKv; - pMethods = unqliteFindKVStore(pKv->zString,pKv->nByte); - if( pMethods == 0 ){ - unqliteGenErrorFormat(pPager->pDb,"No such Key/Value storage engine '%z'",pKv); - return UNQLITE_NOTIMPLEMENTED; - } - /* Install the new KV storage engine */ - rc = unqlitePagerRegisterKvEngine(pPager,pMethods); - if( rc != UNQLITE_OK ){ - return rc; - } - }else{ - /* Set a default page and sector size */ - pPager->iSectorSize = GetSectorSize(pPager->pfd); - pPager->iPageSize = unqliteGetPageSize(); - SyStringInitFromBuf(&pPager->sKv,pPager->pEngine->pIo->pMethods->zName,SyStrlen(pPager->pEngine->pIo->pMethods->zName)); - pPager->dbSize = 0; - } - /* Allocate a temporary page size */ - pPager->zTmpPage = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iPageSize); - if( pPager->zTmpPage == 0 ){ - unqliteGenOutofMem(pPager->pDb); - return UNQLITE_NOMEM; - } - SyZero(pPager->zTmpPage,(sxu32)pPager->iPageSize); - return UNQLITE_OK; -} -/* - * Write the database header. - */ -static int pager_create_header(Pager *pPager) -{ - Page *pHeader; - int rc; - /* Allocate a new page */ - pHeader = pager_alloc_page(pPager,0); - if( pHeader == 0 ){ - return UNQLITE_NOMEM; - } - pPager->pHeader = pHeader; - /* Link the page */ - pager_link_page(pPager,pHeader); - /* Add to the dirty list */ - pager_page_to_dirty_list(pPager,pHeader); - /* Write the database header */ - rc = pager_write_db_header(pPager); - return rc; -} -/* -** This function is called to obtain a shared lock on the database file. -** It is illegal to call unqlitePagerAcquire() until after this function -** has been successfully called. If a shared-lock is already held when -** this function is called, it is a no-op. -** -** The following operations are also performed by this function. -** -** 1) If the pager is currently in PAGER_OPEN state (no lock held -** on the database file), then an attempt is made to obtain a -** SHARED lock on the database file. Immediately after obtaining -** the SHARED lock, the file-system is checked for a hot-journal, -** which is played back if present. -** -** If everything is successful, UNQLITE_OK is returned. If an IO error -** occurs while locking the database, checking for a hot-journal file or -** rolling back a journal file, the IO error code is returned. -*/ -static int pager_shared_lock(Pager *pPager) -{ - int rc = UNQLITE_OK; - if( pPager->iState == PAGER_OPEN ){ - unqlite_kv_methods *pMethods; - /* Open the target database */ - rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zFilename,&pPager->pfd,pPager->iOpenFlags); - if( rc != UNQLITE_OK ){ - unqliteGenErrorFormat(pPager->pDb, - "IO error while opening the target database file: %s",pPager->zFilename - ); - return rc; - } - /* Try to obtain a shared lock */ - rc = pager_wait_on_lock(pPager,SHARED_LOCK); - if( rc == UNQLITE_OK ){ - if( pPager->iLock <= SHARED_LOCK ){ - /* Rollback any hot journal */ - rc = pager_journal_rollback(pPager,1); - if( rc != UNQLITE_OK ){ - return rc; - } - } - /* Read the database header */ - rc = pager_read_db_header(pPager); - if( rc != UNQLITE_OK ){ - return rc; - } - if(pPager->dbSize > 0 ){ - if( pPager->iOpenFlags & UNQLITE_OPEN_MMAP ){ - const jx9_vfs *pVfs = jx9ExportBuiltinVfs(); - /* Obtain a read-only memory view of the whole file */ - if( pVfs && pVfs->xMmap ){ - int vr; - vr = pVfs->xMmap(pPager->zFilename,&pPager->pMmap,&pPager->dbByteSize); - if( vr != JX9_OK ){ - /* Generate a warning */ - unqliteGenError(pPager->pDb,"Cannot obtain a read-only memory view of the target database"); - pPager->iOpenFlags &= ~UNQLITE_OPEN_MMAP; - } - }else{ - /* Generate a warning */ - unqliteGenError(pPager->pDb,"Cannot obtain a read-only memory view of the target database"); - pPager->iOpenFlags &= ~UNQLITE_OPEN_MMAP; - } - } - } - /* Update the pager state */ - pPager->iState = PAGER_READER; - /* Invoke the xOpen methods if available */ - pMethods = pPager->pEngine->pIo->pMethods; - if( pMethods->xOpen ){ - rc = pMethods->xOpen(pPager->pEngine,pPager->dbSize); - if( rc != UNQLITE_OK ){ - unqliteGenErrorFormat(pPager->pDb, - "xOpen() method of the underlying KV engine '%z' failed", - &pPager->sKv - ); - pager_unlock_db(pPager,NO_LOCK); - pPager->iState = PAGER_OPEN; - return rc; - } - } - }else if( rc == UNQLITE_BUSY ){ - unqliteGenError(pPager->pDb,"Another process or thread have a reserved or exclusive lock on this database"); - } - } - return rc; -} -/* -** Begin a write-transaction on the specified pager object. If a -** write-transaction has already been opened, this function is a no-op. -*/ -UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager) -{ - int rc; - /* Obtain a shared lock on the database first */ - rc = pager_shared_lock(pPager); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pPager->iState >= PAGER_WRITER_LOCKED ){ - return UNQLITE_OK; - } - if( pPager->is_rdonly ){ - unqliteGenError(pPager->pDb,"Read-only database"); - /* Read only database */ - return UNQLITE_READ_ONLY; - } - /* Obtain a reserved lock on the database */ - rc = pager_wait_on_lock(pPager,RESERVED_LOCK); - if( rc == UNQLITE_OK ){ - /* Create the bitvec */ - pPager->pVec = unqliteBitvecCreate(pPager->pAllocator,pPager->dbSize); - if( pPager->pVec == 0 ){ - unqliteGenOutofMem(pPager->pDb); - rc = UNQLITE_NOMEM; - goto fail; - } - /* Change to the WRITER_LOCK state */ - pPager->iState = PAGER_WRITER_LOCKED; - pPager->dbOrigSize = pPager->dbSize; - pPager->iJournalOfft = 0; - pPager->nRec = 0; - if( pPager->dbSize < 1 ){ - /* Write the database header */ - rc = pager_create_header(pPager); - if( rc != UNQLITE_OK ){ - goto fail; - } - pPager->dbSize = 1; - } - }else if( rc == UNQLITE_BUSY ){ - unqliteGenError(pPager->pDb,"Another process or thread have a reserved lock on this database"); - } - return rc; -fail: - /* Downgrade to shared lock */ - pager_unlock_db(pPager,SHARED_LOCK); - return rc; -} -/* -** This function is called at the start of every write transaction. -** There must already be a RESERVED or EXCLUSIVE lock on the database -** file when this routine is called. -** -*/ -static int unqliteOpenJournal(Pager *pPager) -{ - unsigned char *zHeader; - int rc = UNQLITE_OK; - if( pPager->is_mem || pPager->no_jrnl ){ - /* Journaling is omitted for this database */ - goto finish; - } - if( pPager->iState >= PAGER_WRITER_CACHEMOD ){ - /* Already opened */ - return UNQLITE_OK; - } - /* Delete any previously journal with the same name */ - unqliteOsDelete(pPager->pVfs,pPager->zJournal,1); - /* Open the journal file */ - rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zJournal, - &pPager->pjfd,UNQLITE_OPEN_CREATE|UNQLITE_OPEN_READWRITE); - if( rc != UNQLITE_OK ){ - unqliteGenErrorFormat(pPager->pDb,"IO error while opening journal file: %s",pPager->zJournal); - return rc; - } - /* Write the journal header */ - zHeader = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iSectorSize); - if( zHeader == 0 ){ - rc = UNQLITE_NOMEM; - goto fail; - } - pager_write_journal_header(pPager,zHeader); - /* Perform the disk write */ - rc = unqliteOsWrite(pPager->pjfd,zHeader,pPager->iSectorSize,0); - /* Offset to start writing from */ - pPager->iJournalOfft = pPager->iSectorSize; - /* All done, journal will be synced later */ - SyMemBackendFree(pPager->pAllocator,zHeader); -finish: - if( rc == UNQLITE_OK ){ - pPager->iState = PAGER_WRITER_CACHEMOD; - return UNQLITE_OK; - } -fail: - /* Unlink the journal file if something goes wrong */ - unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd); - unqliteOsDelete(pPager->pVfs,pPager->zJournal,0); - pPager->pjfd = 0; - return rc; -} -/* -** Sync the journal. In other words, make sure all the pages that have -** been written to the journal have actually reached the surface of the -** disk and can be restored in the event of a hot-journal rollback. -* -* This routine try also to obtain an exlusive lock on the database. -*/ -static int unqliteFinalizeJournal(Pager *pPager,int *pRetry,int close_jrnl) -{ - int rc; - *pRetry = 0; - /* Grab the exclusive lock first */ - rc = pager_lock_db(pPager,EXCLUSIVE_LOCK); - if( rc != UNQLITE_OK ){ - /* Retry the excusive lock process */ - *pRetry = 1; - rc = UNQLITE_OK; - } - if( pPager->no_jrnl ){ - /* Journaling is omitted, return immediately */ - return UNQLITE_OK; - } - /* Write the total number of database records */ - rc = WriteInt32(pPager->pjfd,pPager->nRec,8 /* sizeof(aJournalRec) */); - if( rc != UNQLITE_OK ){ - if( pPager->nRec > 0 ){ - return rc; - }else{ - /* Not so fatal */ - rc = UNQLITE_OK; - } - } - /* Sync the journal and close it */ - rc = unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL); - if( close_jrnl ){ - /* close the journal file */ - if( UNQLITE_OK != unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd) ){ - if( rc != UNQLITE_OK /* unqliteOsSync */ ){ - return rc; - } - } - pPager->pjfd = 0; - } - if( (*pRetry) == 1 ){ - if( pager_lock_db(pPager,EXCLUSIVE_LOCK) == UNQLITE_OK ){ - /* Got exclusive lock */ - *pRetry = 0; - } - } - return UNQLITE_OK; -} -/* - * Mark a single data page as writeable. The page is written into the - * main journal as required. - */ -static int page_write(Pager *pPager,Page *pPage) -{ - int rc; - if( !pPager->is_mem && !pPager->no_jrnl ){ - /* Write the page to the transaction journal */ - if( pPage->pgno < pPager->dbOrigSize && !unqliteBitvecTest(pPager->pVec,pPage->pgno) ){ - sxu32 cksum; - if( pPager->nRec == SXU32_HIGH ){ - /* Journal Limit reached */ - unqliteGenError(pPager->pDb,"Journal record limit reached, commit your changes"); - return UNQLITE_LIMIT; - } - /* Write the page number */ - rc = WriteInt64(pPager->pjfd,pPage->pgno,pPager->iJournalOfft); - if( rc != UNQLITE_OK ){ return rc; } - /* Write the raw page */ - /** CODEC */ - rc = unqliteOsWrite(pPager->pjfd,pPage->zData,pPager->iPageSize,pPager->iJournalOfft + 8); - if( rc != UNQLITE_OK ){ return rc; } - /* Compute the checksum */ - cksum = pager_cksum(pPager,pPage->zData); - rc = WriteInt32(pPager->pjfd,cksum,pPager->iJournalOfft + 8 + pPager->iPageSize); - if( rc != UNQLITE_OK ){ return rc; } - /* Update the journal offset */ - pPager->iJournalOfft += 8 /* page num */ + pPager->iPageSize + 4 /* cksum */; - pPager->nRec++; - /* Mark as journalled */ - unqliteBitvecSet(pPager->pVec,pPage->pgno); - } - } - /* Add the page to the dirty list */ - pager_page_to_dirty_list(pPager,pPage); - /* Update the database size and return. */ - if( (1 + pPage->pgno) > pPager->dbSize ){ - pPager->dbSize = 1 + pPage->pgno; - if( pPager->dbSize == SXU64_HIGH ){ - unqliteGenError(pPager->pDb,"Database maximum page limit (64-bit) reached"); - return UNQLITE_LIMIT; - } - } - return UNQLITE_OK; -} -/* -** The argument is the first in a linked list of dirty pages connected -** by the PgHdr.pDirty pointer. This function writes each one of the -** in-memory pages in the list to the database file. The argument may -** be NULL, representing an empty list. In this case this function is -** a no-op. -** -** The pager must hold at least a RESERVED lock when this function -** is called. Before writing anything to the database file, this lock -** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained, -** UNQLITE_BUSY is returned and no data is written to the database file. -*/ -static int pager_write_dirty_pages(Pager *pPager,Page *pDirty) -{ - int rc = UNQLITE_OK; - Page *pNext; - for(;;){ - if( pDirty == 0 ){ - break; - } - /* Point to the next dirty page */ - pNext = pDirty->pDirtyPrev; /* Not a bug: Reverse link */ - if( (pDirty->flags & PAGE_DONT_WRITE) == 0 ){ - rc = unqliteOsWrite(pPager->pfd,pDirty->zData,pPager->iPageSize,pDirty->pgno * pPager->iPageSize); - if( rc != UNQLITE_OK ){ - /* A rollback should be done */ - break; - } - } - /* Remove stale flags */ - pDirty->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY); - if( pDirty->nRef < 1 ){ - /* Unlink the page now it is unused */ - pager_unlink_page(pPager,pDirty); - /* Release the page */ - pager_release_page(pPager,pDirty); - } - /* Point to the next page */ - pDirty = pNext; - } - pPager->pDirty = pPager->pFirstDirty = 0; - pPager->pHotDirty = pPager->pFirstHot = 0; - pPager->nHot = 0; - return rc; -} -/* -** The argument is the first in a linked list of hot dirty pages connected -** by the PgHdr.pHotDirty pointer. This function writes each one of the -** in-memory pages in the list to the database file. The argument may -** be NULL, representing an empty list. In this case this function is -** a no-op. -** -** The pager must hold at least a RESERVED lock when this function -** is called. Before writing anything to the database file, this lock -** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained, -** UNQLITE_BUSY is returned and no data is written to the database file. -*/ -static int pager_write_hot_dirty_pages(Pager *pPager,Page *pDirty) -{ - int rc = UNQLITE_OK; - Page *pNext; - for(;;){ - if( pDirty == 0 ){ - break; - } - /* Point to the next page */ - pNext = pDirty->pPrevHot; /* Not a bug: Reverse link */ - if( (pDirty->flags & PAGE_DONT_WRITE) == 0 ){ - rc = unqliteOsWrite(pPager->pfd,pDirty->zData,pPager->iPageSize,pDirty->pgno * pPager->iPageSize); - if( rc != UNQLITE_OK ){ - break; - } - } - /* Remove stale flags */ - pDirty->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY); - /* Unlink from the list of dirty pages */ - if( pDirty->pDirtyPrev ){ - pDirty->pDirtyPrev->pDirtyNext = pDirty->pDirtyNext; - }else{ - pPager->pDirty = pDirty->pDirtyNext; - } - if( pDirty->pDirtyNext ){ - pDirty->pDirtyNext->pDirtyPrev = pDirty->pDirtyPrev; - }else{ - pPager->pFirstDirty = pDirty->pDirtyPrev; - } - /* Discard */ - pager_unlink_page(pPager,pDirty); - /* Release the page */ - pager_release_page(pPager,pDirty); - /* Next hot page */ - pDirty = pNext; - } - return rc; -} -/* - * Commit a transaction: Phase one. - */ -static int pager_commit_phase1(Pager *pPager) -{ - int get_excl = 0; - Page *pDirty; - int rc; - /* If no database changes have been made, return early. */ - if( pPager->iState < PAGER_WRITER_CACHEMOD ){ - return UNQLITE_OK; - } - if( pPager->is_mem ){ - /* An in-memory database */ - return UNQLITE_OK; - } - if( pPager->is_rdonly ){ - /* Read-Only DB */ - unqliteGenError(pPager->pDb,"Read-Only database"); - return UNQLITE_READ_ONLY; - } - /* Finalize the journal file */ - rc = unqliteFinalizeJournal(pPager,&get_excl,1); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Get the dirty pages */ - pDirty = pager_get_dirty_pages(pPager); - if( get_excl ){ - /* Wait one last time for the exclusive lock */ - rc = pager_wait_on_lock(pPager,EXCLUSIVE_LOCK); - if( rc != UNQLITE_OK ){ - unqliteGenError(pPager->pDb,"Cannot obtain an Exclusive lock on the target database"); - return rc; - } - } - if( pPager->iFlags & PAGER_CTRL_DIRTY_COMMIT ){ - /* Synce the database first if a dirty commit have been applied */ - unqliteOsSync(pPager->pfd,UNQLITE_SYNC_NORMAL); - } - /* Write the dirty pages */ - rc = pager_write_dirty_pages(pPager,pDirty); - if( rc != UNQLITE_OK ){ - /* Rollback your DB */ - pPager->iFlags |= PAGER_CTRL_COMMIT_ERR; - pPager->pFirstDirty = pDirty; - unqliteGenError(pPager->pDb,"IO error while writing dirty pages, rollback your database"); - return rc; - } - /* If the file on disk is not the same size as the database image, - * then use unqliteOsTruncate to grow or shrink the file here. - */ - if( pPager->dbSize != pPager->dbOrigSize ){ - unqliteOsTruncate(pPager->pfd,pPager->iPageSize * pPager->dbSize); - } - /* Sync the database file */ - unqliteOsSync(pPager->pfd,UNQLITE_SYNC_FULL); - /* Remove stale flags */ - pPager->iJournalOfft = 0; - pPager->nRec = 0; - return UNQLITE_OK; -} -/* - * Commit a transaction: Phase two. - */ -static int pager_commit_phase2(Pager *pPager) -{ - if( !pPager->is_mem ){ - if( pPager->iState == PAGER_OPEN ){ - return UNQLITE_OK; - } - if( pPager->iState != PAGER_READER ){ - if( !pPager->no_jrnl ){ - /* Finally, unlink the journal file */ - unqliteOsDelete(pPager->pVfs,pPager->zJournal,1); - } - /* Downgrade to shraed lock */ - pager_unlock_db(pPager,SHARED_LOCK); - pPager->iState = PAGER_READER; - if( pPager->pVec ){ - unqliteBitvecDestroy(pPager->pVec); - pPager->pVec = 0; - } - } - } - return UNQLITE_OK; -} -/* - * Perform a dirty commit. - */ -static int pager_dirty_commit(Pager *pPager) -{ - int get_excl = 0; - Page *pHot; - int rc; - /* Finalize the journal file without closing it */ - rc = unqliteFinalizeJournal(pPager,&get_excl,0); - if( rc != UNQLITE_OK ){ - /* It's not a fatal error if something goes wrong here since - * its not the final commit. - */ - return UNQLITE_OK; - } - /* Point to the list of hot pages */ - pHot = pager_get_hot_pages(pPager); - if( pHot == 0 ){ - return UNQLITE_OK; - } - if( get_excl ){ - /* Wait one last time for the exclusive lock */ - rc = pager_wait_on_lock(pPager,EXCLUSIVE_LOCK); - if( rc != UNQLITE_OK ){ - /* Not so fatal, will try another time */ - return UNQLITE_OK; - } - } - /* Tell that a dirty commit happen */ - pPager->iFlags |= PAGER_CTRL_DIRTY_COMMIT; - /* Write the hot pages now */ - rc = pager_write_hot_dirty_pages(pPager,pHot); - if( rc != UNQLITE_OK ){ - pPager->iFlags |= PAGER_CTRL_COMMIT_ERR; - unqliteGenError(pPager->pDb,"IO error while writing hot dirty pages, rollback your database"); - return rc; - } - pPager->pFirstHot = pPager->pHotDirty = 0; - pPager->nHot = 0; - /* No need to sync the database file here, since the journal is already - * open here and this is not the final commit. - */ - return UNQLITE_OK; -} -/* -** Commit a transaction and sync the database file for the pager pPager. -** -** This routine ensures that: -** -** * the journal is synced, -** * all dirty pages are written to the database file, -** * the database file is truncated (if required), and -** * the database file synced. -** * the journal file is deleted. -*/ -UNQLITE_PRIVATE int unqlitePagerCommit(Pager *pPager) -{ - int rc; - /* Commit: Phase One */ - rc = pager_commit_phase1(pPager); - if( rc != UNQLITE_OK ){ - goto fail; - } - /* Commit: Phase Two */ - rc = pager_commit_phase2(pPager); - if( rc != UNQLITE_OK ){ - goto fail; - } - /* Remove stale flags */ - pPager->iFlags &= ~PAGER_CTRL_COMMIT_ERR; - /* All done */ - return UNQLITE_OK; -fail: - /* Disable the auto-commit flag */ - pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT; - return rc; -} -/* - * Reset the pager to its initial state. This is caused by - * a rollback operation. - */ -static int pager_reset_state(Pager *pPager,int bResetKvEngine) -{ - unqlite_kv_engine *pEngine = pPager->pEngine; - Page *pNext,*pPtr = pPager->pAll; - const unqlite_kv_io *pIo; - int rc; - /* Remove stale flags */ - pPager->iFlags &= ~(PAGER_CTRL_COMMIT_ERR|PAGER_CTRL_DIRTY_COMMIT); - pPager->iJournalOfft = 0; - pPager->nRec = 0; - /* Database original size */ - pPager->dbSize = pPager->dbOrigSize; - /* Discard all in-memory pages */ - for(;;){ - if( pPtr == 0 ){ - break; - } - pNext = pPtr->pNext; /* Reverse link */ - /* Remove stale flags */ - pPtr->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY); - /* Release the page */ - pager_release_page(pPager,pPtr); - /* Point to the next page */ - pPtr = pNext; - } - pPager->pAll = 0; - pPager->nPage = 0; - pPager->pDirty = pPager->pFirstDirty = 0; - pPager->pHotDirty = pPager->pFirstHot = 0; - pPager->nHot = 0; - if( pPager->apHash ){ - /* Zero the table */ - SyZero((void *)pPager->apHash,sizeof(Page *) * pPager->nSize); - } - if( pPager->pVec ){ - unqliteBitvecDestroy(pPager->pVec); - pPager->pVec = 0; - } - /* Switch back to shared lock */ - pager_unlock_db(pPager,SHARED_LOCK); - pPager->iState = PAGER_READER; - if( bResetKvEngine ){ - /* Reset the underlying KV engine */ - pIo = pEngine->pIo; - if( pIo->pMethods->xRelease ){ - /* Call the release callback */ - pIo->pMethods->xRelease(pEngine); - } - /* Zero the structure */ - SyZero(pEngine,(sxu32)pIo->pMethods->szKv); - /* Fill in */ - pEngine->pIo = pIo; - if( pIo->pMethods->xInit ){ - /* Call the init method */ - rc = pIo->pMethods->xInit(pEngine,pPager->iPageSize); - if( rc != UNQLITE_OK ){ - return rc; - } - } - if( pIo->pMethods->xOpen ){ - /* Call the xOpen method */ - rc = pIo->pMethods->xOpen(pEngine,pPager->dbSize); - if( rc != UNQLITE_OK ){ - return rc; - } - } - } - /* All done */ - return UNQLITE_OK; -} -/* -** If a write transaction is open, then all changes made within the -** transaction are reverted and the current write-transaction is closed. -** The pager falls back to PAGER_READER state if successful. -** -** Otherwise, in rollback mode, this function performs two functions: -** -** 1) It rolls back the journal file, restoring all database file and -** in-memory cache pages to the state they were in when the transaction -** was opened, and -** -** 2) It finalizes the journal file, so that it is not used for hot -** rollback at any point in the future (i.e. deletion). -** -** Finalization of the journal file (task 2) is only performed if the -** rollback is successful. -** -*/ -UNQLITE_PRIVATE int unqlitePagerRollback(Pager *pPager,int bResetKvEngine) -{ - int rc = UNQLITE_OK; - if( pPager->iState < PAGER_WRITER_LOCKED ){ - /* A write transaction must be opened */ - return UNQLITE_OK; - } - if( pPager->is_mem ){ - /* As of this release 1.1.6: Transactions are not supported for in-memory databases */ - return UNQLITE_OK; - } - if( pPager->is_rdonly ){ - /* Read-Only DB */ - unqliteGenError(pPager->pDb,"Read-Only database"); - return UNQLITE_READ_ONLY; - } - if( pPager->iState >= PAGER_WRITER_CACHEMOD ){ - if( !pPager->no_jrnl ){ - /* Close any outstanding joural file */ - if( pPager->pjfd ){ - /* Sync the journal file */ - unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL); - } - unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd); - pPager->pjfd = 0; - if( pPager->iFlags & (PAGER_CTRL_COMMIT_ERR|PAGER_CTRL_DIRTY_COMMIT) ){ - /* Perform the rollback */ - rc = pager_journal_rollback(pPager,0); - if( rc != UNQLITE_OK ){ - /* Set the auto-commit flag */ - pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT; - return rc; - } - } - } - /* Unlink the journal file */ - unqliteOsDelete(pPager->pVfs,pPager->zJournal,1); - /* Reset the pager state */ - rc = pager_reset_state(pPager,bResetKvEngine); - if( rc != UNQLITE_OK ){ - /* Mostly an unlikely scenario */ - pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT; /* Set the auto-commit flag */ - unqliteGenError(pPager->pDb,"Error while reseting pager to its initial state"); - return rc; - } - }else{ - /* Downgrade to shared lock */ - pager_unlock_db(pPager,SHARED_LOCK); - pPager->iState = PAGER_READER; - } - return UNQLITE_OK; -} -/* - * Mark a data page as non writeable. - */ -static int unqlitePagerDontWrite(unqlite_page *pMyPage) -{ - Page *pPage = (Page *)pMyPage; - if( pPage->pgno > 0 /* Page 0 is always writeable */ ){ - pPage->flags |= PAGE_DONT_WRITE; - } - return UNQLITE_OK; -} -/* -** Mark a data page as writeable. This routine must be called before -** making changes to a page. The caller must check the return value -** of this function and be careful not to change any page data unless -** this routine returns UNQLITE_OK. -*/ -static int unqlitePageWrite(unqlite_page *pMyPage) -{ - Page *pPage = (Page *)pMyPage; - Pager *pPager = pPage->pPager; - int rc; - /* Begin the write transaction */ - rc = unqlitePagerBegin(pPager); - if( rc != UNQLITE_OK ){ - return rc; - } - if( pPager->iState == PAGER_WRITER_LOCKED ){ - /* The journal file needs to be opened. Higher level routines have already - ** obtained the necessary locks to begin the write-transaction, but the - ** rollback journal might not yet be open. Open it now if this is the case. - */ - rc = unqliteOpenJournal(pPager); - if( rc != UNQLITE_OK ){ - return rc; - } - } - if( pPager->nHot > 127 ){ - /* Write hot dirty pages */ - rc = pager_dirty_commit(pPager); - if( rc != UNQLITE_OK ){ - /* A rollback must be done */ - unqliteGenError(pPager->pDb,"Please perform a rollback"); - return rc; - } - } - /* Write the page to the journal file */ - rc = page_write(pPager,pPage); - return rc; -} -/* -** Acquire a reference to page number pgno in pager pPager (a page -** reference has type unqlite_page*). If the requested reference is -** successfully obtained, it is copied to *ppPage and UNQLITE_OK returned. -** -** If the requested page is already in the cache, it is returned. -** Otherwise, a new page object is allocated and populated with data -** read from the database file. -*/ -static int unqlitePagerAcquire( - Pager *pPager, /* The pager open on the database file */ - pgno pgno, /* Page number to fetch */ - unqlite_page **ppPage, /* OUT: Acquired page */ - int fetchOnly, /* Cache lookup only */ - int noContent /* Do not bother reading content from disk if true */ -) -{ - Page *pPage; - int rc; - /* Acquire a shared lock (if not yet done) on the database and rollback any hot-journal if present */ - rc = pager_shared_lock(pPager); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Fetch the page from the cache */ - pPage = pager_fetch_page(pPager,pgno); - if( fetchOnly ){ - if( ppPage ){ - *ppPage = (unqlite_page *)pPage; - } - return pPage ? UNQLITE_OK : UNQLITE_NOTFOUND; - } - if( pPage == 0 ){ - /* Allocate a new page */ - pPage = pager_alloc_page(pPager,pgno); - if( pPage == 0 ){ - unqliteGenOutofMem(pPager->pDb); - return UNQLITE_NOMEM; - } - /* Read page contents */ - rc = pager_get_page_contents(pPager,pPage,noContent); - if( rc != UNQLITE_OK ){ - SyMemBackendPoolFree(pPager->pAllocator,pPage); - return rc; - } - /* Link the page */ - pager_link_page(pPager,pPage); - }else{ - if( ppPage ){ - page_ref(pPage); - } - } - /* All done, page is loaded in memeory */ - if( ppPage ){ - *ppPage = (unqlite_page *)pPage; - } - return UNQLITE_OK; -} -/* - * Return true if we are dealing with an in-memory database. - */ -static int unqliteInMemory(const char *zFilename) -{ - sxu32 n; - if( SX_EMPTY_STR(zFilename) ){ - /* NULL or the empty string means an in-memory database */ - return TRUE; - } - n = SyStrlen(zFilename); - if( n == sizeof(":mem:") - 1 && - SyStrnicmp(zFilename,":mem:",sizeof(":mem:") - 1) == 0 ){ - return TRUE; - } - if( n == sizeof(":memory:") - 1 && - SyStrnicmp(zFilename,":memory:",sizeof(":memory:") - 1) == 0 ){ - return TRUE; - } - return FALSE; -} -/* - * Allocate a new KV cursor. - */ -UNQLITE_PRIVATE int unqliteInitCursor(unqlite *pDb,unqlite_kv_cursor **ppOut) -{ - unqlite_kv_methods *pMethods; - unqlite_kv_cursor *pCur; - sxu32 nByte; - /* Storage engine methods */ - pMethods = pDb->sDB.pPager->pEngine->pIo->pMethods; - if( pMethods->szCursor < 1 ){ - /* Implementation does not supprt cursors */ - unqliteGenErrorFormat(pDb,"Storage engine '%s' does not support cursors",pMethods->zName); - return UNQLITE_NOTIMPLEMENTED; - } - nByte = pMethods->szCursor; - if( nByte < sizeof(unqlite_kv_cursor) ){ - nByte += sizeof(unqlite_kv_cursor); - } - pCur = (unqlite_kv_cursor *)SyMemBackendPoolAlloc(&pDb->sMem,nByte); - if( pCur == 0 ){ - unqliteGenOutofMem(pDb); - return UNQLITE_NOMEM; - } - /* Zero the structure */ - SyZero(pCur,nByte); - /* Save the cursor */ - pCur->pStore = pDb->sDB.pPager->pEngine; - /* Invoke the initialization callback if any */ - if( pMethods->xCursorInit ){ - pMethods->xCursorInit(pCur); - } - /* All done */ - *ppOut = pCur; - return UNQLITE_OK; -} -/* - * Release a cursor. - */ -UNQLITE_PRIVATE int unqliteReleaseCursor(unqlite *pDb,unqlite_kv_cursor *pCur) -{ - unqlite_kv_methods *pMethods; - /* Storage engine methods */ - pMethods = pDb->sDB.pPager->pEngine->pIo->pMethods; - /* Invoke the release callback if available */ - if( pMethods->xCursorRelease ){ - pMethods->xCursorRelease(pCur); - } - /* Finally, free the whole instance */ - SyMemBackendPoolFree(&pDb->sMem,pCur); - return UNQLITE_OK; -} -/* - * Release the underlying KV storage engine and invoke - * its associated callbacks if available. - */ -static void pager_release_kv_engine(Pager *pPager) -{ - unqlite_kv_engine *pEngine = pPager->pEngine; - unqlite_db *pStorage = &pPager->pDb->sDB; - if( pStorage->pCursor ){ - /* Release the associated cursor */ - unqliteReleaseCursor(pPager->pDb,pStorage->pCursor); - pStorage->pCursor = 0; - } - if( pEngine->pIo->pMethods->xRelease ){ - pEngine->pIo->pMethods->xRelease(pEngine); - } - /* Release the whole instance */ - SyMemBackendFree(&pPager->pDb->sMem,(void *)pEngine->pIo); - SyMemBackendFree(&pPager->pDb->sMem,(void *)pEngine); - pPager->pEngine = 0; -} -/* Forward declaration */ -static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_kv_io *pIo); -/* - * Allocate, initialize and register a new KV storage engine - * within this database instance. - */ -UNQLITE_PRIVATE int unqlitePagerRegisterKvEngine(Pager *pPager,unqlite_kv_methods *pMethods) -{ - unqlite_db *pStorage = &pPager->pDb->sDB; - unqlite *pDb = pPager->pDb; - unqlite_kv_engine *pEngine; - unqlite_kv_io *pIo; - sxu32 nByte; - int rc; - if( pPager->pEngine ){ - if( pMethods == pPager->pEngine->pIo->pMethods ){ - /* Ticket 1432: Same implementation */ - return UNQLITE_OK; - } - /* Release the old KV engine */ - pager_release_kv_engine(pPager); - } - /* Allocate a new KV engine instance */ - nByte = (sxu32)pMethods->szKv; - pEngine = (unqlite_kv_engine *)SyMemBackendAlloc(&pDb->sMem,nByte); - if( pEngine == 0 ){ - unqliteGenOutofMem(pDb); - return UNQLITE_NOMEM; - } - pIo = (unqlite_kv_io *)SyMemBackendAlloc(&pDb->sMem,sizeof(unqlite_kv_io)); - if( pIo == 0 ){ - SyMemBackendFree(&pDb->sMem,pEngine); - unqliteGenOutofMem(pDb); - return UNQLITE_NOMEM; - } - /* Zero the structure */ - SyZero(pIo,sizeof(unqlite_io_methods)); - SyZero(pEngine,nByte); - /* Populate the IO structure */ - pager_kv_io_init(pPager,pMethods,pIo); - pEngine->pIo = pIo; - /* Invoke the init callback if avaialble */ - if( pMethods->xInit ){ - rc = pMethods->xInit(pEngine,unqliteGetPageSize()); - if( rc != UNQLITE_OK ){ - unqliteGenErrorFormat(pDb, - "xInit() method of the underlying KV engine '%z' failed",&pPager->sKv); - goto fail; - } - pEngine->pIo = pIo; - } - pPager->pEngine = pEngine; - /* Allocate a new cursor */ - rc = unqliteInitCursor(pDb,&pStorage->pCursor); - if( rc != UNQLITE_OK ){ - goto fail; - } - return UNQLITE_OK; -fail: - SyMemBackendFree(&pDb->sMem,pEngine); - SyMemBackendFree(&pDb->sMem,pIo); - return rc; -} -/* - * Return the underlying KV storage engine instance. - */ -UNQLITE_PRIVATE unqlite_kv_engine * unqlitePagerGetKvEngine(unqlite *pDb) -{ - return pDb->sDB.pPager->pEngine; -} -/* -* Allocate and initialize a new Pager object. The pager should -* eventually be freed by passing it to unqlitePagerClose(). -* -* The zFilename argument is the path to the database file to open. -* If zFilename is NULL or ":memory:" then all information is held -* in cache. It is never written to disk. This can be used to implement -* an in-memory database. -*/ -UNQLITE_PRIVATE int unqlitePagerOpen( - unqlite_vfs *pVfs, /* The virtual file system to use */ - unqlite *pDb, /* Database handle */ - const char *zFilename, /* Name of the database file to open */ - unsigned int iFlags /* flags controlling this file */ - ) -{ - unqlite_kv_methods *pMethods = 0; - int is_mem,rd_only,no_jrnl; - Pager *pPager; - sxu32 nByte; - sxu32 nLen; - int rc; - - /* Select the appropriate KV storage subsytem */ - if( (iFlags & UNQLITE_OPEN_IN_MEMORY) || unqliteInMemory(zFilename) ){ - /* An in-memory database, record that */ - pMethods = unqliteFindKVStore("mem",sizeof("mem") - 1); /* Always available */ - iFlags |= UNQLITE_OPEN_IN_MEMORY; - }else{ - /* Install the default key value storage subsystem [i.e. Linear Hash] */ - pMethods = unqliteFindKVStore("hash",sizeof("hash")-1); - if( pMethods == 0 ){ - /* Use the b+tree storage backend if the linear hash storage is not available */ - pMethods = unqliteFindKVStore("btree",sizeof("btree")-1); - } - } - if( pMethods == 0 ){ - /* Can't happen */ - unqliteGenError(pDb,"Cannot install a default Key/Value storage engine"); - return UNQLITE_NOTIMPLEMENTED; - } - is_mem = (iFlags & UNQLITE_OPEN_IN_MEMORY) != 0; - rd_only = (iFlags & UNQLITE_OPEN_READONLY) != 0; - no_jrnl = (iFlags & UNQLITE_OPEN_OMIT_JOURNALING) != 0; - rc = UNQLITE_OK; - if( is_mem ){ - /* Omit journaling for in-memory database */ - no_jrnl = 1; - } - /* Total number of bytes to allocate */ - nByte = sizeof(Pager); - nLen = 0; - if( !is_mem ){ - nLen = SyStrlen(zFilename); - nByte += pVfs->mxPathname + nLen + sizeof(char) /* null termniator */; - } - /* Allocate */ - pPager = (Pager *)SyMemBackendAlloc(&pDb->sMem,nByte); - if( pPager == 0 ){ - return UNQLITE_NOMEM; - } - /* Zero the structure */ - SyZero(pPager,nByte); - /* Fill-in the structure */ - pPager->pAllocator = &pDb->sMem; - pPager->pDb = pDb; - pDb->sDB.pPager = pPager; - /* Allocate page table */ - pPager->nSize = 128; /* Must be a power of two */ - nByte = pPager->nSize * sizeof(Page *); - pPager->apHash = (Page **)SyMemBackendAlloc(pPager->pAllocator,nByte); - if( pPager->apHash == 0 ){ - rc = UNQLITE_NOMEM; - goto fail; - } - SyZero(pPager->apHash,nByte); - pPager->is_mem = is_mem; - pPager->no_jrnl = no_jrnl; - pPager->is_rdonly = rd_only; - pPager->iOpenFlags = iFlags; - pPager->pVfs = pVfs; - SyRandomnessInit(&pPager->sPrng,0,0); - SyRandomness(&pPager->sPrng,(void *)&pPager->cksumInit,sizeof(sxu32)); - /* Unlimited cache size */ - pPager->nCacheMax = SXU32_HIGH; - /* Copy filename and journal name */ - if( !is_mem ){ - pPager->zFilename = (char *)&pPager[1]; - rc = UNQLITE_OK; - if( pVfs->xFullPathname ){ - rc = pVfs->xFullPathname(pVfs,zFilename,pVfs->mxPathname + nLen,pPager->zFilename); - } - if( rc != UNQLITE_OK ){ - /* Simple filename copy */ - SyMemcpy(zFilename,pPager->zFilename,nLen); - pPager->zFilename[nLen] = 0; - rc = UNQLITE_OK; - }else{ - nLen = SyStrlen(pPager->zFilename); - } - pPager->zJournal = (char *) SyMemBackendAlloc(pPager->pAllocator,nLen + sizeof(UNQLITE_JOURNAL_FILE_SUFFIX) + sizeof(char)); - if( pPager->zJournal == 0 ){ - rc = UNQLITE_NOMEM; - goto fail; - } - /* Copy filename */ - SyMemcpy(pPager->zFilename,pPager->zJournal,nLen); - /* Copy journal suffix */ - SyMemcpy(UNQLITE_JOURNAL_FILE_SUFFIX,&pPager->zJournal[nLen],sizeof(UNQLITE_JOURNAL_FILE_SUFFIX)-1); - /* Append the nul terminator to the journal path */ - pPager->zJournal[nLen + ( sizeof(UNQLITE_JOURNAL_FILE_SUFFIX) - 1)] = 0; - } - /* Finally, register the selected KV engine */ - rc = unqlitePagerRegisterKvEngine(pPager,pMethods); - if( rc != UNQLITE_OK ){ - goto fail; - } - /* Set the pager state */ - if( pPager->is_mem ){ - pPager->iState = PAGER_WRITER_FINISHED; - pPager->iLock = EXCLUSIVE_LOCK; - }else{ - pPager->iState = PAGER_OPEN; - pPager->iLock = NO_LOCK; - } - /* All done, ready for processing */ - return UNQLITE_OK; -fail: - SyMemBackendFree(&pDb->sMem,pPager); - return rc; -} -/* - * Set a cache limit. Note that, this is a simple hint, the pager is not - * forced to honor this limit. - */ -UNQLITE_PRIVATE int unqlitePagerSetCachesize(Pager *pPager,int mxPage) -{ - if( mxPage < 256 ){ - return UNQLITE_INVALID; - } - pPager->nCacheMax = mxPage; - return UNQLITE_OK; -} -/* - * Shutdown the page cache. Free all memory and close the database file. - */ -UNQLITE_PRIVATE int unqlitePagerClose(Pager *pPager) -{ - /* Release the KV engine */ - pager_release_kv_engine(pPager); - if( pPager->iOpenFlags & UNQLITE_OPEN_MMAP ){ - const jx9_vfs *pVfs = jx9ExportBuiltinVfs(); - if( pVfs && pVfs->xUnmap && pPager->pMmap ){ - pVfs->xUnmap(pPager->pMmap,pPager->dbByteSize); - } - } - if( !pPager->is_mem && pPager->iState > PAGER_OPEN ){ - /* Release all lock on this database handle */ - pager_unlock_db(pPager,NO_LOCK); - /* Close the file */ - unqliteOsCloseFree(pPager->pAllocator,pPager->pfd); - } - if( pPager->pVec ){ - unqliteBitvecDestroy(pPager->pVec); - pPager->pVec = 0; - } - return UNQLITE_OK; -} -/* - * Generate a random string. - */ -UNQLITE_PRIVATE void unqlitePagerRandomString(Pager *pPager,char *zBuf,sxu32 nLen) -{ - static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */ - sxu32 i; - /* Generate a binary string first */ - SyRandomness(&pPager->sPrng,zBuf,nLen); - /* Turn the binary string into english based alphabet */ - for( i = 0 ; i < nLen ; ++i ){ - zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)]; - } -} -/* - * Generate a random number. - */ -UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager) -{ - sxu32 iNum; - SyRandomness(&pPager->sPrng,(void *)&iNum,sizeof(iNum)); - return iNum; -} -/* Exported KV IO Methods */ -/* - * Refer to [unqlitePagerAcquire()] - */ -static int unqliteKvIoPageGet(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage) -{ - int rc; - rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,0,0); - return rc; -} -/* - * Refer to [unqlitePagerAcquire()] - */ -static int unqliteKvIoPageLookup(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage) -{ - int rc; - rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,1,0); - return rc; -} -/* - * Refer to [unqlitePagerAcquire()] - */ -static int unqliteKvIoNewPage(unqlite_kv_handle pHandle,unqlite_page **ppPage) -{ - Pager *pPager = (Pager *)pHandle; - int rc; - /* - * Acquire a reader-lock first so that pPager->dbSize get initialized. - */ - rc = pager_shared_lock(pPager); - if( rc == UNQLITE_OK ){ - rc = unqlitePagerAcquire(pPager,pPager->dbSize == 0 ? /* Page 0 is reserved */ 1 : pPager->dbSize ,ppPage,0,0); - } - return rc; -} -/* - * Refer to [unqlitePageWrite()] - */ -static int unqliteKvIopageWrite(unqlite_page *pPage) -{ - int rc; - if( pPage == 0 ){ - /* TICKET 1433-0348 */ - return UNQLITE_OK; - } - rc = unqlitePageWrite(pPage); - return rc; -} -/* - * Refer to [unqlitePagerDontWrite()] - */ -static int unqliteKvIoPageDontWrite(unqlite_page *pPage) -{ - int rc; - if( pPage == 0 ){ - /* TICKET 1433-0348 */ - return UNQLITE_OK; - } - rc = unqlitePagerDontWrite(pPage); - return rc; -} -/* - * Refer to [unqliteBitvecSet()] - */ -static int unqliteKvIoPageDontJournal(unqlite_page *pRaw) -{ - Page *pPage = (Page *)pRaw; - Pager *pPager; - if( pPage == 0 ){ - /* TICKET 1433-0348 */ - return UNQLITE_OK; - } - pPager = pPage->pPager; - if( pPager->iState >= PAGER_WRITER_LOCKED ){ - if( !pPager->no_jrnl && pPager->pVec && !unqliteBitvecTest(pPager->pVec,pPage->pgno) ){ - unqliteBitvecSet(pPager->pVec,pPage->pgno); - } - } - return UNQLITE_OK; -} -/* - * Do not add a page to the hot dirty list. - */ -static int unqliteKvIoPageDontMakeHot(unqlite_page *pRaw) -{ - Page *pPage = (Page *)pRaw; - - if( pPage == 0 ){ - /* TICKET 1433-0348 */ - return UNQLITE_OK; - } - pPage->flags |= PAGE_DONT_MAKE_HOT; - return UNQLITE_OK; -} -/* - * Refer to [page_ref()] - */ -static int unqliteKvIopage_ref(unqlite_page *pPage) -{ - if( pPage ){ - page_ref((Page *)pPage); - } - return UNQLITE_OK; -} -/* - * Refer to [page_unref()] - */ -static int unqliteKvIoPageUnRef(unqlite_page *pPage) -{ - if( pPage ){ - page_unref((Page *)pPage); - } - return UNQLITE_OK; -} -/* - * Refer to the declaration of the [Pager] structure - */ -static int unqliteKvIoReadOnly(unqlite_kv_handle pHandle) -{ - return ((Pager *)pHandle)->is_rdonly; -} -/* - * Refer to the declaration of the [Pager] structure - */ -static int unqliteKvIoPageSize(unqlite_kv_handle pHandle) -{ - return ((Pager *)pHandle)->iPageSize; -} -/* - * Refer to the declaration of the [Pager] structure - */ -static unsigned char * unqliteKvIoTempPage(unqlite_kv_handle pHandle) -{ - return ((Pager *)pHandle)->zTmpPage; -} -/* - * Set a page unpin callback. - * Refer to the declaration of the [Pager] structure - */ -static void unqliteKvIoPageUnpin(unqlite_kv_handle pHandle,void (*xPageUnpin)(void *)) -{ - Pager *pPager = (Pager *)pHandle; - pPager->xPageUnpin = xPageUnpin; -} -/* - * Set a page reload callback. - * Refer to the declaration of the [Pager] structure - */ -static void unqliteKvIoPageReload(unqlite_kv_handle pHandle,void (*xPageReload)(void *)) -{ - Pager *pPager = (Pager *)pHandle; - pPager->xPageReload = xPageReload; -} -/* - * Log an error. - * Refer to the declaration of the [Pager] structure - */ -static void unqliteKvIoErr(unqlite_kv_handle pHandle,const char *zErr) -{ - Pager *pPager = (Pager *)pHandle; - unqliteGenError(pPager->pDb,zErr); -} -/* - * Init an instance of the [unqlite_kv_io] structure. - */ -static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_kv_io *pIo) -{ - pIo->pHandle = pPager; - pIo->pMethods = pMethods; - - pIo->xGet = unqliteKvIoPageGet; - pIo->xLookup = unqliteKvIoPageLookup; - pIo->xNew = unqliteKvIoNewPage; - - pIo->xWrite = unqliteKvIopageWrite; - pIo->xDontWrite = unqliteKvIoPageDontWrite; - pIo->xDontJournal = unqliteKvIoPageDontJournal; - pIo->xDontMkHot = unqliteKvIoPageDontMakeHot; - - pIo->xPageRef = unqliteKvIopage_ref; - pIo->xPageUnref = unqliteKvIoPageUnRef; - - pIo->xPageSize = unqliteKvIoPageSize; - pIo->xReadOnly = unqliteKvIoReadOnly; - - pIo->xTmpPage = unqliteKvIoTempPage; - - pIo->xSetUnpin = unqliteKvIoPageUnpin; - pIo->xSetReload = unqliteKvIoPageReload; - - pIo->xErr = unqliteKvIoErr; - - return UNQLITE_OK; -} -/* - * ---------------------------------------------------------- - * File: unqlite_vm.c - * MD5: 358e7f319791c11c8262b81573d3a90e - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: unqlite_vm.c v1.0 Win7 2013-01-29 23:37 stable $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif -/* This file deals with low level stuff related to the unQLite Virtual Machine */ - -/* Record ID as a hash value */ -#define COL_RECORD_HASH(RID) (RID) -/* - * Fetch a record from a given collection. - */ -static unqlite_col_record * CollectionCacheFetchRecord( - unqlite_col *pCol, /* Target collection */ - jx9_int64 nId /* Unique record ID */ - ) -{ - unqlite_col_record *pEntry; - if( pCol->nRec < 1 ){ - /* Don't bother hashing */ - return 0; - } - pEntry = pCol->apRecord[COL_RECORD_HASH(nId) & (pCol->nRecSize - 1)]; - for(;;){ - if( pEntry == 0 ){ - break; - } - if( pEntry->nId == nId ){ - /* Record found */ - return pEntry; - } - /* Point to the next entry */ - pEntry = pEntry->pNextCol; - - } - /* No such record */ - return 0; -} -/* - * Install a freshly created record in a given collection. - */ -static int CollectionCacheInstallRecord( - unqlite_col *pCol, /* Target collection */ - jx9_int64 nId, /* Unique record ID */ - jx9_value *pValue /* JSON value */ - ) -{ - unqlite_col_record *pRecord; - sxu32 iBucket; - /* Fetch the record first */ - pRecord = CollectionCacheFetchRecord(pCol,nId); - if( pRecord ){ - /* Record already installed, overwrite its old value */ - jx9MemObjStore(pValue,&pRecord->sValue); - return UNQLITE_OK; - } - /* Allocate a new instance */ - pRecord = (unqlite_col_record *)SyMemBackendPoolAlloc(&pCol->pVm->sAlloc,sizeof(unqlite_col_record)); - if( pRecord == 0 ){ - return UNQLITE_NOMEM; - } - /* Zero the structure */ - SyZero(pRecord,sizeof(unqlite_col_record)); - /* Fill in the structure */ - jx9MemObjInit(pCol->pVm->pJx9Vm,&pRecord->sValue); - jx9MemObjStore(pValue,&pRecord->sValue); - pRecord->nId = nId; - pRecord->pCol = pCol; - /* Install in the corresponding bucket */ - iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1); - pRecord->pNextCol = pCol->apRecord[iBucket]; - if( pCol->apRecord[iBucket] ){ - pCol->apRecord[iBucket]->pPrevCol = pRecord; - } - pCol->apRecord[iBucket] = pRecord; - /* Link */ - MACRO_LD_PUSH(pCol->pList,pRecord); - pCol->nRec++; - if( (pCol->nRec >= pCol->nRecSize * 3) && pCol->nRec < 100000 ){ - /* Allocate a new larger table */ - sxu32 nNewSize = pCol->nRecSize << 1; - unqlite_col_record *pEntry; - unqlite_col_record **apNew; - sxu32 n; - - apNew = (unqlite_col_record **)SyMemBackendAlloc(&pCol->pVm->sAlloc, nNewSize * sizeof(unqlite_col_record *)); - if( apNew ){ - /* Zero the new table */ - SyZero((void *)apNew, nNewSize * sizeof(unqlite_col_record *)); - /* Rehash all entries */ - n = 0; - pEntry = pCol->pList; - for(;;){ - /* Loop one */ - if( n >= pCol->nRec ){ - break; - } - pEntry->pNext = pEntry->pPrevCol = 0; - /* Install in the new bucket */ - iBucket = COL_RECORD_HASH(pEntry->nId) & (nNewSize - 1); - pEntry->pNextCol = apNew[iBucket]; - if( apNew[iBucket] ){ - apNew[iBucket]->pPrevCol = pEntry; - } - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - n++; - } - /* Release the old table and reflect the change */ - SyMemBackendFree(&pCol->pVm->sAlloc,(void *)pCol->apRecord); - pCol->apRecord = apNew; - pCol->nRecSize = nNewSize; - } - } - /* All done */ - return UNQLITE_OK; -} -/* - * Remove a record from the collection table. - */ -UNQLITE_PRIVATE int unqliteCollectionCacheRemoveRecord( - unqlite_col *pCol, /* Target collection */ - jx9_int64 nId /* Unique record ID */ - ) -{ - unqlite_col_record *pRecord; - /* Fetch the record first */ - pRecord = CollectionCacheFetchRecord(pCol,nId); - if( pRecord == 0 ){ - /* No such record */ - return UNQLITE_NOTFOUND; - } - if( pRecord->pPrevCol ){ - pRecord->pPrevCol->pNextCol = pRecord->pNextCol; - }else{ - sxu32 iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1); - pCol->apRecord[iBucket] = pRecord->pNextCol; - } - if( pRecord->pNextCol ){ - pRecord->pNextCol->pPrevCol = pRecord->pPrevCol; - } - /* Unlink */ - MACRO_LD_REMOVE(pCol->pList,pRecord); - pCol->nRec--; - return UNQLITE_OK; -} -/* - * Discard a collection and its records. - */ -static int CollectionCacheRelease(unqlite_col *pCol) -{ - unqlite_col_record *pNext,*pRec = pCol->pList; - unqlite_vm *pVm = pCol->pVm; - sxu32 n; - /* Discard all records */ - for( n = 0 ; n < pCol->nRec ; ++n ){ - pNext = pRec->pNext; - jx9MemObjRelease(&pRec->sValue); - SyMemBackendPoolFree(&pVm->sAlloc,(void *)pRec); - /* Point to the next record */ - pRec = pNext; - } - SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord); - pCol->nRec = pCol->nRecSize = 0; - pCol->pList = 0; - return UNQLITE_OK; -} -/* - * Install a freshly created collection in the unqlite VM. - */ -static int unqliteVmInstallCollection( - unqlite_vm *pVm, /* Target VM */ - unqlite_col *pCol /* Collection to install */ - ) -{ - SyString *pName = &pCol->sName; - sxu32 iBucket; - /* Hash the collection name */ - pCol->nHash = SyBinHash((const void *)pName->zString,pName->nByte); - /* Install it in the corresponding bucket */ - iBucket = pCol->nHash & (pVm->iColSize - 1); - pCol->pNextCol = pVm->apCol[iBucket]; - if( pVm->apCol[iBucket] ){ - pVm->apCol[iBucket]->pPrevCol = pCol; - } - pVm->apCol[iBucket] = pCol; - /* Link to the list of active collections */ - MACRO_LD_PUSH(pVm->pCol,pCol); - pVm->iCol++; - if( (pVm->iCol >= pVm->iColSize * 4) && pVm->iCol < 10000 ){ - /* Grow the hashtable */ - sxu32 nNewSize = pVm->iColSize << 1; - unqlite_col *pEntry; - unqlite_col **apNew; - sxu32 n; - - apNew = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc, nNewSize * sizeof(unqlite_col *)); - if( apNew ){ - /* Zero the new table */ - SyZero((void *)apNew, nNewSize * sizeof(unqlite_col *)); - /* Rehash all entries */ - n = 0; - pEntry = pVm->pCol; - for(;;){ - /* Loop one */ - if( n >= pVm->iCol ){ - break; - } - pEntry->pNextCol = pEntry->pPrevCol = 0; - /* Install in the new bucket */ - iBucket = pEntry->nHash & (nNewSize - 1); - pEntry->pNextCol = apNew[iBucket]; - if( apNew[iBucket] ){ - apNew[iBucket]->pPrevCol = pEntry; - } - apNew[iBucket] = pEntry; - /* Point to the next entry */ - pEntry = pEntry->pNext; - n++; - } - /* Release the old table and reflect the change */ - SyMemBackendFree(&pVm->sAlloc,(void *)pVm->apCol); - pVm->apCol = apNew; - pVm->iColSize = nNewSize; - } - } - return UNQLITE_OK; -} -/* - * Fetch a collection from the target VM. - */ -static unqlite_col * unqliteVmFetchCollection( - unqlite_vm *pVm, /* Target VM */ - SyString *pName /* Lookup name */ - ) -{ - unqlite_col *pCol; - sxu32 nHash; - if( pVm->iCol < 1 ){ - /* Don't bother hashing */ - return 0; - } - nHash = SyBinHash((const void *)pName->zString,pName->nByte); - /* Perform the lookup */ - pCol = pVm->apCol[nHash & ( pVm->iColSize - 1)]; - for(;;){ - if( pCol == 0 ){ - break; - } - if( nHash == pCol->nHash && SyStringCmp(pName,&pCol->sName,SyMemcmp) == 0 ){ - /* Collection found */ - return pCol; - } - /* Point to the next entry */ - pCol = pCol->pNextCol; - } - /* No such collection */ - return 0; -} -/* - * Write and/or alter collection binary header. - */ -static int CollectionSetHeader( - unqlite_kv_engine *pEngine, /* Underlying KV storage engine */ - unqlite_col *pCol, /* Target collection */ - jx9_int64 iRec, /* Last record ID */ - jx9_int64 iTotal, /* Total number of records in this collection */ - jx9_value *pSchema /* Collection schema */ - ) -{ - SyBlob *pHeader = &pCol->sHeader; - unqlite_kv_methods *pMethods; - int iWrite = 0; - int rc; - if( pEngine == 0 ){ - /* Default storage engine */ - pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb); - } - pMethods = pEngine->pIo->pMethods; - if( SyBlobLength(pHeader) < 1 ){ - Sytm *pCreate = &pCol->sCreation; /* Creation time */ - unqlite_vfs *pVfs; - sxu32 iDos; - /* Magic number */ - rc = SyBlobAppendBig16(pHeader,UNQLITE_COLLECTION_MAGIC); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Initial record ID */ - rc = SyBlobAppendBig64(pHeader,0); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Total records in the collection */ - rc = SyBlobAppendBig64(pHeader,0); - if( rc != UNQLITE_OK ){ - return rc; - } - pVfs = (unqlite_vfs *)unqliteExportBuiltinVfs(); - /* Creation time of the collection */ - if( pVfs->xCurrentTime ){ - /* Get the creation time */ - pVfs->xCurrentTime(pVfs,pCreate); - }else{ - /* Zero the structure */ - SyZero(pCreate,sizeof(Sytm)); - } - /* Convert to DOS time */ - SyTimeFormatToDos(pCreate,&iDos); - rc = SyBlobAppendBig32(pHeader,iDos); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Offset to start writing collection schema */ - pCol->nSchemaOfft = SyBlobLength(pHeader); - iWrite = 1; - }else{ - unsigned char *zBinary = (unsigned char *)SyBlobData(pHeader); - /* Header update */ - if( iRec >= 0 ){ - /* Update record ID */ - SyBigEndianPack64(&zBinary[2/* Magic number*/],(sxu64)iRec); - iWrite = 1; - } - if( iTotal >= 0 ){ - /* Total records */ - SyBigEndianPack64(&zBinary[2/* Magic number*/+8/* Record ID*/],(sxu64)iTotal); - iWrite = 1; - } - if( pSchema ){ - /* Collection Schema */ - SyBlobTruncate(pHeader,pCol->nSchemaOfft); - /* Encode the schema to FastJson */ - rc = FastJsonEncode(pSchema,pHeader,0); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Copy the collection schema */ - jx9MemObjStore(pSchema,&pCol->sSchema); - iWrite = 1; - } - } - if( iWrite ){ - SyString *pId = &pCol->sName; - /* Reflect the disk and/or in-memory image */ - rc = pMethods->xReplace(pEngine, - (const void *)pId->zString,pId->nByte, - SyBlobData(pHeader),SyBlobLength(pHeader) - ); - if( rc != UNQLITE_OK ){ - unqliteGenErrorFormat(pCol->pVm->pDb, - "Cannot save collection '%z' header in the underlying storage engine", - pId - ); - return rc; - } - } - return UNQLITE_OK; -} -/* - * Load a binary collection from disk. - */ -static int CollectionLoadHeader(unqlite_col *pCol) -{ - SyBlob *pHeader = &pCol->sHeader; - unsigned char *zRaw,*zEnd; - sxu16 nMagic; - sxu32 iDos; - int rc; - SyBlobReset(pHeader); - /* Read the binary header */ - rc = unqlite_kv_cursor_data_callback(pCol->pCursor,unqliteDataConsumer,pHeader); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Perform a sanity check */ - if( SyBlobLength(pHeader) < (2 /* magic */ + 8 /* record_id */ + 8 /* total_records */+ 4 /* DOS creation time*/) ){ - return UNQLITE_CORRUPT; - } - zRaw = (unsigned char *)SyBlobData(pHeader); - zEnd = &zRaw[SyBlobLength(pHeader)]; - /* Extract the magic number */ - SyBigEndianUnpack16(zRaw,&nMagic); - if( nMagic != UNQLITE_COLLECTION_MAGIC ){ - return UNQLITE_CORRUPT; - } - zRaw += 2; /* sizeof(sxu16) */ - /* Extract the record ID */ - SyBigEndianUnpack64(zRaw,(sxu64 *)&pCol->nLastid); - zRaw += 8; /* sizeof(sxu64) */ - /* Total records in the collection */ - SyBigEndianUnpack64(zRaw,(sxu64 *)&pCol->nTotRec); - /* Extract the collection creation date (DOS) */ - zRaw += 8; /* sizeof(sxu64) */ - SyBigEndianUnpack32(zRaw,&iDos); - SyDosTimeFormat(iDos,&pCol->sCreation); - zRaw += 4; - /* Check for a collection schema */ - pCol->nSchemaOfft = (sxu32)(zRaw - (unsigned char *)SyBlobData(pHeader)); - if( zRaw < zEnd ){ - /* Decode the FastJson value */ - FastJsonDecode((const void *)zRaw,(sxu32)(zEnd-zRaw),&pCol->sSchema,0,0); - } - return UNQLITE_OK; -} -/* - * Load or create a binary collection. - */ -static int unqliteVmLoadCollection( - unqlite_vm *pVm, /* Target VM */ - const char *zName, /* Collection name */ - sxu32 nByte, /* zName length */ - int iFlag, /* Control flag */ - unqlite_col **ppOut /* OUT: in-memory collection */ - ) -{ - unqlite_kv_methods *pMethods; - unqlite_kv_engine *pEngine; - unqlite_kv_cursor *pCursor; - unqlite *pDb = pVm->pDb; - unqlite_col *pCol = 0; /* cc warning */ - int rc = SXERR_MEM; - char *zDup = 0; - /* Point to the underlying KV store */ - pEngine = unqlitePagerGetKvEngine(pVm->pDb); - pMethods = pEngine->pIo->pMethods; - /* Allocate a new cursor */ - rc = unqliteInitCursor(pDb,&pCursor); - if( rc != UNQLITE_OK ){ - return rc; - } - if( (iFlag & UNQLITE_VM_COLLECTION_CREATE) == 0 ){ - /* Seek to the desired location */ - rc = pMethods->xSeek(pCursor,(const void *)zName,(unqlite_int64)nByte,UNQLITE_CURSOR_MATCH_EXACT); - if( rc != UNQLITE_OK ){ - unqliteGenErrorFormat(pDb,"Collection '%.*s' not defined in the underlying database",nByte,zName); - unqliteReleaseCursor(pDb,pCursor); - return rc; - } - } - /* Allocate a new instance */ - pCol = (unqlite_col *)SyMemBackendPoolAlloc(&pVm->sAlloc,sizeof(unqlite_col)); - if( pCol == 0 ){ - unqliteGenOutofMem(pDb); - rc = UNQLITE_NOMEM; - goto fail; - } - SyZero(pCol,sizeof(unqlite_col)); - /* Fill in the structure */ - SyBlobInit(&pCol->sWorker,&pVm->sAlloc); - SyBlobInit(&pCol->sHeader,&pVm->sAlloc); - pCol->pVm = pVm; - pCol->pCursor = pCursor; - /* Duplicate collection name */ - zDup = SyMemBackendStrDup(&pVm->sAlloc,zName,nByte); - if( zDup == 0 ){ - unqliteGenOutofMem(pDb); - rc = UNQLITE_NOMEM; - goto fail; - } - pCol->nRecSize = 64; /* Must be a power of two */ - pCol->apRecord = (unqlite_col_record **)SyMemBackendAlloc(&pVm->sAlloc,pCol->nRecSize * sizeof(unqlite_col_record *)); - if( pCol->apRecord == 0 ){ - unqliteGenOutofMem(pDb); - rc = UNQLITE_NOMEM; - goto fail; - } - /* Zero the table */ - SyZero((void *)pCol->apRecord,pCol->nRecSize * sizeof(unqlite_col_record *)); - SyStringInitFromBuf(&pCol->sName,zDup,nByte); - jx9MemObjInit(pVm->pJx9Vm,&pCol->sSchema); - if( iFlag & UNQLITE_VM_COLLECTION_CREATE ){ - /* Create a new collection */ - if( pMethods->xReplace == 0 ){ - /* Read-only KV engine: Generate an error message and return */ - unqliteGenErrorFormat(pDb, - "Cannot create new collection '%z' due to a read-only Key/Value storage engine", - &pCol->sName - ); - rc = UNQLITE_ABORT; /* Abort VM execution */ - goto fail; - } - /* Write the collection header */ - rc = CollectionSetHeader(pEngine,pCol,0,0,0); - if( rc != UNQLITE_OK ){ - rc = UNQLITE_ABORT; /* Abort VM execution */ - goto fail; - } - }else{ - /* Read the collection header */ - rc = CollectionLoadHeader(pCol); - if( rc != UNQLITE_OK ){ - unqliteGenErrorFormat(pDb,"Corrupt collection '%z' header",&pCol->sName); - goto fail; - } - } - /* Finally install the collection */ - unqliteVmInstallCollection(pVm,pCol); - /* All done */ - if( ppOut ){ - *ppOut = pCol; - } - return UNQLITE_OK; -fail: - unqliteReleaseCursor(pDb,pCursor); - if( zDup ){ - SyMemBackendFree(&pVm->sAlloc,zDup); - } - if( pCol ){ - if( pCol->apRecord ){ - SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord); - } - SyBlobRelease(&pCol->sHeader); - SyBlobRelease(&pCol->sWorker); - jx9MemObjRelease(&pCol->sSchema); - SyMemBackendPoolFree(&pVm->sAlloc,pCol); - } - return rc; -} -/* - * Fetch a collection. - */ -UNQLITE_PRIVATE unqlite_col * unqliteCollectionFetch( - unqlite_vm *pVm, /* Target VM */ - SyString *pName, /* Lookup key */ - int iFlag /* Control flag */ - ) -{ - unqlite_col *pCol = 0; /* cc warning */ - int rc; - /* Check if the collection is already loaded in memory */ - pCol = unqliteVmFetchCollection(pVm,pName); - if( pCol ){ - /* Already loaded in memory*/ - return pCol; - } - if( (iFlag & UNQLITE_VM_AUTO_LOAD) == 0 ){ - return 0; - } - /* Ask the storage engine for the collection */ - rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,0,&pCol); - /* Return to the caller */ - return rc == UNQLITE_OK ? pCol : 0; -} -/* - * Return the unique ID of the last inserted record. - */ -UNQLITE_PRIVATE jx9_int64 unqliteCollectionLastRecordId(unqlite_col *pCol) -{ - return pCol->nLastid == 0 ? 0 : (pCol->nLastid - 1); -} -/* - * Return the current record ID. - */ -UNQLITE_PRIVATE jx9_int64 unqliteCollectionCurrentRecordId(unqlite_col *pCol) -{ - return pCol->nCurid; -} -/* - * Return the total number of records in a given collection. - */ -UNQLITE_PRIVATE jx9_int64 unqliteCollectionTotalRecords(unqlite_col *pCol) -{ - return pCol->nTotRec; -} -/* - * Reset the record cursor. - */ -UNQLITE_PRIVATE void unqliteCollectionResetRecordCursor(unqlite_col *pCol) -{ - pCol->nCurid = 0; -} -/* - * Fetch a record by its unique ID. - */ -UNQLITE_PRIVATE int unqliteCollectionFetchRecordById( - unqlite_col *pCol, /* Target collection */ - jx9_int64 nId, /* Unique record ID */ - jx9_value *pValue /* OUT: record value */ - ) -{ - SyBlob *pWorker = &pCol->sWorker; - unqlite_col_record *pRec; - int rc; - jx9_value_null(pValue); - /* Perform a cache lookup first */ - pRec = CollectionCacheFetchRecord(pCol,nId); - if( pRec ){ - /* Copy record value */ - jx9MemObjStore(&pRec->sValue,pValue); - return UNQLITE_OK; - } - /* Reset the working buffer */ - SyBlobReset(pWorker); - /* Generate the unique ID */ - SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,nId); - /* Reset the cursor */ - unqlite_kv_cursor_reset(pCol->pCursor); - /* Seek the cursor to the desired location */ - rc = unqlite_kv_cursor_seek(pCol->pCursor, - SyBlobData(pWorker),SyBlobLength(pWorker), - UNQLITE_CURSOR_MATCH_EXACT - ); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Consume the binary JSON */ - SyBlobReset(pWorker); - unqlite_kv_cursor_data_callback(pCol->pCursor,unqliteDataConsumer,pWorker); - if( SyBlobLength(pWorker) < 1 ){ - unqliteGenErrorFormat(pCol->pVm->pDb, - "Empty record '%qd'",nId - ); - jx9_value_null(pValue); - }else{ - /* Decode the binary JSON */ - rc = FastJsonDecode(SyBlobData(pWorker),SyBlobLength(pWorker),pValue,0,0); - if( rc == UNQLITE_OK ){ - /* Install the record in the cache */ - CollectionCacheInstallRecord(pCol,nId,pValue); - } - } - return rc; -} -/* - * Fetch the next record from a given collection. - */ -UNQLITE_PRIVATE int unqliteCollectionFetchNextRecord(unqlite_col *pCol,jx9_value *pValue) -{ - int rc; - for(;;){ - if( pCol->nCurid >= pCol->nLastid ){ - /* No more records, reset the record cursor ID */ - pCol->nCurid = 0; - /* Return to the caller */ - return SXERR_EOF; - } - rc = unqliteCollectionFetchRecordById(pCol,pCol->nCurid,pValue); - /* Increment the record ID */ - pCol->nCurid++; - /* Lookup result */ - if( rc == UNQLITE_OK || rc != UNQLITE_NOTFOUND ){ - break; - } - } - return rc; -} -/* - * Create a new collection. - */ -UNQLITE_PRIVATE int unqliteCreateCollection( - unqlite_vm *pVm, /* Target VM */ - SyString *pName /* Collection name */ - ) -{ - unqlite_col *pCol; - int rc; - /* Perform a lookup first */ - pCol = unqliteCollectionFetch(pVm,pName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - return UNQLITE_EXISTS; - } - /* Now, safely create the collection */ - rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,UNQLITE_VM_COLLECTION_CREATE,0); - return rc; -} -/* - * Set a schema (JSON object) for a given collection. - */ -UNQLITE_PRIVATE int unqliteCollectionSetSchema(unqlite_col *pCol,jx9_value *pValue) -{ - int rc; - if( !jx9_value_is_json_object(pValue) ){ - /* Must be a JSON object */ - return SXERR_INVALID; - } - rc = CollectionSetHeader(0,pCol,-1,-1,pValue); - return rc; -} -/* - * Perform a store operation on a given collection. - */ -static int CollectionStore( - unqlite_col *pCol, /* Target collection */ - jx9_value *pValue /* JSON value to be stored */ - ) -{ - SyBlob *pWorker = &pCol->sWorker; - unqlite_kv_methods *pMethods; - unqlite_kv_engine *pEngine; - sxu32 nKeyLen; - int rc; - /* Point to the underlying KV store */ - pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb); - pMethods = pEngine->pIo->pMethods; - if( pCol->nTotRec >= SXI64_HIGH ){ - /* Collection limit reached. No more records */ - unqliteGenErrorFormat(pCol->pVm->pDb, - "Collection '%z': Records limit reached", - &pCol->sName - ); - return UNQLITE_LIMIT; - } - if( pMethods->xReplace == 0 ){ - unqliteGenErrorFormat(pCol->pVm->pDb, - "Cannot store record into collection '%z' due to a read-only Key/Value storage engine", - &pCol->sName - ); - return UNQLITE_READ_ONLY; - } - /* Reset the working buffer */ - SyBlobReset(pWorker); - if( jx9_value_is_json_object(pValue) ){ - jx9_value sId; - /* If the given type is a JSON object, then add the special __id field */ - jx9MemObjInitFromInt(pCol->pVm->pJx9Vm,&sId,pCol->nLastid); - jx9_array_add_strkey_elem(pValue,"__id",&sId); - jx9MemObjRelease(&sId); - } - /* Prepare the unique ID for this record */ - SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,pCol->nLastid); - nKeyLen = SyBlobLength(pWorker); - if( nKeyLen < 1 ){ - unqliteGenOutofMem(pCol->pVm->pDb); - return UNQLITE_NOMEM; - } - /* Turn to FastJson */ - rc = FastJsonEncode(pValue,pWorker,0); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Finally perform the insertion */ - rc = pMethods->xReplace( - pEngine, - SyBlobData(pWorker),nKeyLen, - SyBlobDataAt(pWorker,nKeyLen),SyBlobLength(pWorker)-nKeyLen - ); - if( rc == UNQLITE_OK ){ - /* Save the value in the cache */ - CollectionCacheInstallRecord(pCol,pCol->nLastid,pValue); - /* Increment the unique __id */ - pCol->nLastid++; - pCol->nTotRec++; - /* Reflect the change */ - rc = CollectionSetHeader(0,pCol,pCol->nLastid,pCol->nTotRec,0); - } - if( rc != UNQLITE_OK ){ - unqliteGenErrorFormat(pCol->pVm->pDb, - "IO error while storing record into collection '%z'", - &pCol->sName - ); - return rc; - } - return UNQLITE_OK; -} -/* - * Array walker callback (Refer to jx9_array_walk()). - */ -static int CollectionRecordArrayWalker(jx9_value *pKey,jx9_value *pData,void *pUserData) -{ - unqlite_col *pCol = (unqlite_col *)pUserData; - int rc; - /* Perform the insertion */ - rc = CollectionStore(pCol,pData); - if( rc != UNQLITE_OK ){ - SXUNUSED(pKey); /* cc warning */ - } - return rc; -} -/* - * Perform a store operation on a given collection. - */ -UNQLITE_PRIVATE int unqliteCollectionPut(unqlite_col *pCol,jx9_value *pValue,int iFlag) -{ - int rc; - if( !jx9_value_is_json_object(pValue) && jx9_value_is_json_array(pValue) ){ - /* Iterate over the array and store its members in the collection */ - rc = jx9_array_walk(pValue,CollectionRecordArrayWalker,pCol); - SXUNUSED(iFlag); /* cc warning */ - }else{ - rc = CollectionStore(pCol,pValue); - } - return rc; -} -/* - * Drop a record from a given collection. - */ -UNQLITE_PRIVATE int unqliteCollectionDropRecord( - unqlite_col *pCol, /* Target collection */ - jx9_int64 nId, /* Unique ID of the record to be droped */ - int wr_header, /* True to alter collection header */ - int log_err /* True to log error */ - ) -{ - SyBlob *pWorker = &pCol->sWorker; - int rc; - /* Reset the working buffer */ - SyBlobReset(pWorker); - /* Prepare the unique ID for this record */ - SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,nId); - /* Reset the cursor */ - unqlite_kv_cursor_reset(pCol->pCursor); - /* Seek the cursor to the desired location */ - rc = unqlite_kv_cursor_seek(pCol->pCursor, - SyBlobData(pWorker),SyBlobLength(pWorker), - UNQLITE_CURSOR_MATCH_EXACT - ); - if( rc != UNQLITE_OK ){ - return rc; - } - /* Remove the record from the storage engine */ - rc = unqlite_kv_cursor_delete_entry(pCol->pCursor); - /* Finally, Remove the record from the cache */ - unqliteCollectionCacheRemoveRecord(pCol,nId); - if( rc == UNQLITE_OK ){ - pCol->nTotRec--; - if( wr_header ){ - /* Relect in the collection header */ - rc = CollectionSetHeader(0,pCol,-1,pCol->nTotRec,0); - } - }else if( rc == UNQLITE_NOTIMPLEMENTED ){ - if( log_err ){ - unqliteGenErrorFormat(pCol->pVm->pDb, - "Cannot delete record from collection '%z' due to a read-only Key/Value storage engine", - &pCol->sName - ); - } - } - return rc; -} -/* - * Drop a collection from the KV storage engine and the underlying - * unqlite VM. - */ -UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol) -{ - unqlite_vm *pVm = pCol->pVm; - jx9_int64 nId; - int rc; - /* Reset the cursor */ - unqlite_kv_cursor_reset(pCol->pCursor); - /* Seek the cursor to the desired location */ - rc = unqlite_kv_cursor_seek(pCol->pCursor, - SyStringData(&pCol->sName),SyStringLength(&pCol->sName), - UNQLITE_CURSOR_MATCH_EXACT - ); - if( rc == UNQLITE_OK ){ - /* Remove the record from the storage engine */ - rc = unqlite_kv_cursor_delete_entry(pCol->pCursor); - } - if( rc != UNQLITE_OK ){ - unqliteGenErrorFormat(pCol->pVm->pDb, - "Cannot remove collection '%z' due to a read-only Key/Value storage engine", - &pCol->sName - ); - return rc; - } - /* Drop collection records */ - for( nId = 0 ; nId < pCol->nLastid ; ++nId ){ - unqliteCollectionDropRecord(pCol,nId,0,0); - } - /* Cleanup */ - CollectionCacheRelease(pCol); - SyBlobRelease(&pCol->sHeader); - SyBlobRelease(&pCol->sWorker); - SyMemBackendFree(&pVm->sAlloc,(void *)SyStringData(&pCol->sName)); - unqliteReleaseCursor(pVm->pDb,pCol->pCursor); - /* Unlink */ - if( pCol->pPrevCol ){ - pCol->pPrevCol->pNextCol = pCol->pNextCol; - }else{ - sxu32 iBucket = pCol->nHash & (pVm->iColSize - 1); - pVm->apCol[iBucket] = pCol->pNextCol; - } - if( pCol->pNextCol ){ - pCol->pNextCol->pPrevCol = pCol->pPrevCol; - } - MACRO_LD_REMOVE(pVm->pCol,pCol); - pVm->iCol--; - SyMemBackendPoolFree(&pVm->sAlloc,pCol); - return UNQLITE_OK; -} -/* - * ---------------------------------------------------------- - * File: unqlite_jx9.c - * MD5: 8fddc15b667e85d7b5df5367132518fb - * ---------------------------------------------------------- - */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ - /* $SymiscID: unql_jx9.c v1.2 FreeBSD 2013-01-24 22:45 stable $ */ -#ifndef UNQLITE_AMALGAMATION -#include "unqliteInt.h" -#endif -/* - * This file implements UnQLite functions (db_exists(), db_create(), db_put(), db_get(), etc.) for the - * underlying Jx9 Virtual Machine. - */ -/* - * string db_version(void) - * Return the current version of the unQLite database engine. - * Parameter - * None - * Return - * unQLite version number (string). - */ -static int unqliteBuiltin_db_version(jx9_context *pCtx,int argc,jx9_value **argv) -{ - SXUNUSED(argc); /* cc warning */ - SXUNUSED(argv); - jx9_result_string(pCtx,UNQLITE_VERSION,(int)sizeof(UNQLITE_VERSION)-1); - return JX9_OK; -} -/* - * string db_errlog(void) - * Return the database error log. - * Parameter - * None - * Return - * Database error log (string). - */ -static int unqliteBuiltin_db_errlog(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_vm *pVm; - SyBlob *pErr; - - SXUNUSED(argc); /* cc warning */ - SXUNUSED(argv); - - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Point to the error log */ - pErr = &pVm->pDb->sErr; - /* Return the log */ - jx9_result_string(pCtx,(const char *)SyBlobData(pErr),(int)SyBlobLength(pErr)); - return JX9_OK; -} -/* - * string db_copyright(void) - * string db_credits(void) - * Return the unQLite database engine copyright notice. - * Parameter - * None - * Return - * Copyright notice. - */ -static int unqliteBuiltin_db_credits(jx9_context *pCtx,int argc,jx9_value **argv) -{ - SXUNUSED(argc); /* cc warning */ - SXUNUSED(argv); - jx9_result_string(pCtx,UNQLITE_COPYRIGHT,(int)sizeof(UNQLITE_COPYRIGHT)-1); - return JX9_OK; -} -/* - * string db_sig(void) - * Return the unQLite database engine unique signature. - * Parameter - * None - * Return - * unQLite signature. - */ -static int unqliteBuiltin_db_sig(jx9_context *pCtx,int argc,jx9_value **argv) -{ - SXUNUSED(argc); /* cc warning */ - SXUNUSED(argv); - jx9_result_string(pCtx,UNQLITE_IDENT,sizeof(UNQLITE_IDENT)-1); - return JX9_OK; -} -/* - * bool collection_exists(string $name) - * bool db_exits(string $name) - * Check if a given collection exists in the underlying database. - * Parameter - * name: Lookup name - * Return - * TRUE if the collection exits. FALSE otherwise. - */ -static int unqliteBuiltin_collection_exists(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Perform the lookup */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - /* Lookup result */ - jx9_result_bool(pCtx,pCol ? 1 : 0); - return JX9_OK; -} -/* - * bool collection_create(string $name) - * bool db_create(string $name) - * Create a new collection. - * Parameter - * name: Collection name - * Return - * TRUE if the collection was successfuly created. FALSE otherwise. - */ -static int unqliteBuiltin_collection_create(jx9_context *pCtx,int argc,jx9_value **argv) -{ - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - int rc; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Try to create the collection */ - rc = unqliteCreateCollection(pVm,&sName); - /* Return the result to the caller */ - jx9_result_bool(pCtx,rc == UNQLITE_OK ? 1 : 0); - return JX9_OK; -} -/* - * value db_fetch(string $col_name) - * value db_get(string $col_name) - * Fetch the current record from a given collection and advance - * the record cursor. - * Parameter - * col_name: Collection name - * Return - * Record content success. NULL on failure (No more records to retrieve). - */ -static int unqliteBuiltin_db_fetch_next(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - int rc; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name"); - /* Return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - /* Fetch the current record */ - jx9_value *pValue; - pValue = jx9_context_new_scalar(pCtx); - if( pValue == 0 ){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory"); - jx9_result_null(pCtx); - return JX9_OK; - }else{ - rc = unqliteCollectionFetchNextRecord(pCol,pValue); - if( rc == UNQLITE_OK ){ - jx9_result_value(pCtx,pValue); - /* pValue will be automatically released as soon we return from this function */ - }else{ - /* Return null */ - jx9_result_null(pCtx); - } - } - }else{ - /* No such collection, return null */ - jx9_result_null(pCtx); - } - return JX9_OK; -} -/* - * value db_fetch_by_id(string $col_name,int64 $record_id) - * value db_get_by_id(string $col_name,int64 $record_id) - * Fetch a record using its unique ID from a given collection. - * Parameter - * col_name: Collection name - * record_id: Record number (__id field of a JSON object) - * Return - * Record content success. NULL on failure (No such record). - */ -static int unqliteBuiltin_db_fetch_by_id(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - jx9_int64 nId; - int nByte; - int rc; - /* Extract collection name */ - if( argc < 2 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or record ID"); - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Extract the record ID */ - nId = jx9_value_to_int(argv[1]); - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - /* Fetch the desired record */ - jx9_value *pValue; - pValue = jx9_context_new_scalar(pCtx); - if( pValue == 0 ){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory"); - jx9_result_null(pCtx); - return JX9_OK; - }else{ - rc = unqliteCollectionFetchRecordById(pCol,nId,pValue); - if( rc == UNQLITE_OK ){ - jx9_result_value(pCtx,pValue); - /* pValue will be automatically released as soon we return from this function */ - }else{ - /* No such record, return null */ - jx9_result_null(pCtx); - } - } - }else{ - /* No such collection, return null */ - jx9_result_null(pCtx); - } - return JX9_OK; -} -/* - * array db_fetch_all(string $col_name,[callback filter_callback]) - * array db_get_all(string $col_name,[callback filter_callback]) - * Retrieve all records of a given collection and apply the given - * callback if available to filter records. - * Parameter - * col_name: Collection name - * Return - * Contents of the collection (JSON array) on success. NULL on failure. - */ -static int unqliteBuiltin_db_fetch_all(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - int rc; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name"); - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - jx9_value *pValue,*pArray,*pCallback = 0; - jx9_value sResult; /* Callback result */ - /* Allocate an empty scalar value and an empty JSON array */ - pArray = jx9_context_new_array(pCtx); - pValue = jx9_context_new_scalar(pCtx); - jx9MemObjInit(pCtx->pVm,&sResult); - if( pValue == 0 || pArray == 0 ){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory"); - jx9_result_null(pCtx); - return JX9_OK; - } - if( argc > 1 && jx9_value_is_callable(argv[1]) ){ - pCallback = argv[1]; - } - unqliteCollectionResetRecordCursor(pCol); - /* Fetch collection records one after one */ - while( UNQLITE_OK == unqliteCollectionFetchNextRecord(pCol,pValue) ){ - if( pCallback ){ - jx9_value *apArg[2]; - /* Invoke the filter callback */ - apArg[0] = pValue; - rc = jx9VmCallUserFunction(pCtx->pVm,pCallback,1,apArg,&sResult); - if( rc == JX9_OK ){ - int iResult; /* Callback result */ - /* Extract callback result */ - iResult = jx9_value_to_bool(&sResult); - if( !iResult ){ - /* Discard the result */ - unqliteCollectionCacheRemoveRecord(pCol,unqliteCollectionCurrentRecordId(pCol) - 1); - continue; - } - } - } - /* Put the value in the JSON array */ - jx9_array_add_elem(pArray,0,pValue); - /* Release the value */ - jx9_value_null(pValue); - } - jx9MemObjRelease(&sResult); - /* Finally, return our array */ - jx9_result_value(pCtx,pArray); - /* pValue will be automatically released as soon we return from - * this foreign function. - */ - }else{ - /* No such collection, return null */ - jx9_result_null(pCtx); - } - return JX9_OK; -} -/* - * int64 db_last_record_id(string $col_name) - * Return the ID of the last inserted record. - * Parameter - * col_name: Collection name - * Return - * Record ID (64-bit integer) on success. FALSE on failure. - */ -static int unqliteBuiltin_db_last_record_id(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - jx9_result_int64(pCtx,unqliteCollectionLastRecordId(pCol)); - }else{ - /* No such collection, return FALSE */ - jx9_result_bool(pCtx,0); - } - return JX9_OK; -} -/* - * inr64 db_current_record_id(string $col_name) - * Return the current record ID. - * Parameter - * col_name: Collection name - * Return - * Current record ID (64-bit integer) on success. FALSE on failure. - */ -static int unqliteBuiltin_db_current_record_id(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - jx9_result_int64(pCtx,unqliteCollectionCurrentRecordId(pCol)); - }else{ - /* No such collection, return FALSE */ - jx9_result_bool(pCtx,0); - } - return JX9_OK; -} -/* - * bool db_reset_record_cursor(string $col_name) - * Reset the record ID cursor. - * Parameter - * col_name: Collection name - * Return - * TRUE on success. FALSE on failure. - */ -static int unqliteBuiltin_db_reset_record_cursor(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - unqliteCollectionResetRecordCursor(pCol); - jx9_result_bool(pCtx,1); - }else{ - /* No such collection */ - jx9_result_bool(pCtx,0); - } - return JX9_OK; -} -/* - * int64 db_total_records(string $col_name) - * Return the total number of inserted records in the given collection. - * Parameter - * col_name: Collection name - * Return - * Total number of records on success. FALSE on failure. - */ -static int unqliteBuiltin_db_total_records(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - unqlite_int64 nRec; - nRec = unqliteCollectionTotalRecords(pCol); - jx9_result_int64(pCtx,nRec); - }else{ - /* No such collection */ - jx9_result_bool(pCtx,0); - } - return JX9_OK; -} -/* - * string db_creation_date(string $col_name) - * Return the creation date of the given collection. - * Parameter - * col_name: Collection name - * Return - * Creation date on success. FALSE on failure. - */ -static int unqliteBuiltin_db_creation_date(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - Sytm *pTm = &pCol->sCreation; - jx9_result_string_format(pCtx,"%d-%d-%d %02d:%02d:%02d", - pTm->tm_year,pTm->tm_mon,pTm->tm_mday, - pTm->tm_hour,pTm->tm_min,pTm->tm_sec - ); - }else{ - /* No such collection */ - jx9_result_bool(pCtx,0); - } - return JX9_OK; -} -/* - * bool db_store(string $col_name,...) - * bool db_put(string $col_name,...) - * Store one or more JSON values in a given collection. - * Parameter - * col_name: Collection name - * Return - * TRUE on success. FALSE on failure. - */ -static int unqliteBuiltin_db_store(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - int rc; - int i; - /* Extract collection name */ - if( argc < 2 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol == 0 ){ - jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - /* Store the given values */ - for( i = 1 ; i < argc ; ++i ){ - rc = unqliteCollectionPut(pCol,argv[i],0); - if( rc != UNQLITE_OK){ - jx9_context_throw_error_format(pCtx,JX9_CTX_ERR, - "Error while storing record %d in collection '%z'",i,&sName - ); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - } - /* All done, return TRUE */ - jx9_result_bool(pCtx,1); - return JX9_OK; -} -/* - * bool db_drop_collection(string $col_name) - * bool collection_delete(string $col_name) - * Remove a given collection from the database. - * Parameter - * col_name: Collection name - * Return - * TRUE on success. FALSE on failure. - */ -static int unqliteBuiltin_db_drop_col(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - int rc; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol == 0 ){ - jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - /* Drop the collection */ - rc = unqliteDropCollection(pCol); - /* Processing result */ - jx9_result_bool(pCtx,rc == UNQLITE_OK); - return JX9_OK; -} -/* - * bool db_drop_record(string $col_name,int64 record_id) - * Remove a given record from a collection. - * Parameter - * col_name: Collection name. - * record_id: ID of the record. - * Return - * TRUE on success. FALSE on failure. - */ -static int unqliteBuiltin_db_drop_record(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - jx9_int64 nId; - int nByte; - int rc; - /* Extract collection name */ - if( argc < 2 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol == 0 ){ - jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - /* Extract the record ID */ - nId = jx9_value_to_int64(argv[1]); - /* Drop the record */ - rc = unqliteCollectionDropRecord(pCol,nId,1,1); - /* Processing result */ - jx9_result_bool(pCtx,rc == UNQLITE_OK); - return JX9_OK; -} -/* - * bool db_set_schema(string $col_name, object $json_object) - * Set a schema for a given collection. - * Parameter - * col_name: Collection name. - * json_object: Collection schema (Must be a JSON object). - * Return - * TRUE on success. FALSE on failure. - */ -static int unqliteBuiltin_db_set_schema(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - int rc; - /* Extract collection name */ - if( argc < 2 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or db scheme"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - if( !jx9_value_is_json_object(argv[1]) ){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection scheme"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - rc = UNQLITE_NOOP; - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - /* Set the collection scheme */ - rc = unqliteCollectionSetSchema(pCol,argv[1]); - }else{ - jx9_context_throw_error_format(pCtx,JX9_CTX_WARNING, - "No such collection '%z'", - &sName - ); - } - /* Processing result */ - jx9_result_bool(pCtx,rc == UNQLITE_OK); - return JX9_OK; -} -/* - * object db_get_schema(string $col_name) - * Return the schema associated with a given collection. - * Parameter - * col_name: Collection name - * Return - * Collection schema on success. null otherwise. - */ -static int unqliteBuiltin_db_get_schema(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_col *pCol; - const char *zName; - unqlite_vm *pVm; - SyString sName; - int nByte; - /* Extract collection name */ - if( argc < 1 ){ - /* Missing arguments */ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or db scheme"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - zName = jx9_value_to_string(argv[0],&nByte); - if( nByte < 1){ - jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name"); - /* Return false */ - jx9_result_bool(pCtx,0); - return JX9_OK; - } - SyStringInitFromBuf(&sName,zName,nByte); - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Fetch the collection */ - pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD); - if( pCol ){ - /* Return the collection schema */ - jx9_result_value(pCtx,&pCol->sSchema); - }else{ - jx9_context_throw_error_format(pCtx,JX9_CTX_WARNING, - "No such collection '%z'", - &sName - ); - jx9_result_null(pCtx); - } - return JX9_OK; -} -/* - * bool db_begin(void) - * Manually begin a write transaction. - * Parameter - * None - * Return - * TRUE on success. FALSE otherwise. - */ -static int unqliteBuiltin_db_begin(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_vm *pVm; - unqlite *pDb; - int rc; - SXUNUSED(argc); /* cc warning */ - SXUNUSED(argv); - /* Point to the unqlite Vm */ - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Point to the underlying database handle */ - pDb = pVm->pDb; - /* Begin the transaction */ - rc = unqlitePagerBegin(pDb->sDB.pPager); - /* result */ - jx9_result_bool(pCtx,rc == UNQLITE_OK ); - return JX9_OK; -} -/* - * bool db_commit(void) - * Manually commit a transaction. - * Parameter - * None - * Return - * TRUE if the transaction was successfuly commited. FALSE otherwise. - */ -static int unqliteBuiltin_db_commit(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_vm *pVm; - unqlite *pDb; - int rc; - SXUNUSED(argc); /* cc warning */ - SXUNUSED(argv); - /* Point to the unqlite Vm */ - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Point to the underlying database handle */ - pDb = pVm->pDb; - /* Commit the transaction if any */ - rc = unqlitePagerCommit(pDb->sDB.pPager); - /* Commit result */ - jx9_result_bool(pCtx,rc == UNQLITE_OK ); - return JX9_OK; -} -/* - * bool db_rollback(void) - * Manually rollback a transaction. - * Parameter - * None - * Return - * TRUE if the transaction was successfuly rolled back. FALSE otherwise - */ -static int unqliteBuiltin_db_rollback(jx9_context *pCtx,int argc,jx9_value **argv) -{ - unqlite_vm *pVm; - unqlite *pDb; - int rc; - SXUNUSED(argc); /* cc warning */ - SXUNUSED(argv); - /* Point to the unqlite Vm */ - pVm = (unqlite_vm *)jx9_context_user_data(pCtx); - /* Point to the underlying database handle */ - pDb = pVm->pDb; - /* Rollback the transaction if any */ - rc = unqlitePagerRollback(pDb->sDB.pPager,TRUE); - /* Rollback result */ - jx9_result_bool(pCtx,rc == UNQLITE_OK ); - return JX9_OK; -} -/* - * Register all the UnQLite foreign functions defined above. - */ -UNQLITE_PRIVATE int unqliteRegisterJx9Functions(unqlite_vm *pVm) -{ - static const jx9_builtin_func aBuiltin[] = { - { "db_version" , unqliteBuiltin_db_version }, - { "db_copyright", unqliteBuiltin_db_credits }, - { "db_credits" , unqliteBuiltin_db_credits }, - { "db_sig" , unqliteBuiltin_db_sig }, - { "db_errlog", unqliteBuiltin_db_errlog }, - { "collection_exists", unqliteBuiltin_collection_exists }, - { "db_exists", unqliteBuiltin_collection_exists }, - { "collection_create", unqliteBuiltin_collection_create }, - { "db_create", unqliteBuiltin_collection_create }, - { "db_fetch", unqliteBuiltin_db_fetch_next }, - { "db_get", unqliteBuiltin_db_fetch_next }, - { "db_fetch_by_id", unqliteBuiltin_db_fetch_by_id }, - { "db_get_by_id", unqliteBuiltin_db_fetch_by_id }, - { "db_fetch_all", unqliteBuiltin_db_fetch_all }, - { "db_get_all", unqliteBuiltin_db_fetch_all }, - { "db_last_record_id", unqliteBuiltin_db_last_record_id }, - { "db_current_record_id", unqliteBuiltin_db_current_record_id }, - { "db_reset_record_cursor", unqliteBuiltin_db_reset_record_cursor }, - { "db_total_records", unqliteBuiltin_db_total_records }, - { "db_creation_date", unqliteBuiltin_db_creation_date }, - { "db_store", unqliteBuiltin_db_store }, - { "db_put", unqliteBuiltin_db_store }, - { "db_drop_collection", unqliteBuiltin_db_drop_col }, - { "collection_delete", unqliteBuiltin_db_drop_col }, - { "db_drop_record", unqliteBuiltin_db_drop_record }, - { "db_set_schema", unqliteBuiltin_db_set_schema }, - { "db_get_schema", unqliteBuiltin_db_get_schema }, - { "db_begin", unqliteBuiltin_db_begin }, - { "db_commit", unqliteBuiltin_db_commit }, - { "db_rollback", unqliteBuiltin_db_rollback }, - }; - int rc = UNQLITE_OK; - sxu32 n; - /* Register the unQLite functions defined above in the Jx9 call table */ - for( n = 0 ; n < SX_ARRAYSIZE(aBuiltin) ; ++n ){ - rc = jx9_create_function(pVm->pJx9Vm,aBuiltin[n].zName,aBuiltin[n].xFunc,pVm); - } - return rc; -} -/* END-OF-IMPLEMENTATION: unqlite@embedded@symisc 34-09-46 */ -/* - * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ -/* - * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ diff --git a/unqlite.c b/unqlite.c new file mode 120000 index 0000000..32e5b65 --- /dev/null +++ b/unqlite.c @@ -0,0 +1 @@ +unqlite/unqlite.c \ No newline at end of file diff --git a/unqlite.h b/unqlite.h deleted file mode 100644 index 2a2b1ad..0000000 --- a/unqlite.h +++ /dev/null @@ -1,949 +0,0 @@ -/* This file was automatically generated. Do not edit (Except for compile time directives)! */ -#ifndef _UNQLITE_H_ -#define _UNQLITE_H_ -/* - * Symisc UnQLite: An Embeddable NoSQL (Post Modern) Database Engine. - * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ - * Version 1.1.6 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ -/* - * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - /* $SymiscID: unqlite.h v1.1 UNIX|WIN32/64 2012-11-02 02:10 stable $ */ -#include /* needed for the definition of va_list */ -/* - * Compile time engine version, signature, identification in the symisc source tree - * and copyright notice. - * Each macro have an equivalent C interface associated with it that provide the same - * information but are associated with the library instead of the header file. - * Refer to [unqlite_lib_version()], [unqlite_lib_signature()], [unqlite_lib_ident()] and - * [unqlite_lib_copyright()] for more information. - */ -/* - * The UNQLITE_VERSION C preprocessor macroevaluates to a string literal - * that is the unqlite version in the format "X.Y.Z" where X is the major - * version number and Y is the minor version number and Z is the release - * number. - */ -#define UNQLITE_VERSION "1.1.6" -/* - * The UNQLITE_VERSION_NUMBER C preprocessor macro resolves to an integer - * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same - * numbers used in [UNQLITE_VERSION]. - */ -#define UNQLITE_VERSION_NUMBER 1001006 -/* - * The UNQLITE_SIG C preprocessor macro evaluates to a string - * literal which is the public signature of the unqlite engine. - * This signature could be included for example in a host-application - * generated Server MIME header as follows: - * Server: YourWebServer/x.x unqlite/x.x.x \r\n - */ -#define UNQLITE_SIG "unqlite/1.1.6" -/* - * UnQLite identification in the Symisc source tree: - * Each particular check-in of a particular software released - * by symisc systems have an unique identifier associated with it. - * This macro hold the one associated with unqlite. - */ -#define UNQLITE_IDENT "unqlite:b172a1e2c3f62fb35c8e1fb2795121f82356cad6" -/* - * Copyright notice. - * If you have any questions about the licensing situation, please - * visit http://unqlite.org/licensing.html - * or contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - */ -#define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine ] 2012-2013, http://unqlite.org/" - -/* Forward declaration to public objects */ -typedef struct unqlite_io_methods unqlite_io_methods; -typedef struct unqlite_kv_methods unqlite_kv_methods; -typedef struct unqlite_kv_engine unqlite_kv_engine; -typedef struct jx9_io_stream unqlite_io_stream; -typedef struct jx9_context unqlite_context; -typedef struct jx9_value unqlite_value; -typedef struct unqlite_vfs unqlite_vfs; -typedef struct unqlite_vm unqlite_vm; -typedef struct unqlite unqlite; -/* - * ------------------------------ - * Compile time directives - * ------------------------------ - * For most purposes, UnQLite can be built just fine using the default compilation options. - * However, if required, the compile-time options documented below can be used to omit UnQLite - * features (resulting in a smaller compiled library size) or to change the default values - * of some parameters. - * Every effort has been made to ensure that the various combinations of compilation options - * work harmoniously and produce a working library. - * - * UNQLITE_ENABLE_THREADS - * This option controls whether or not code is included in UnQLite to enable it to operate - * safely in a multithreaded environment. The default is not. All mutexing code is omitted - * and it is unsafe to use UnQLite in a multithreaded program. When compiled with the - * UNQLITE_ENABLE_THREADS directive enabled, UnQLite can be used in a multithreaded program - * and it is safe to share the same virtual machine and engine handle between two or more threads. - * The value of UNQLITE_ENABLE_THREADS can be determined at run-time using the unqlite_lib_is_threadsafe() - * interface. - * When UnQLite has been compiled with threading support then the threading mode can be altered - * at run-time using the unqlite_lib_config() interface together with one of these verbs: - * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE - * UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI - * Platforms others than Windows and UNIX systems must install their own mutex subsystem via - * unqlite_lib_config() with a configuration verb set to UNQLITE_LIB_CONFIG_USER_MUTEX. - * Otherwise the library is not threadsafe. - * Note that you must link UnQLite with the POSIX threads library under UNIX systems (i.e: -lpthread). - * - * Options To Omit/Enable Features - * - * The following options can be used to reduce the size of the compiled library by omitting optional - * features. This is probably only useful in embedded systems where space is especially tight, as even - * with all features included the UnQLite library is relatively small. Don't forget to tell your - * compiler to optimize for binary size! (the -Os option if using GCC). Telling your compiler - * to optimize for size usually has a much larger impact on library footprint than employing - * any of these compile-time options. - * - * JX9_DISABLE_BUILTIN_FUNC - * Jx9 is shipped with more than 312 built-in functions suitable for most purposes like - * string and INI processing, ZIP extracting, Base64 encoding/decoding, JSON encoding/decoding - * and so forth. - * If this directive is enabled, then all built-in Jx9 functions are omitted from the build. - * Note that special functions such as db_create(), db_store(), db_fetch(), etc. are not omitted - * from the build and are not affected by this directive. - * - * JX9_ENABLE_MATH_FUNC - * If this directive is enabled, built-in math functions such as sqrt(), abs(), log(), ceil(), etc. - * are included in the build. Note that you may need to link UnQLite with the math library in same - * Linux/BSD flavor (i.e: -lm). - * - * JX9_DISABLE_DISK_IO - * If this directive is enabled, built-in VFS functions such as chdir(), mkdir(), chroot(), unlink(), - * sleep(), etc. are omitted from the build. - * - * UNQLITE_ENABLE_JX9_HASH_IO - * If this directive is enabled, built-in hash functions such as md5(), sha1(), md5_file(), crc32(), etc. - * are included in the build. - */ -/* Symisc public definitions */ -#if !defined(SYMISC_STANDARD_DEFS) -#define SYMISC_STANDARD_DEFS -#if defined (_WIN32) || defined (WIN32) || defined(__MINGW32__) || defined (_MSC_VER) || defined (_WIN32_WCE) -/* Windows Systems */ -#if !defined(__WINNT__) -#define __WINNT__ -#endif -/* - * Determine if we are dealing with WindowsCE - which has a much - * reduced API. - */ -#if defined(_WIN32_WCE) -#ifndef __WIN_CE__ -#define __WIN_CE__ -#endif /* __WIN_CE__ */ -#endif /* _WIN32_WCE */ -#else -/* - * By default we will assume that we are compiling on a UNIX systems. - * Otherwise the OS_OTHER directive must be defined. - */ -#if !defined(OS_OTHER) -#if !defined(__UNIXES__) -#define __UNIXES__ -#endif /* __UNIXES__ */ -#else -#endif /* OS_OTHER */ -#endif /* __WINNT__/__UNIXES__ */ -#if defined(_MSC_VER) || defined(__BORLANDC__) -typedef signed __int64 sxi64; /* 64 bits(8 bytes) signed int64 */ -typedef unsigned __int64 sxu64; /* 64 bits(8 bytes) unsigned int64 */ -#else -typedef signed long long int sxi64; /* 64 bits(8 bytes) signed int64 */ -typedef unsigned long long int sxu64; /* 64 bits(8 bytes) unsigned int64 */ -#endif /* _MSC_VER */ -/* Signature of the consumer routine */ -typedef int (*ProcConsumer)(const void *, unsigned int, void *); -/* Forward reference */ -typedef struct SyMutexMethods SyMutexMethods; -typedef struct SyMemMethods SyMemMethods; -typedef struct SyString SyString; -typedef struct syiovec syiovec; -typedef struct SyMutex SyMutex; -typedef struct Sytm Sytm; -/* Scatter and gather array. */ -struct syiovec -{ -#if defined (__WINNT__) - /* Same fields type and offset as WSABUF structure defined one winsock2 header */ - unsigned long nLen; - char *pBase; -#else - void *pBase; - unsigned long nLen; -#endif -}; -struct SyString -{ - const char *zString; /* Raw string (may not be null terminated) */ - unsigned int nByte; /* Raw string length */ -}; -/* Time structure. */ -struct Sytm -{ - int tm_sec; /* seconds (0 - 60) */ - int tm_min; /* minutes (0 - 59) */ - int tm_hour; /* hours (0 - 23) */ - int tm_mday; /* day of month (1 - 31) */ - int tm_mon; /* month of year (0 - 11) */ - int tm_year; /* year + 1900 */ - int tm_wday; /* day of week (Sunday = 0) */ - int tm_yday; /* day of year (0 - 365) */ - int tm_isdst; /* is summer time in effect? */ - char *tm_zone; /* abbreviation of timezone name */ - long tm_gmtoff; /* offset from UTC in seconds */ -}; -/* Convert a tm structure (struct tm *) found in to a Sytm structure */ -#define STRUCT_TM_TO_SYTM(pTM, pSYTM) \ - (pSYTM)->tm_hour = (pTM)->tm_hour;\ - (pSYTM)->tm_min = (pTM)->tm_min;\ - (pSYTM)->tm_sec = (pTM)->tm_sec;\ - (pSYTM)->tm_mon = (pTM)->tm_mon;\ - (pSYTM)->tm_mday = (pTM)->tm_mday;\ - (pSYTM)->tm_year = (pTM)->tm_year + 1900;\ - (pSYTM)->tm_yday = (pTM)->tm_yday;\ - (pSYTM)->tm_wday = (pTM)->tm_wday;\ - (pSYTM)->tm_isdst = (pTM)->tm_isdst;\ - (pSYTM)->tm_gmtoff = 0;\ - (pSYTM)->tm_zone = 0; - -/* Convert a SYSTEMTIME structure (LPSYSTEMTIME: Windows Systems only ) to a Sytm structure */ -#define SYSTEMTIME_TO_SYTM(pSYSTIME, pSYTM) \ - (pSYTM)->tm_hour = (pSYSTIME)->wHour;\ - (pSYTM)->tm_min = (pSYSTIME)->wMinute;\ - (pSYTM)->tm_sec = (pSYSTIME)->wSecond;\ - (pSYTM)->tm_mon = (pSYSTIME)->wMonth - 1;\ - (pSYTM)->tm_mday = (pSYSTIME)->wDay;\ - (pSYTM)->tm_year = (pSYSTIME)->wYear;\ - (pSYTM)->tm_yday = 0;\ - (pSYTM)->tm_wday = (pSYSTIME)->wDayOfWeek;\ - (pSYTM)->tm_gmtoff = 0;\ - (pSYTM)->tm_isdst = -1;\ - (pSYTM)->tm_zone = 0; - -/* Dynamic memory allocation methods. */ -struct SyMemMethods -{ - void * (*xAlloc)(unsigned int); /* [Required:] Allocate a memory chunk */ - void * (*xRealloc)(void *, unsigned int); /* [Required:] Re-allocate a memory chunk */ - void (*xFree)(void *); /* [Required:] Release a memory chunk */ - unsigned int (*xChunkSize)(void *); /* [Optional:] Return chunk size */ - int (*xInit)(void *); /* [Optional:] Initialization callback */ - void (*xRelease)(void *); /* [Optional:] Release callback */ - void *pUserData; /* [Optional:] First argument to xInit() and xRelease() */ -}; -/* Out of memory callback signature. */ -typedef int (*ProcMemError)(void *); -/* Mutex methods. */ -struct SyMutexMethods -{ - int (*xGlobalInit)(void); /* [Optional:] Global mutex initialization */ - void (*xGlobalRelease)(void); /* [Optional:] Global Release callback () */ - SyMutex * (*xNew)(int); /* [Required:] Request a new mutex */ - void (*xRelease)(SyMutex *); /* [Optional:] Release a mutex */ - void (*xEnter)(SyMutex *); /* [Required:] Enter mutex */ - int (*xTryEnter)(SyMutex *); /* [Optional:] Try to enter a mutex */ - void (*xLeave)(SyMutex *); /* [Required:] Leave a locked mutex */ -}; -#if defined (_MSC_VER) || defined (__MINGW32__) || defined (__GNUC__) && defined (__declspec) -#define SX_APIIMPORT __declspec(dllimport) -#define SX_APIEXPORT __declspec(dllexport) -#else -#define SX_APIIMPORT -#define SX_APIEXPORT -#endif -/* Standard return values from Symisc public interfaces */ -#define SXRET_OK 0 /* Not an error */ -#define SXERR_MEM (-1) /* Out of memory */ -#define SXERR_IO (-2) /* IO error */ -#define SXERR_EMPTY (-3) /* Empty field */ -#define SXERR_LOCKED (-4) /* Locked operation */ -#define SXERR_ORANGE (-5) /* Out of range value */ -#define SXERR_NOTFOUND (-6) /* Item not found */ -#define SXERR_LIMIT (-7) /* Limit reached */ -#define SXERR_MORE (-8) /* Need more input */ -#define SXERR_INVALID (-9) /* Invalid parameter */ -#define SXERR_ABORT (-10) /* User callback request an operation abort */ -#define SXERR_EXISTS (-11) /* Item exists */ -#define SXERR_SYNTAX (-12) /* Syntax error */ -#define SXERR_UNKNOWN (-13) /* Unknown error */ -#define SXERR_BUSY (-14) /* Busy operation */ -#define SXERR_OVERFLOW (-15) /* Stack or buffer overflow */ -#define SXERR_WILLBLOCK (-16) /* Operation will block */ -#define SXERR_NOTIMPLEMENTED (-17) /* Operation not implemented */ -#define SXERR_EOF (-18) /* End of input */ -#define SXERR_PERM (-19) /* Permission error */ -#define SXERR_NOOP (-20) /* No-op */ -#define SXERR_FORMAT (-21) /* Invalid format */ -#define SXERR_NEXT (-22) /* Not an error */ -#define SXERR_OS (-23) /* System call return an error */ -#define SXERR_CORRUPT (-24) /* Corrupted pointer */ -#define SXERR_CONTINUE (-25) /* Not an error: Operation in progress */ -#define SXERR_NOMATCH (-26) /* No match */ -#define SXERR_RESET (-27) /* Operation reset */ -#define SXERR_DONE (-28) /* Not an error */ -#define SXERR_SHORT (-29) /* Buffer too short */ -#define SXERR_PATH (-30) /* Path error */ -#define SXERR_TIMEOUT (-31) /* Timeout */ -#define SXERR_BIG (-32) /* Too big for processing */ -#define SXERR_RETRY (-33) /* Retry your call */ -#define SXERR_IGNORE (-63) /* Ignore */ -#endif /* SYMISC_PUBLIC_DEFS */ -/* - * Marker for exported interfaces. - */ -#define UNQLITE_APIEXPORT SX_APIEXPORT -/* - * If compiling for a processor that lacks floating point - * support, substitute integer for floating-point. - */ -#ifdef UNQLITE_OMIT_FLOATING_POINT -typedef sxi64 uqlite_real; -#else -typedef double unqlite_real; -#endif -typedef sxi64 unqlite_int64; -/* Standard UnQLite return values */ -#define UNQLITE_OK SXRET_OK /* Successful result */ -/* Beginning of error codes */ -#define UNQLITE_NOMEM SXERR_MEM /* Out of memory */ -#define UNQLITE_ABORT SXERR_ABORT /* Another thread have released this instance */ -#define UNQLITE_IOERR SXERR_IO /* IO error */ -#define UNQLITE_CORRUPT SXERR_CORRUPT /* Corrupt pointer */ -#define UNQLITE_LOCKED SXERR_LOCKED /* Forbidden Operation */ -#define UNQLITE_BUSY SXERR_BUSY /* The database file is locked */ -#define UNQLITE_DONE SXERR_DONE /* Operation done */ -#define UNQLITE_PERM SXERR_PERM /* Permission error */ -#define UNQLITE_NOTIMPLEMENTED SXERR_NOTIMPLEMENTED /* Method not implemented by the underlying Key/Value storage engine */ -#define UNQLITE_NOTFOUND SXERR_NOTFOUND /* No such record */ -#define UNQLITE_NOOP SXERR_NOOP /* No such method */ -#define UNQLITE_INVALID SXERR_INVALID /* Invalid parameter */ -#define UNQLITE_EOF SXERR_EOF /* End Of Input */ -#define UNQLITE_UNKNOWN SXERR_UNKNOWN /* Unknown configuration option */ -#define UNQLITE_LIMIT SXERR_LIMIT /* Database limit reached */ -#define UNQLITE_EXISTS SXERR_EXISTS /* Record exists */ -#define UNQLITE_EMPTY SXERR_EMPTY /* Empty record */ -#define UNQLITE_COMPILE_ERR (-70) /* Compilation error */ -#define UNQLITE_VM_ERR (-71) /* Virtual machine error */ -#define UNQLITE_FULL (-73) /* Full database (unlikely) */ -#define UNQLITE_CANTOPEN (-74) /* Unable to open the database file */ -#define UNQLITE_READ_ONLY (-75) /* Read only Key/Value storage engine */ -#define UNQLITE_LOCKERR (-76) /* Locking protocol error */ -/* end-of-error-codes */ -/* - * Database Handle Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure an UnQLite database handle. - * These constants must be passed as the second argument to [unqlite_config()]. - * - * Each options require a variable number of arguments. - * The [unqlite_config()] interface will return UNQLITE_OK on success, any other - * return value indicates failure. - * For a full discussion on the configuration verbs and their expected - * parameters, please refer to this page: - * http://unqlite.org/c_api/unqlite_config.html - */ -#define UNQLITE_CONFIG_JX9_ERR_LOG 1 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */ -#define UNQLITE_CONFIG_MAX_PAGE_CACHE 2 /* ONE ARGUMENT: int nMaxPage */ -#define UNQLITE_CONFIG_ERR_LOG 3 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */ -#define UNQLITE_CONFIG_KV_ENGINE 4 /* ONE ARGUMENT: const char *zKvName */ -#define UNQLITE_CONFIG_DISABLE_AUTO_COMMIT 5 /* NO ARGUMENTS */ -#define UNQLITE_CONFIG_GET_KV_NAME 6 /* ONE ARGUMENT: const char **pzPtr */ -/* - * UnQLite/Jx9 Virtual Machine Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the Jx9 (Via UnQLite) Virtual machine. - * These constants must be passed as the second argument to the [unqlite_vm_config()] - * interface. - * Each options require a variable number of arguments. - * The [unqlite_vm_config()] interface will return UNQLITE_OK on success, any other return - * value indicates failure. - * There are many options but the most importants are: UNQLITE_VM_CONFIG_OUTPUT which install - * a VM output consumer callback, UNQLITE_VM_CONFIG_HTTP_REQUEST which parse and register - * a HTTP request and UNQLITE_VM_CONFIG_ARGV_ENTRY which populate the $argv array. - * For a full discussion on the configuration verbs and their expected parameters, please - * refer to this page: - * http://unqlite.org/c_api/unqlite_vm_config.html - */ -#define UNQLITE_VM_CONFIG_OUTPUT 1 /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */ -#define UNQLITE_VM_CONFIG_IMPORT_PATH 2 /* ONE ARGUMENT: const char *zIncludePath */ -#define UNQLITE_VM_CONFIG_ERR_REPORT 3 /* NO ARGUMENTS: Report all run-time errors in the VM output */ -#define UNQLITE_VM_CONFIG_RECURSION_DEPTH 4 /* ONE ARGUMENT: int nMaxDepth */ -#define UNQLITE_VM_OUTPUT_LENGTH 5 /* ONE ARGUMENT: unsigned int *pLength */ -#define UNQLITE_VM_CONFIG_CREATE_VAR 6 /* TWO ARGUMENTS: const char *zName, unqlite_value *pValue */ -#define UNQLITE_VM_CONFIG_HTTP_REQUEST 7 /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */ -#define UNQLITE_VM_CONFIG_SERVER_ATTR 8 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */ -#define UNQLITE_VM_CONFIG_ENV_ATTR 9 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */ -#define UNQLITE_VM_CONFIG_EXEC_VALUE 10 /* ONE ARGUMENT: unqlite_value **ppValue */ -#define UNQLITE_VM_CONFIG_IO_STREAM 11 /* ONE ARGUMENT: const unqlite_io_stream *pStream */ -#define UNQLITE_VM_CONFIG_ARGV_ENTRY 12 /* ONE ARGUMENT: const char *zValue */ -#define UNQLITE_VM_CONFIG_EXTRACT_OUTPUT 13 /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */ -/* - * Storage engine configuration commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the underlying storage engine (i.e Hash, B+tree, R+tree). - * These constants must be passed as the first argument to [unqlite_kv_config()]. - * Each options require a variable number of arguments. - * The [unqlite_kv_config()] interface will return UNQLITE_OK on success, any other return - * value indicates failure. - * For a full discussion on the configuration verbs and their expected parameters, please - * refer to this page: - * http://unqlite.org/c_api/unqlite_kv_config.html - */ -#define UNQLITE_KV_CONFIG_HASH_FUNC 1 /* ONE ARGUMENT: unsigned int (*xHash)(const void *,unsigned int) */ -#define UNQLITE_KV_CONFIG_CMP_FUNC 2 /* ONE ARGUMENT: int (*xCmp)(const void *,const void *,unsigned int) */ -/* - * Global Library Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the whole library. - * These constants must be passed as the first argument to [unqlite_lib_config()]. - * - * Each options require a variable number of arguments. - * The [unqlite_lib_config()] interface will return UNQLITE_OK on success, any other return - * value indicates failure. - * Notes: - * The default configuration is recommended for most applications and so the call to - * [unqlite_lib_config()] is usually not necessary. It is provided to support rare - * applications with unusual needs. - * The [unqlite_lib_config()] interface is not threadsafe. The application must insure that - * no other [unqlite_*()] interfaces are invoked by other threads while [unqlite_lib_config()] - * is running. Furthermore, [unqlite_lib_config()] may only be invoked prior to library - * initialization using [unqlite_lib_init()] or [unqlite_init()] or after shutdown - * by [unqlite_lib_shutdown()]. If [unqlite_lib_config()] is called after [unqlite_lib_init()] - * or [unqlite_init()] and before [unqlite_lib_shutdown()] then it will return UNQLITE_LOCKED. - * For a full discussion on the configuration verbs and their expected parameters, please - * refer to this page: - * http://unqlite.org/c_api/unqlite_lib.html - */ -#define UNQLITE_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ -#define UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */ -#define UNQLITE_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ -#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */ -#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */ -#define UNQLITE_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const unqlite_vfs *pVfs */ -#define UNQLITE_LIB_CONFIG_STORAGE_ENGINE 7 /* ONE ARGUMENT: unqlite_kv_methods *pStorage */ -#define UNQLITE_LIB_CONFIG_PAGE_SIZE 8 /* ONE ARGUMENT: int iPageSize */ -/* - * These bit values are intended for use in the 3rd parameter to the [unqlite_open()] interface - * and in the 4th parameter to the xOpen method of the [unqlite_vfs] object. - */ -#define UNQLITE_OPEN_READONLY 0x00000001 /* Read only mode. Ok for [unqlite_open] */ -#define UNQLITE_OPEN_READWRITE 0x00000002 /* Ok for [unqlite_open] */ -#define UNQLITE_OPEN_CREATE 0x00000004 /* Ok for [unqlite_open] */ -#define UNQLITE_OPEN_EXCLUSIVE 0x00000008 /* VFS only */ -#define UNQLITE_OPEN_TEMP_DB 0x00000010 /* VFS only */ -#define UNQLITE_OPEN_NOMUTEX 0x00000020 /* Ok for [unqlite_open] */ -#define UNQLITE_OPEN_OMIT_JOURNALING 0x00000040 /* Omit journaling for this database. Ok for [unqlite_open] */ -#define UNQLITE_OPEN_IN_MEMORY 0x00000080 /* An in memory database. Ok for [unqlite_open]*/ -#define UNQLITE_OPEN_MMAP 0x00000100 /* Obtain a memory view of the whole file. Ok for [unqlite_open] */ -/* - * Synchronization Type Flags - * - * When UnQLite invokes the xSync() method of an [unqlite_io_methods] object it uses - * a combination of these integer values as the second argument. - * - * When the UNQLITE_SYNC_DATAONLY flag is used, it means that the sync operation only - * needs to flush data to mass storage. Inode information need not be flushed. - * If the lower four bits of the flag equal UNQLITE_SYNC_NORMAL, that means to use normal - * fsync() semantics. If the lower four bits equal UNQLITE_SYNC_FULL, that means to use - * Mac OS X style fullsync instead of fsync(). - */ -#define UNQLITE_SYNC_NORMAL 0x00002 -#define UNQLITE_SYNC_FULL 0x00003 -#define UNQLITE_SYNC_DATAONLY 0x00010 -/* - * File Locking Levels - * - * UnQLite uses one of these integer values as the second - * argument to calls it makes to the xLock() and xUnlock() methods - * of an [unqlite_io_methods] object. - */ -#define UNQLITE_LOCK_NONE 0 -#define UNQLITE_LOCK_SHARED 1 -#define UNQLITE_LOCK_RESERVED 2 -#define UNQLITE_LOCK_PENDING 3 -#define UNQLITE_LOCK_EXCLUSIVE 4 -/* - * CAPIREF: OS Interface: Open File Handle - * - * An [unqlite_file] object represents an open file in the [unqlite_vfs] OS interface - * layer. - * Individual OS interface implementations will want to subclass this object by appending - * additional fields for their own use. The pMethods entry is a pointer to an - * [unqlite_io_methods] object that defines methods for performing - * I/O operations on the open file. -*/ -typedef struct unqlite_file unqlite_file; -struct unqlite_file { - const unqlite_io_methods *pMethods; /* Methods for an open file. MUST BE FIRST */ -}; -/* - * CAPIREF: OS Interface: File Methods Object - * - * Every file opened by the [unqlite_vfs] xOpen method populates an - * [unqlite_file] object (or, more commonly, a subclass of the - * [unqlite_file] object) with a pointer to an instance of this object. - * This object defines the methods used to perform various operations - * against the open file represented by the [unqlite_file] object. - * - * If the xOpen method sets the unqlite_file.pMethods element - * to a non-NULL pointer, then the unqlite_io_methods.xClose method - * may be invoked even if the xOpen reported that it failed. The - * only way to prevent a call to xClose following a failed xOpen - * is for the xOpen to set the unqlite_file.pMethods element to NULL. - * - * The flags argument to xSync may be one of [UNQLITE_SYNC_NORMAL] or - * [UNQLITE_SYNC_FULL]. The first choice is the normal fsync(). - * The second choice is a Mac OS X style fullsync. The [UNQLITE_SYNC_DATAONLY] - * flag may be ORed in to indicate that only the data of the file - * and not its inode needs to be synced. - * - * The integer values to xLock() and xUnlock() are one of - * - * UNQLITE_LOCK_NONE - * UNQLITE_LOCK_SHARED - * UNQLITE_LOCK_RESERVED - * UNQLITE_LOCK_PENDING - * UNQLITE_LOCK_EXCLUSIVE - * - * xLock() increases the lock. xUnlock() decreases the lock. - * The xCheckReservedLock() method checks whether any database connection, - * either in this process or in some other process, is holding a RESERVED, - * PENDING, or EXCLUSIVE lock on the file. It returns true if such a lock exists - * and false otherwise. - * - * The xSectorSize() method returns the sector size of the device that underlies - * the file. The sector size is the minimum write that can be performed without - * disturbing other bytes in the file. - * - */ -struct unqlite_io_methods { - int iVersion; /* Structure version number (currently 1) */ - int (*xClose)(unqlite_file*); - int (*xRead)(unqlite_file*, void*, unqlite_int64 iAmt, unqlite_int64 iOfst); - int (*xWrite)(unqlite_file*, const void*, unqlite_int64 iAmt, unqlite_int64 iOfst); - int (*xTruncate)(unqlite_file*, unqlite_int64 size); - int (*xSync)(unqlite_file*, int flags); - int (*xFileSize)(unqlite_file*, unqlite_int64 *pSize); - int (*xLock)(unqlite_file*, int); - int (*xUnlock)(unqlite_file*, int); - int (*xCheckReservedLock)(unqlite_file*, int *pResOut); - int (*xSectorSize)(unqlite_file*); -}; -/* - * CAPIREF: OS Interface Object - * - * An instance of the unqlite_vfs object defines the interface between - * the UnQLite core and the underlying operating system. The "vfs" - * in the name of the object stands for "Virtual File System". - * - * Only a single vfs can be registered within the UnQLite core. - * Vfs registration is done using the [unqlite_lib_config()] interface - * with a configuration verb set to UNQLITE_LIB_CONFIG_VFS. - * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users - * does not have to worry about registering and installing a vfs since UnQLite - * come with a built-in vfs for these platforms that implements most the methods - * defined below. - * - * Clients running on exotic systems (ie: Other than Windows and UNIX systems) - * must register their own vfs in order to be able to use the UnQLite library. - * - * The value of the iVersion field is initially 1 but may be larger in - * future versions of UnQLite. - * - * The szOsFile field is the size of the subclassed [unqlite_file] structure - * used by this VFS. mxPathname is the maximum length of a pathname in this VFS. - * - * At least szOsFile bytes of memory are allocated by UnQLite to hold the [unqlite_file] - * structure passed as the third argument to xOpen. The xOpen method does not have to - * allocate the structure; it should just fill it in. Note that the xOpen method must - * set the unqlite_file.pMethods to either a valid [unqlite_io_methods] object or to NULL. - * xOpen must do this even if the open fails. UnQLite expects that the unqlite_file.pMethods - * element will be valid after xOpen returns regardless of the success or failure of the - * xOpen call. - * - */ -struct unqlite_vfs { - const char *zName; /* Name of this virtual file system [i.e: Windows, UNIX, etc.] */ - int iVersion; /* Structure version number (currently 1) */ - int szOsFile; /* Size of subclassed unqlite_file */ - int mxPathname; /* Maximum file pathname length */ - int (*xOpen)(unqlite_vfs*, const char *zName, unqlite_file*,unsigned int flags); - int (*xDelete)(unqlite_vfs*, const char *zName, int syncDir); - int (*xAccess)(unqlite_vfs*, const char *zName, int flags, int *pResOut); - int (*xFullPathname)(unqlite_vfs*, const char *zName,int buf_len,char *zBuf); - int (*xTmpDir)(unqlite_vfs*,char *zBuf,int buf_len); - int (*xSleep)(unqlite_vfs*, int microseconds); - int (*xCurrentTime)(unqlite_vfs*,Sytm *pOut); - int (*xGetLastError)(unqlite_vfs*, int, char *); -}; -/* - * Flags for the xAccess VFS method - * - * These integer constants can be used as the third parameter to - * the xAccess method of an [unqlite_vfs] object. They determine - * what kind of permissions the xAccess method is looking for. - * With UNQLITE_ACCESS_EXISTS, the xAccess method - * simply checks whether the file exists. - * With UNQLITE_ACCESS_READWRITE, the xAccess method - * checks whether the named directory is both readable and writable - * (in other words, if files can be added, removed, and renamed within - * the directory). - * The UNQLITE_ACCESS_READWRITE constant is currently used only by the - * [temp_store_directory pragma], though this could change in a future - * release of UnQLite. - * With UNQLITE_ACCESS_READ, the xAccess method - * checks whether the file is readable. The UNQLITE_ACCESS_READ constant is - * currently unused, though it might be used in a future release of - * UnQLite. - */ -#define UNQLITE_ACCESS_EXISTS 0 -#define UNQLITE_ACCESS_READWRITE 1 -#define UNQLITE_ACCESS_READ 2 -/* - * The type used to represent a page number. The first page in a file - * is called page 1. 0 is used to represent "not a page". - * A page number is an unsigned 64-bit integer. - */ -typedef sxu64 pgno; -/* - * A database disk page is represented by an instance - * of the follwoing structure. - */ -typedef struct unqlite_page unqlite_page; -struct unqlite_page -{ - unsigned char *zData; /* Content of this page */ - void *pUserData; /* Extra content */ - pgno pgno; /* Page number for this page */ -}; -/* - * UnQLite handle to the underlying Key/Value Storage Engine (See below). - */ -typedef void * unqlite_kv_handle; -/* - * UnQLite pager IO methods. - * - * An instance of the following structure define the exported methods of the UnQLite pager - * to the underlying Key/Value storage engine. - */ -typedef struct unqlite_kv_io unqlite_kv_io; -struct unqlite_kv_io -{ - unqlite_kv_handle pHandle; /* UnQLite handle passed as the first parameter to the - * method defined below. - */ - unqlite_kv_methods *pMethods; /* Underlying storage engine */ - /* Pager methods */ - int (*xGet)(unqlite_kv_handle,pgno,unqlite_page **); - int (*xLookup)(unqlite_kv_handle,pgno,unqlite_page **); - int (*xNew)(unqlite_kv_handle,unqlite_page **); - int (*xWrite)(unqlite_page *); - int (*xDontWrite)(unqlite_page *); - int (*xDontJournal)(unqlite_page *); - int (*xDontMkHot)(unqlite_page *); - int (*xPageRef)(unqlite_page *); - int (*xPageUnref)(unqlite_page *); - int (*xPageSize)(unqlite_kv_handle); - int (*xReadOnly)(unqlite_kv_handle); - unsigned char * (*xTmpPage)(unqlite_kv_handle); - void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *)); - void (*xSetReload)(unqlite_kv_handle,void (*xPageReload)(void *)); - void (*xErr)(unqlite_kv_handle,const char *); -}; -/* - * Key/Value Storage Engine Cursor Object - * - * An instance of a subclass of the following object defines a cursor - * used to scan through a key-value storage engine. - */ -typedef struct unqlite_kv_cursor unqlite_kv_cursor; -struct unqlite_kv_cursor -{ - unqlite_kv_engine *pStore; /* Must be first */ - /* Subclasses will typically add additional fields */ -}; -/* - * Possible seek positions. - */ -#define UNQLITE_CURSOR_MATCH_EXACT 1 -#define UNQLITE_CURSOR_MATCH_LE 2 -#define UNQLITE_CURSOR_MATCH_GE 3 -/* - * Key/Value Storage Engine. - * - * A Key-Value storage engine is defined by an instance of the following - * object. - * UnQLite works with run-time interchangeable storage engines (i.e. Hash, B+Tree, R+Tree, LSM, etc.). - * The storage engine works with key/value pairs where both the key - * and the value are byte arrays of arbitrary length and with no restrictions on content. - * UnQLite come with two built-in KV storage engine: A Virtual Linear Hash (VLH) storage - * engine is used for persistent on-disk databases with O(1) lookup time and an in-memory - * hash-table or Red-black tree storage engine is used for in-memory databases. - * Future versions of UnQLite might add other built-in storage engines (i.e. LSM). - * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()] - * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE. - */ -struct unqlite_kv_engine -{ - const unqlite_kv_io *pIo; /* IO methods: MUST be first */ - /* Subclasses will typically add additional fields */ -}; -/* - * Key/Value Storage Engine Virtual Method Table. - * - * Key/Value storage engine methods is defined by an instance of the following - * object. - * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()] - * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE. - */ -struct unqlite_kv_methods -{ - const char *zName; /* Storage engine name [i.e. Hash, B+tree, LSM, R-tree, Mem, etc.]*/ - int szKv; /* 'unqlite_kv_engine' subclass size */ - int szCursor; /* 'unqlite_kv_cursor' subclass size */ - int iVersion; /* Structure version, currently 1 */ - /* Storage engine methods */ - int (*xInit)(unqlite_kv_engine *,int iPageSize); - void (*xRelease)(unqlite_kv_engine *); - int (*xConfig)(unqlite_kv_engine *,int op,va_list ap); - int (*xOpen)(unqlite_kv_engine *,pgno); - int (*xReplace)( - unqlite_kv_engine *, - const void *pKey,int nKeyLen, - const void *pData,unqlite_int64 nDataLen - ); - int (*xAppend)( - unqlite_kv_engine *, - const void *pKey,int nKeyLen, - const void *pData,unqlite_int64 nDataLen - ); - void (*xCursorInit)(unqlite_kv_cursor *); - int (*xSeek)(unqlite_kv_cursor *,const void *pKey,int nByte,int iPos); /* Mandatory */ - int (*xFirst)(unqlite_kv_cursor *); - int (*xLast)(unqlite_kv_cursor *); - int (*xValid)(unqlite_kv_cursor *); - int (*xNext)(unqlite_kv_cursor *); - int (*xPrev)(unqlite_kv_cursor *); - int (*xDelete)(unqlite_kv_cursor *); - int (*xKeyLength)(unqlite_kv_cursor *,int *); - int (*xKey)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); - int (*xDataLength)(unqlite_kv_cursor *,unqlite_int64 *); - int (*xData)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); - void (*xReset)(unqlite_kv_cursor *); - void (*xCursorRelease)(unqlite_kv_cursor *); -}; -/* - * UnQLite journal file suffix. - */ -#ifndef UNQLITE_JOURNAL_FILE_SUFFIX -#define UNQLITE_JOURNAL_FILE_SUFFIX "_unqlite_journal" -#endif -/* - * Call Context - Error Message Serverity Level. - * - * The following constans are the allowed severity level that can - * passed as the second argument to the [unqlite_context_throw_error()] or - * [unqlite_context_throw_error_format()] interfaces. - * Refer to the official documentation for additional information. - */ -#define UNQLITE_CTX_ERR 1 /* Call context error such as unexpected number of arguments, invalid types and so on. */ -#define UNQLITE_CTX_WARNING 2 /* Call context Warning */ -#define UNQLITE_CTX_NOTICE 3 /* Call context Notice */ -/* - * C-API-REF: Please refer to the official documentation for interfaces - * purpose and expected parameters. - */ - -/* Database Engine Handle */ -UNQLITE_APIEXPORT int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode); -UNQLITE_APIEXPORT int unqlite_config(unqlite *pDb,int nOp,...); -UNQLITE_APIEXPORT int unqlite_close(unqlite *pDb); - -/* Key/Value (KV) Store Interfaces */ -UNQLITE_APIEXPORT int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen); -UNQLITE_APIEXPORT int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen); -UNQLITE_APIEXPORT int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...); -UNQLITE_APIEXPORT int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...); -UNQLITE_APIEXPORT int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 /* in|out */*pBufLen); -UNQLITE_APIEXPORT int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey, - int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen); -UNQLITE_APIEXPORT int unqlite_kv_config(unqlite *pDb,int iOp,...); - -/* Document (JSON) Store Interfaces powered by the Jx9 Scripting Language */ -UNQLITE_APIEXPORT int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut); -UNQLITE_APIEXPORT int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut); -UNQLITE_APIEXPORT int unqlite_vm_config(unqlite_vm *pVm,int iOp,...); -UNQLITE_APIEXPORT int unqlite_vm_exec(unqlite_vm *pVm); -UNQLITE_APIEXPORT int unqlite_vm_reset(unqlite_vm *pVm); -UNQLITE_APIEXPORT int unqlite_vm_release(unqlite_vm *pVm); -UNQLITE_APIEXPORT int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData); -UNQLITE_APIEXPORT unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname); - -/* Cursor Iterator Interfaces */ -UNQLITE_APIEXPORT int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut); -UNQLITE_APIEXPORT int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur); -UNQLITE_APIEXPORT int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos); -UNQLITE_APIEXPORT int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte); -UNQLITE_APIEXPORT int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnData); -UNQLITE_APIEXPORT int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor); - -/* Manual Transaction Manager */ -UNQLITE_APIEXPORT int unqlite_begin(unqlite *pDb); -UNQLITE_APIEXPORT int unqlite_commit(unqlite *pDb); -UNQLITE_APIEXPORT int unqlite_rollback(unqlite *pDb); - -/* Utility interfaces */ -UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize); -UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize); -UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size); -UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb); - -/* In-process extending interfaces */ -UNQLITE_APIEXPORT int unqlite_create_function(unqlite_vm *pVm,const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData); -UNQLITE_APIEXPORT int unqlite_delete_function(unqlite_vm *pVm, const char *zName); -UNQLITE_APIEXPORT int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_delete_constant(unqlite_vm *pVm, const char *zName); - -/* On Demand Object allocation interfaces */ -UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm); -UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm); -UNQLITE_APIEXPORT int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue); -UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx); -UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_array(unqlite_context *pCtx); -UNQLITE_APIEXPORT void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue); - -/* Dynamically Typed Value Object Management Interfaces */ -UNQLITE_APIEXPORT int unqlite_value_int(unqlite_value *pVal, int iValue); -UNQLITE_APIEXPORT int unqlite_value_int64(unqlite_value *pVal, unqlite_int64 iValue); -UNQLITE_APIEXPORT int unqlite_value_bool(unqlite_value *pVal, int iBool); -UNQLITE_APIEXPORT int unqlite_value_null(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_double(unqlite_value *pVal, double Value); -UNQLITE_APIEXPORT int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen); -UNQLITE_APIEXPORT int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...); -UNQLITE_APIEXPORT int unqlite_value_reset_string_cursor(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_resource(unqlite_value *pVal, void *pUserData); -UNQLITE_APIEXPORT int unqlite_value_release(unqlite_value *pVal); - -/* Foreign Function Parameter Values */ -UNQLITE_APIEXPORT int unqlite_value_to_int(unqlite_value *pValue); -UNQLITE_APIEXPORT int unqlite_value_to_bool(unqlite_value *pValue); -UNQLITE_APIEXPORT unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue); -UNQLITE_APIEXPORT double unqlite_value_to_double(unqlite_value *pValue); -UNQLITE_APIEXPORT const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen); -UNQLITE_APIEXPORT void * unqlite_value_to_resource(unqlite_value *pValue); -UNQLITE_APIEXPORT int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict); - -/* Setting The Result Of A Foreign Function */ -UNQLITE_APIEXPORT int unqlite_result_int(unqlite_context *pCtx, int iValue); -UNQLITE_APIEXPORT int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue); -UNQLITE_APIEXPORT int unqlite_result_bool(unqlite_context *pCtx, int iBool); -UNQLITE_APIEXPORT int unqlite_result_double(unqlite_context *pCtx, double Value); -UNQLITE_APIEXPORT int unqlite_result_null(unqlite_context *pCtx); -UNQLITE_APIEXPORT int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen); -UNQLITE_APIEXPORT int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...); -UNQLITE_APIEXPORT int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue); -UNQLITE_APIEXPORT int unqlite_result_resource(unqlite_context *pCtx, void *pUserData); - -/* Dynamically Typed Value Object Query Interfaces */ -UNQLITE_APIEXPORT int unqlite_value_is_int(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_float(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_bool(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_string(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_null(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_numeric(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_callable(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_scalar(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_json_array(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_json_object(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_resource(unqlite_value *pVal); -UNQLITE_APIEXPORT int unqlite_value_is_empty(unqlite_value *pVal); - -/* JSON Array/Object Management Interfaces */ -UNQLITE_APIEXPORT unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte); -UNQLITE_APIEXPORT int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData); -UNQLITE_APIEXPORT int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue); -UNQLITE_APIEXPORT int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue); -UNQLITE_APIEXPORT int unqlite_array_count(unqlite_value *pArray); - -/* Call Context Handling Interfaces */ -UNQLITE_APIEXPORT int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen); -UNQLITE_APIEXPORT int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...); -UNQLITE_APIEXPORT int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr); -UNQLITE_APIEXPORT int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...); -UNQLITE_APIEXPORT unsigned int unqlite_context_random_num(unqlite_context *pCtx); -UNQLITE_APIEXPORT int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen); -UNQLITE_APIEXPORT void * unqlite_context_user_data(unqlite_context *pCtx); -UNQLITE_APIEXPORT int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData); -UNQLITE_APIEXPORT void * unqlite_context_peek_aux_data(unqlite_context *pCtx); -UNQLITE_APIEXPORT unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx); -UNQLITE_APIEXPORT const char * unqlite_function_name(unqlite_context *pCtx); - -/* Call Context Memory Management Interfaces */ -UNQLITE_APIEXPORT void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease); -UNQLITE_APIEXPORT void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte); -UNQLITE_APIEXPORT void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk); - -/* Global Library Management Interfaces */ -UNQLITE_APIEXPORT int unqlite_lib_config(int nConfigOp,...); -UNQLITE_APIEXPORT int unqlite_lib_init(void); -UNQLITE_APIEXPORT int unqlite_lib_shutdown(void); -UNQLITE_APIEXPORT int unqlite_lib_is_threadsafe(void); -UNQLITE_APIEXPORT const char * unqlite_lib_version(void); -UNQLITE_APIEXPORT const char * unqlite_lib_signature(void); -UNQLITE_APIEXPORT const char * unqlite_lib_ident(void); -UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void); - -#endif /* _UNQLITE_H_ */ \ No newline at end of file diff --git a/unqlite.h b/unqlite.h new file mode 120000 index 0000000..c9c5f07 --- /dev/null +++ b/unqlite.h @@ -0,0 +1 @@ +unqlite/unqlite.h \ No newline at end of file