mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #14871 from mjlbach/feature/lua-cjson-embedded
feat(lua): expose lua-cjson as vim.json
This commit is contained in:
commit
68c65b7732
211
src/cjson/fpconv.c
Normal file
211
src/cjson/fpconv.c
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
/* fpconv - Floating point conversion routines
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Mark Pulford <mark@kyne.com.au>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries
|
||||||
|
* with locale support will break when the decimal separator is a comma.
|
||||||
|
*
|
||||||
|
* fpconv_* will around these issues with a translation buffer if required.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "fpconv.h"
|
||||||
|
|
||||||
|
/* Workaround for MSVC */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define inline __inline
|
||||||
|
#define snprintf sprintf_s
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Lua CJSON assumes the locale is the same for all threads within a
|
||||||
|
* process and doesn't change after initialisation.
|
||||||
|
*
|
||||||
|
* This avoids the need for per thread storage or expensive checks
|
||||||
|
* for call. */
|
||||||
|
static char locale_decimal_point = '.';
|
||||||
|
|
||||||
|
/* In theory multibyte decimal_points are possible, but
|
||||||
|
* Lua CJSON only supports UTF-8 and known locales only have
|
||||||
|
* single byte decimal points ([.,]).
|
||||||
|
*
|
||||||
|
* localconv() may not be thread safe (=>crash), and nl_langinfo() is
|
||||||
|
* not supported on some platforms. Use sprintf() instead - if the
|
||||||
|
* locale does change, at least Lua CJSON won't crash. */
|
||||||
|
static void fpconv_update_locale(void)
|
||||||
|
{
|
||||||
|
char buf[8];
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%g", 0.5);
|
||||||
|
|
||||||
|
/* Failing this test might imply the platform has a buggy dtoa
|
||||||
|
* implementation or wide characters */
|
||||||
|
if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) {
|
||||||
|
fprintf(stderr, "Error: wide characters found or printf() bug.");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
locale_decimal_point = buf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for a valid number character: [-+0-9a-yA-Y.]
|
||||||
|
* Eg: -0.6e+5, infinity, 0xF0.F0pF0
|
||||||
|
*
|
||||||
|
* Used to find the probable end of a number. It doesn't matter if
|
||||||
|
* invalid characters are counted - strtod() will find the valid
|
||||||
|
* number if it exists. The risk is that slightly more memory might
|
||||||
|
* be allocated before a parse error occurs. */
|
||||||
|
static inline int valid_number_character(char ch)
|
||||||
|
{
|
||||||
|
char lower_ch;
|
||||||
|
|
||||||
|
if ('0' <= ch && ch <= '9')
|
||||||
|
return 1;
|
||||||
|
if (ch == '-' || ch == '+' || ch == '.')
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Hex digits, exponent (e), base (p), "infinity",.. */
|
||||||
|
lower_ch = ch | 0x20;
|
||||||
|
if ('a' <= lower_ch && lower_ch <= 'y')
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the size of the buffer required for a strtod locale
|
||||||
|
* conversion. */
|
||||||
|
static int strtod_buffer_size(const char *s)
|
||||||
|
{
|
||||||
|
const char *p = s;
|
||||||
|
|
||||||
|
while (valid_number_character(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
return p - s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Similar to strtod(), but must be passed the current locale's decimal point
|
||||||
|
* character. Guaranteed to be called at the start of any valid number in a string */
|
||||||
|
double fpconv_strtod(const char *nptr, char **endptr)
|
||||||
|
{
|
||||||
|
char localbuf[FPCONV_G_FMT_BUFSIZE];
|
||||||
|
char *buf, *endbuf, *dp;
|
||||||
|
int buflen;
|
||||||
|
double value;
|
||||||
|
|
||||||
|
/* System strtod() is fine when decimal point is '.' */
|
||||||
|
if (locale_decimal_point == '.')
|
||||||
|
return strtod(nptr, endptr);
|
||||||
|
|
||||||
|
buflen = strtod_buffer_size(nptr);
|
||||||
|
if (!buflen) {
|
||||||
|
/* No valid characters found, standard strtod() return */
|
||||||
|
*endptr = (char *)nptr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Duplicate number into buffer */
|
||||||
|
if (buflen >= FPCONV_G_FMT_BUFSIZE) {
|
||||||
|
/* Handle unusually large numbers */
|
||||||
|
buf = malloc(buflen + 1);
|
||||||
|
if (!buf) {
|
||||||
|
fprintf(stderr, "Out of memory");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* This is the common case.. */
|
||||||
|
buf = localbuf;
|
||||||
|
}
|
||||||
|
memcpy(buf, nptr, buflen);
|
||||||
|
buf[buflen] = 0;
|
||||||
|
|
||||||
|
/* Update decimal point character if found */
|
||||||
|
dp = strchr(buf, '.');
|
||||||
|
if (dp)
|
||||||
|
*dp = locale_decimal_point;
|
||||||
|
|
||||||
|
value = strtod(buf, &endbuf);
|
||||||
|
*endptr = (char *)&nptr[endbuf - buf];
|
||||||
|
if (buflen >= FPCONV_G_FMT_BUFSIZE)
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "fmt" must point to a buffer of at least 6 characters */
|
||||||
|
static void set_number_format(char *fmt, int precision)
|
||||||
|
{
|
||||||
|
int d1, d2, i;
|
||||||
|
|
||||||
|
assert(1 <= precision && precision <= 16);
|
||||||
|
|
||||||
|
/* Create printf format (%.14g) from precision */
|
||||||
|
d1 = precision / 10;
|
||||||
|
d2 = precision % 10;
|
||||||
|
fmt[0] = '%';
|
||||||
|
fmt[1] = '.';
|
||||||
|
i = 2;
|
||||||
|
if (d1) {
|
||||||
|
fmt[i++] = '0' + d1;
|
||||||
|
}
|
||||||
|
fmt[i++] = '0' + d2;
|
||||||
|
fmt[i++] = 'g';
|
||||||
|
fmt[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assumes there is always at least 32 characters available in the target buffer */
|
||||||
|
int fpconv_g_fmt(char *str, double num, int precision)
|
||||||
|
{
|
||||||
|
char buf[FPCONV_G_FMT_BUFSIZE];
|
||||||
|
char fmt[6];
|
||||||
|
int len;
|
||||||
|
char *b;
|
||||||
|
|
||||||
|
set_number_format(fmt, precision);
|
||||||
|
|
||||||
|
/* Pass through when decimal point character is dot. */
|
||||||
|
if (locale_decimal_point == '.')
|
||||||
|
return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num);
|
||||||
|
|
||||||
|
/* snprintf() to a buffer then translate for other decimal point characters */
|
||||||
|
len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);
|
||||||
|
|
||||||
|
/* Copy into target location. Translate decimal point if required */
|
||||||
|
b = buf;
|
||||||
|
do {
|
||||||
|
*str++ = (*b == locale_decimal_point ? '.' : *b);
|
||||||
|
} while(*b++);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fpconv_init()
|
||||||
|
{
|
||||||
|
fpconv_update_locale();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi:ai et sw=4 ts=4:
|
||||||
|
*/
|
22
src/cjson/fpconv.h
Normal file
22
src/cjson/fpconv.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* Lua CJSON floating point conversion routines */
|
||||||
|
|
||||||
|
/* Buffer required to store the largest string representation of a double.
|
||||||
|
*
|
||||||
|
* Longest double printed with %.14g is 21 characters long:
|
||||||
|
* -1.7976931348623e+308 */
|
||||||
|
# define FPCONV_G_FMT_BUFSIZE 32
|
||||||
|
|
||||||
|
#ifdef USE_INTERNAL_FPCONV
|
||||||
|
static inline void fpconv_init()
|
||||||
|
{
|
||||||
|
/* Do nothing - not required */
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
extern void fpconv_init(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int fpconv_g_fmt(char*, double, int);
|
||||||
|
extern double fpconv_strtod(const char*, char**);
|
||||||
|
|
||||||
|
/* vi:ai et sw=4 ts=4:
|
||||||
|
*/
|
1609
src/cjson/lua_cjson.c
Normal file
1609
src/cjson/lua_cjson.c
Normal file
File diff suppressed because it is too large
Load Diff
10
src/cjson/lua_cjson.h
Normal file
10
src/cjson/lua_cjson.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef CJSON_LUACJSON_H
|
||||||
|
#define CJSON_LUACJSON_H
|
||||||
|
|
||||||
|
#include "lua.h"
|
||||||
|
|
||||||
|
int lua_cjson_new(lua_State *l);
|
||||||
|
int luaopen_cjson(lua_State *l);
|
||||||
|
int luaopen_cjson_safe(lua_State *l);
|
||||||
|
|
||||||
|
#endif // CJSON_LUACJSON_H
|
251
src/cjson/strbuf.c
Normal file
251
src/cjson/strbuf.c
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
/* strbuf - String buffer routines
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "strbuf.h"
|
||||||
|
|
||||||
|
static void die(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
|
||||||
|
va_start(arg, fmt);
|
||||||
|
vfprintf(stderr, fmt, arg);
|
||||||
|
va_end(arg);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void strbuf_init(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
size = STRBUF_DEFAULT_SIZE;
|
||||||
|
else
|
||||||
|
size = len + 1; /* \0 terminator */
|
||||||
|
|
||||||
|
s->buf = NULL;
|
||||||
|
s->size = size;
|
||||||
|
s->length = 0;
|
||||||
|
s->increment = STRBUF_DEFAULT_INCREMENT;
|
||||||
|
s->dynamic = 0;
|
||||||
|
s->reallocs = 0;
|
||||||
|
s->debug = 0;
|
||||||
|
|
||||||
|
s->buf = malloc(size);
|
||||||
|
if (!s->buf)
|
||||||
|
die("Out of memory");
|
||||||
|
|
||||||
|
strbuf_ensure_null(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf_t *strbuf_new(int len)
|
||||||
|
{
|
||||||
|
strbuf_t *s;
|
||||||
|
|
||||||
|
s = malloc(sizeof(strbuf_t));
|
||||||
|
if (!s)
|
||||||
|
die("Out of memory");
|
||||||
|
|
||||||
|
strbuf_init(s, len);
|
||||||
|
|
||||||
|
/* Dynamic strbuf allocation / deallocation */
|
||||||
|
s->dynamic = 1;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strbuf_set_increment(strbuf_t *s, int increment)
|
||||||
|
{
|
||||||
|
/* Increment > 0: Linear buffer growth rate
|
||||||
|
* Increment < -1: Exponential buffer growth rate */
|
||||||
|
if (increment == 0 || increment == -1)
|
||||||
|
die("BUG: Invalid string increment");
|
||||||
|
|
||||||
|
s->increment = increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void debug_stats(strbuf_t *s)
|
||||||
|
{
|
||||||
|
if (s->debug) {
|
||||||
|
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
|
||||||
|
(long)s, s->reallocs, s->length, s->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If strbuf_t has not been dynamically allocated, strbuf_free() can
|
||||||
|
* be called any number of times strbuf_init() */
|
||||||
|
void strbuf_free(strbuf_t *s)
|
||||||
|
{
|
||||||
|
debug_stats(s);
|
||||||
|
|
||||||
|
if (s->buf) {
|
||||||
|
free(s->buf);
|
||||||
|
s->buf = NULL;
|
||||||
|
}
|
||||||
|
if (s->dynamic)
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
debug_stats(s);
|
||||||
|
|
||||||
|
strbuf_ensure_null(s);
|
||||||
|
|
||||||
|
buf = s->buf;
|
||||||
|
if (len)
|
||||||
|
*len = s->length;
|
||||||
|
|
||||||
|
if (s->dynamic)
|
||||||
|
free(s);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int calculate_new_size(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
int reqsize, newsize;
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
die("BUG: Invalid strbuf length requested");
|
||||||
|
|
||||||
|
/* Ensure there is room for optional NULL termination */
|
||||||
|
reqsize = len + 1;
|
||||||
|
|
||||||
|
/* If the user has requested to shrink the buffer, do it exactly */
|
||||||
|
if (s->size > reqsize)
|
||||||
|
return reqsize;
|
||||||
|
|
||||||
|
newsize = s->size;
|
||||||
|
if (s->increment < 0) {
|
||||||
|
/* Exponential sizing */
|
||||||
|
while (newsize < reqsize)
|
||||||
|
newsize *= -s->increment;
|
||||||
|
} else {
|
||||||
|
/* Linear sizing */
|
||||||
|
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
|
||||||
|
* optional termination). */
|
||||||
|
void strbuf_resize(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
int newsize;
|
||||||
|
|
||||||
|
newsize = calculate_new_size(s, len);
|
||||||
|
|
||||||
|
if (s->debug > 1) {
|
||||||
|
fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
|
||||||
|
(long)s, s->size, newsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->size = newsize;
|
||||||
|
s->buf = realloc(s->buf, s->size);
|
||||||
|
if (!s->buf)
|
||||||
|
die("Out of memory");
|
||||||
|
s->reallocs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strbuf_append_string(strbuf_t *s, const char *str)
|
||||||
|
{
|
||||||
|
int space, i;
|
||||||
|
|
||||||
|
space = strbuf_empty_length(s);
|
||||||
|
|
||||||
|
for (i = 0; str[i]; i++) {
|
||||||
|
if (space < 1) {
|
||||||
|
strbuf_resize(s, s->length + 1);
|
||||||
|
space = strbuf_empty_length(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->buf[s->length] = str[i];
|
||||||
|
s->length++;
|
||||||
|
space--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* strbuf_append_fmt() should only be used when an upper bound
|
||||||
|
* is known for the output string. */
|
||||||
|
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
int fmt_len;
|
||||||
|
|
||||||
|
strbuf_ensure_empty_length(s, len);
|
||||||
|
|
||||||
|
va_start(arg, fmt);
|
||||||
|
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
if (fmt_len < 0)
|
||||||
|
die("BUG: Unable to convert number"); /* This should never happen.. */
|
||||||
|
|
||||||
|
s->length += fmt_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* strbuf_append_fmt_retry() can be used when the there is no known
|
||||||
|
* upper bound for the output string. */
|
||||||
|
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
int fmt_len, try;
|
||||||
|
int empty_len;
|
||||||
|
|
||||||
|
/* If the first attempt to append fails, resize the buffer appropriately
|
||||||
|
* and try again */
|
||||||
|
for (try = 0; ; try++) {
|
||||||
|
va_start(arg, fmt);
|
||||||
|
/* Append the new formatted string */
|
||||||
|
/* fmt_len is the length of the string required, excluding the
|
||||||
|
* trailing NULL */
|
||||||
|
empty_len = strbuf_empty_length(s);
|
||||||
|
/* Add 1 since there is also space to store the terminating NULL. */
|
||||||
|
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
if (fmt_len <= empty_len)
|
||||||
|
break; /* SUCCESS */
|
||||||
|
if (try > 0)
|
||||||
|
die("BUG: length of formatted string changed");
|
||||||
|
|
||||||
|
strbuf_resize(s, s->length + fmt_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->length += fmt_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi:ai et sw=4 ts=4:
|
||||||
|
*/
|
159
src/cjson/strbuf.h
Normal file
159
src/cjson/strbuf.h
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/* strbuf - String buffer routines
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
/* Workaround for MSVC */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define inline __inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Size: Total bytes allocated to *buf
|
||||||
|
* Length: String length, excluding optional NULL terminator.
|
||||||
|
* Increment: Allocation increments when resizing the string buffer.
|
||||||
|
* Dynamic: True if created via strbuf_new()
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *buf;
|
||||||
|
int size;
|
||||||
|
int length;
|
||||||
|
int increment;
|
||||||
|
int dynamic;
|
||||||
|
int reallocs;
|
||||||
|
int debug;
|
||||||
|
} strbuf_t;
|
||||||
|
|
||||||
|
#ifndef STRBUF_DEFAULT_SIZE
|
||||||
|
#define STRBUF_DEFAULT_SIZE 1023
|
||||||
|
#endif
|
||||||
|
#ifndef STRBUF_DEFAULT_INCREMENT
|
||||||
|
#define STRBUF_DEFAULT_INCREMENT -2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initialise */
|
||||||
|
extern strbuf_t *strbuf_new(int len);
|
||||||
|
extern void strbuf_init(strbuf_t *s, int len);
|
||||||
|
extern void strbuf_set_increment(strbuf_t *s, int increment);
|
||||||
|
|
||||||
|
/* Release */
|
||||||
|
extern void strbuf_free(strbuf_t *s);
|
||||||
|
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
|
||||||
|
|
||||||
|
/* Management */
|
||||||
|
extern void strbuf_resize(strbuf_t *s, int len);
|
||||||
|
static int strbuf_empty_length(strbuf_t *s);
|
||||||
|
static int strbuf_length(strbuf_t *s);
|
||||||
|
static char *strbuf_string(strbuf_t *s, int *len);
|
||||||
|
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
|
||||||
|
static char *strbuf_empty_ptr(strbuf_t *s);
|
||||||
|
static void strbuf_extend_length(strbuf_t *s, int len);
|
||||||
|
|
||||||
|
/* Update */
|
||||||
|
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
|
||||||
|
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
|
||||||
|
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
|
||||||
|
extern void strbuf_append_string(strbuf_t *s, const char *str);
|
||||||
|
static void strbuf_append_char(strbuf_t *s, const char c);
|
||||||
|
static void strbuf_ensure_null(strbuf_t *s);
|
||||||
|
|
||||||
|
/* Reset string for before use */
|
||||||
|
static inline void strbuf_reset(strbuf_t *s)
|
||||||
|
{
|
||||||
|
s->length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int strbuf_allocated(strbuf_t *s)
|
||||||
|
{
|
||||||
|
return s->buf != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return bytes remaining in the string buffer
|
||||||
|
* Ensure there is space for a NULL terminator. */
|
||||||
|
static inline int strbuf_empty_length(strbuf_t *s)
|
||||||
|
{
|
||||||
|
return s->size - s->length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
if (len > strbuf_empty_length(s))
|
||||||
|
strbuf_resize(s, s->length + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *strbuf_empty_ptr(strbuf_t *s)
|
||||||
|
{
|
||||||
|
return s->buf + s->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_extend_length(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
s->length += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int strbuf_length(strbuf_t *s)
|
||||||
|
{
|
||||||
|
return s->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_append_char(strbuf_t *s, const char c)
|
||||||
|
{
|
||||||
|
strbuf_ensure_empty_length(s, 1);
|
||||||
|
s->buf[s->length++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
|
||||||
|
{
|
||||||
|
s->buf[s->length++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)
|
||||||
|
{
|
||||||
|
strbuf_ensure_empty_length(s, len);
|
||||||
|
memcpy(s->buf + s->length, c, len);
|
||||||
|
s->length += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
|
||||||
|
{
|
||||||
|
memcpy(s->buf + s->length, c, len);
|
||||||
|
s->length += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_ensure_null(strbuf_t *s)
|
||||||
|
{
|
||||||
|
s->buf[s->length] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *strbuf_string(strbuf_t *s, int *len)
|
||||||
|
{
|
||||||
|
if (len)
|
||||||
|
*len = s->length;
|
||||||
|
|
||||||
|
return s->buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi:ai et sw=4 ts=4:
|
||||||
|
*/
|
@ -87,8 +87,8 @@ file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src)
|
|||||||
|
|
||||||
file(GLOB NVIM_SOURCES *.c)
|
file(GLOB NVIM_SOURCES *.c)
|
||||||
file(GLOB NVIM_HEADERS *.h)
|
file(GLOB NVIM_HEADERS *.h)
|
||||||
file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c)
|
file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c)
|
||||||
file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h)
|
file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h)
|
||||||
|
|
||||||
foreach(subdir
|
foreach(subdir
|
||||||
os
|
os
|
||||||
@ -171,7 +171,7 @@ foreach(sfile ${CONV_SOURCES})
|
|||||||
message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)")
|
message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)")
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
# xdiff, mpack: inlined external project, we don't maintain it. #9306
|
# xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306
|
||||||
list(APPEND CONV_SOURCES ${EXTERNAL_SOURCES})
|
list(APPEND CONV_SOURCES ${EXTERNAL_SOURCES})
|
||||||
|
|
||||||
if(NOT MSVC)
|
if(NOT MSVC)
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "nvim/undo.h"
|
#include "nvim/undo.h"
|
||||||
#include "nvim/version.h"
|
#include "nvim/version.h"
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
|
#include "cjson/lua_cjson.h"
|
||||||
|
|
||||||
static int in_fast_callback = 0;
|
static int in_fast_callback = 0;
|
||||||
|
|
||||||
@ -531,6 +532,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
|||||||
lua_pushcfunction(lstate, &nlua_xdl_diff);
|
lua_pushcfunction(lstate, &nlua_xdl_diff);
|
||||||
lua_setfield(lstate, -2, "diff");
|
lua_setfield(lstate, -2, "diff");
|
||||||
|
|
||||||
|
lua_cjson_new(lstate);
|
||||||
|
lua_setfield(lstate, -2, "json");
|
||||||
|
|
||||||
lua_setglobal(lstate, "vim");
|
lua_setglobal(lstate, "vim");
|
||||||
|
|
||||||
{
|
{
|
||||||
|
133
test/functional/lua/json_spec.lua
Normal file
133
test/functional/lua/json_spec.lua
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
local clear = helpers.clear
|
||||||
|
local NIL = helpers.NIL
|
||||||
|
local exec_lua = helpers.exec_lua
|
||||||
|
local eq = helpers.eq
|
||||||
|
|
||||||
|
describe('vim.json.decode function', function()
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('parses null, true, false', function()
|
||||||
|
eq(NIL, exec_lua([[return vim.json.decode('null')]]))
|
||||||
|
eq(true, exec_lua([[return vim.json.decode('true')]]))
|
||||||
|
eq(false, exec_lua([[return vim.json.decode('false')]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('parses integer numbers', function()
|
||||||
|
eq(100000, exec_lua([[return vim.json.decode('100000')]]))
|
||||||
|
eq(-100000, exec_lua([[return vim.json.decode('-100000')]]))
|
||||||
|
eq(100000, exec_lua([[return vim.json.decode(' 100000 ')]]))
|
||||||
|
eq(-100000, exec_lua([[return vim.json.decode(' -100000 ')]]))
|
||||||
|
eq(0, exec_lua([[return vim.json.decode('0')]]))
|
||||||
|
eq(0, exec_lua([[return vim.json.decode('-0')]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('parses floating-point numbers', function()
|
||||||
|
-- This behavior differs from vim.fn.json_decode, which return '100000.0'
|
||||||
|
eq('100000', exec_lua([[return tostring(vim.json.decode('100000.0'))]]))
|
||||||
|
eq(100000.5, exec_lua([[return vim.json.decode('100000.5')]]))
|
||||||
|
eq(-100000.5, exec_lua([[return vim.json.decode('-100000.5')]]))
|
||||||
|
eq(-100000.5e50, exec_lua([[return vim.json.decode('-100000.5e50')]]))
|
||||||
|
eq(100000.5e50, exec_lua([[return vim.json.decode('100000.5e50')]]))
|
||||||
|
eq(100000.5e50, exec_lua([[return vim.json.decode('100000.5e+50')]]))
|
||||||
|
eq(-100000.5e-50, exec_lua([[return vim.json.decode('-100000.5e-50')]]))
|
||||||
|
eq(100000.5e-50, exec_lua([[return vim.json.decode('100000.5e-50')]]))
|
||||||
|
eq(100000e-50, exec_lua([[return vim.json.decode('100000e-50')]]))
|
||||||
|
eq(0.5, exec_lua([[return vim.json.decode('0.5')]]))
|
||||||
|
eq(0.005, exec_lua([[return vim.json.decode('0.005')]]))
|
||||||
|
eq(0.005, exec_lua([[return vim.json.decode('0.00500')]]))
|
||||||
|
eq(0.5, exec_lua([[return vim.json.decode('0.00500e+002')]]))
|
||||||
|
eq(0.00005, exec_lua([[return vim.json.decode('0.00500e-002')]]))
|
||||||
|
|
||||||
|
eq(-0.0, exec_lua([[return vim.json.decode('-0.0')]]))
|
||||||
|
eq(-0.0, exec_lua([[return vim.json.decode('-0.0e0')]]))
|
||||||
|
eq(-0.0, exec_lua([[return vim.json.decode('-0.0e+0')]]))
|
||||||
|
eq(-0.0, exec_lua([[return vim.json.decode('-0.0e-0')]]))
|
||||||
|
eq(-0.0, exec_lua([[return vim.json.decode('-0e-0')]]))
|
||||||
|
eq(-0.0, exec_lua([[return vim.json.decode('-0e-2')]]))
|
||||||
|
eq(-0.0, exec_lua([[return vim.json.decode('-0e+2')]]))
|
||||||
|
|
||||||
|
eq(0.0, exec_lua([[return vim.json.decode('0.0')]]))
|
||||||
|
eq(0.0, exec_lua([[return vim.json.decode('0.0e0')]]))
|
||||||
|
eq(0.0, exec_lua([[return vim.json.decode('0.0e+0')]]))
|
||||||
|
eq(0.0, exec_lua([[return vim.json.decode('0.0e-0')]]))
|
||||||
|
eq(0.0, exec_lua([[return vim.json.decode('0e-0')]]))
|
||||||
|
eq(0.0, exec_lua([[return vim.json.decode('0e-2')]]))
|
||||||
|
eq(0.0, exec_lua([[return vim.json.decode('0e+2')]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('parses containers', function()
|
||||||
|
eq({1}, exec_lua([[return vim.json.decode('[1]')]]))
|
||||||
|
eq({NIL, 1}, exec_lua([[return vim.json.decode('[null, 1]')]]))
|
||||||
|
eq({['1']=2}, exec_lua([[return vim.json.decode('{"1": 2}')]]))
|
||||||
|
eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}},
|
||||||
|
exec_lua([[return vim.json.decode('{"1": 2, "3": [{"4": {"5": [ [], 1]}}]}')]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('parses strings properly', function()
|
||||||
|
eq('\n', exec_lua([=[return vim.json.decode([["\n"]])]=]))
|
||||||
|
eq('', exec_lua([=[return vim.json.decode([[""]])]=]))
|
||||||
|
eq('\\/"\t\b\n\r\f', exec_lua([=[return vim.json.decode([["\\\/\"\t\b\n\r\f"]])]=]))
|
||||||
|
eq('/a', exec_lua([=[return vim.json.decode([["\/a"]])]=]))
|
||||||
|
-- Unicode characters: 2-byte, 3-byte
|
||||||
|
eq('«',exec_lua([=[return vim.json.decode([["«"]])]=]))
|
||||||
|
eq('ફ',exec_lua([=[return vim.json.decode([["ફ"]])]=]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('parses surrogate pairs properly', function()
|
||||||
|
eq('\240\144\128\128', exec_lua([[return vim.json.decode('"\\uD800\\uDC00"')]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('accepts all spaces in every position where space may be put', function()
|
||||||
|
local s = ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t'
|
||||||
|
local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s)
|
||||||
|
eq({key={'val', 'val2'}, key2=1}, exec_lua([[return vim.json.decode(...)]], str))
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('vim.json.encode function', function()
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('dumps strings', function()
|
||||||
|
eq('"Test"', exec_lua([[return vim.json.encode('Test')]]))
|
||||||
|
eq('""', exec_lua([[return vim.json.encode('')]]))
|
||||||
|
eq('"\\t"', exec_lua([[return vim.json.encode('\t')]]))
|
||||||
|
eq('"\\n"', exec_lua([[return vim.json.encode('\n')]]))
|
||||||
|
-- vim.fn.json_encode return \\u001B
|
||||||
|
eq('"\\u001b"', exec_lua([[return vim.json.encode('\27')]]))
|
||||||
|
eq('"þÿþ"', exec_lua([[return vim.json.encode('þÿþ')]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('dumps numbers', function()
|
||||||
|
eq('0', exec_lua([[return vim.json.encode(0)]]))
|
||||||
|
eq('10', exec_lua([[return vim.json.encode(10)]]))
|
||||||
|
eq('-10', exec_lua([[return vim.json.encode(-10)]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('dumps floats', function()
|
||||||
|
eq('10.5', exec_lua([[return vim.json.encode(10.5)]]))
|
||||||
|
eq('-10.5', exec_lua([[return vim.json.encode(-10.5)]]))
|
||||||
|
eq('-1e-05', exec_lua([[return vim.json.encode(-1e-5)]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('dumps lists', function()
|
||||||
|
eq('[]', exec_lua([[return vim.json.encode({})]]))
|
||||||
|
eq('[[]]', exec_lua([[return vim.json.encode({{}})]]))
|
||||||
|
eq('[[],[]]', exec_lua([[return vim.json.encode({{}, {}})]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('dumps dictionaries', function()
|
||||||
|
eq('{}', exec_lua([[return vim.json.encode(vim.empty_dict())]]))
|
||||||
|
eq('{"d":[]}', exec_lua([[return vim.json.encode({d={}})]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('dumps vim.NIL', function()
|
||||||
|
eq('null', exec_lua([[return vim.json.encode(vim.NIL)]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
Loading…
Reference in New Issue
Block a user