From commits-return-14510-archive-asf-public=cust-asf.ponee.io@apr.apache.org Sun Jul 8 13:42:17 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 2BCA1180630 for ; Sun, 8 Jul 2018 13:42:14 +0200 (CEST) Received: (qmail 72520 invoked by uid 500); 8 Jul 2018 11:42:14 -0000 Mailing-List: contact commits-help@apr.apache.org; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: Reply-To: dev@apr.apache.org List-Id: Delivered-To: mailing list commits@apr.apache.org Received: (qmail 72511 invoked by uid 99); 8 Jul 2018 11:42:14 -0000 Received: from Unknown (HELO svn01-us-west.apache.org) (209.188.14.144) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 08 Jul 2018 11:42:14 +0000 Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 7939E3A00B0 for ; Sun, 8 Jul 2018 11:42:13 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1835350 - in /apr/apr-util/branches/1.7.x: CHANGES build.conf include/apr_json.h json/ json/apr_json.c json/apr_json_decode.c json/apr_json_encode.c test/Makefile.in test/abts_tests.h test/testjson.c test/testutil.h Date: Sun, 08 Jul 2018 11:42:12 -0000 To: commits@apr.apache.org From: minfrin@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20180708114213.7939E3A00B0@svn01-us-west.apache.org> Author: minfrin Date: Sun Jul 8 11:42:12 2018 New Revision: 1835350 URL: http://svn.apache.org/viewvc?rev=1835350&view=rev Log: Backport r1835348: apr_json: Add support for encoding and decoding RFC8259 JSON. Submitted by: Moriyoshi Koizumi Added: apr/apr-util/branches/1.7.x/include/apr_json.h apr/apr-util/branches/1.7.x/json/ apr/apr-util/branches/1.7.x/json/apr_json.c apr/apr-util/branches/1.7.x/json/apr_json_decode.c apr/apr-util/branches/1.7.x/json/apr_json_encode.c apr/apr-util/branches/1.7.x/test/testjson.c Modified: apr/apr-util/branches/1.7.x/CHANGES apr/apr-util/branches/1.7.x/build.conf apr/apr-util/branches/1.7.x/test/Makefile.in apr/apr-util/branches/1.7.x/test/abts_tests.h apr/apr-util/branches/1.7.x/test/testutil.h Modified: apr/apr-util/branches/1.7.x/CHANGES URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.7.x/CHANGES?rev=1835350&r1=1835349&r2=1835350&view=diff ============================================================================== --- apr/apr-util/branches/1.7.x/CHANGES [utf-8] (original) +++ apr/apr-util/branches/1.7.x/CHANGES [utf-8] Sun Jul 8 11:42:12 2018 @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with APR-util 1.7.0 + *) apr_json: Add support for encoding and decoding RFC8259 JSON. + [Moriyoshi Koizumi ] + *) New apr_crypto_prng API and apr_crypto_random[_thread]_bytes() functions. [Yann Ylavic] Modified: apr/apr-util/branches/1.7.x/build.conf URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.7.x/build.conf?rev=1835350&r1=1835349&r2=1835350&view=diff ============================================================================== --- apr/apr-util/branches/1.7.x/build.conf (original) +++ apr/apr-util/branches/1.7.x/build.conf Sun Jul 8 11:42:12 2018 @@ -23,6 +23,7 @@ paths = dbm/sdbm/*.c encoding/*.c hooks/*.c + json/*.c ldap/apr_ldap_stub.c ldap/apr_ldap_url.c misc/*.c Added: apr/apr-util/branches/1.7.x/include/apr_json.h URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.7.x/include/apr_json.h?rev=1835350&view=auto ============================================================================== --- apr/apr-util/branches/1.7.x/include/apr_json.h (added) +++ apr/apr-util/branches/1.7.x/include/apr_json.h Sun Jul 8 11:42:12 2018 @@ -0,0 +1,248 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file apr_json.h + * @brief APR-UTIL JSON Library + */ +#ifndef APR_JSON_H +#define APR_JSON_H + +/** + * @defgroup APR_Util_JSON JSON Encoding and Decoding + * @ingroup APR_Util + * @{ + */ +#include "apr.h" +#include "apr_pools.h" +#include "apr_tables.h" +#include "apr_hash.h" +#include "apr_strings.h" +#include "apr_buckets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @package Apache JSON library + * + * RFC8259 compliant JSON encoding and decoding library. + * + * https://tools.ietf.org/html/rfc8259 + * + * This API generates UTF-8 encoded JSON, and writes it to the + * bucket brigade specified. All strings are verified as valid UTF-8 + * before processing, with invalid UTF-8 characters replaced. + * + * This API parses UTF-8 encoded JSON, and returns the result as + * a set of structures. All JSON strings are unescaped. Any bad + * characters or formatting will cause parsing to be terminated + * and an error returned, along with the offset of the error. + * + * Whitespace may be optionally preserved or ignored as required + * during generation and parsing. + * + * The ordering of object keys is preserved, allowing the decode and + * encode process to reproduce an identical result. This maintains + * stable behaviour during unit tests. + */ + +/** + * When passing a string to one of the encode functions, this value can be + * passed to indicate a string-valued key, and have the length computed + * automatically. + */ +#define APR_JSON_VALUE_STRING (-1) + +/** + * Flag indicating no special processing. + */ +#define APR_JSON_FLAGS_NONE 0 + +/** + * Flag indicating include whitespace. + */ +#define APR_JSON_FLAGS_WHITESPACE 1 + +/** + * A structure to hold a JSON object. + */ +typedef struct apr_json_object_t apr_json_object_t; + +/** + * Enum that represents the type of the given JSON value. + */ +typedef enum apr_json_type_e { + APR_JSON_OBJECT, + APR_JSON_ARRAY, + APR_JSON_STRING, + APR_JSON_LONG, + APR_JSON_DOUBLE, + APR_JSON_BOOLEAN, + APR_JSON_NULL +} apr_json_type_e; + +/** + * A structure to hold a UTF-8 encoded JSON string. + */ +typedef struct apr_json_string_t { + /** pointer to the string */ + const char *p; + /** string length */ + apr_size_t len; +} apr_json_string_t; + +/** + * A structure that holds a JSON value. + * + * Use apr_json_value_create() to allocate. + */ +typedef struct apr_json_value_t { + /** preceding whitespace, if any */ + const char *pre; + /** trailing whitespace, if any */ + const char *post; + /** type of the value */ + apr_json_type_e type; + /** actual value. which member is valid depends on type. */ + union { + /** JSON object */ + apr_json_object_t *object; + /** JSON array */ + apr_array_header_t *array; + /** JSON floating point value */ + double dnumber; + /** JSON long integer value */ + apr_int64_t lnumber; + /** JSON UTF-8 encoded string value */ + apr_json_string_t string; + /** JSON boolean value */ + int boolean; + } value; +} apr_json_value_t; + +/** + * A structure to hold a JSON object key value pair. + * + * Use apr_json_object_set() to allocate. + */ +typedef struct apr_json_kv_t { + /** Links to the rest of the kv pairs */ + APR_RING_ENTRY(apr_json_kv_t) link; + /** the key */ + apr_json_value_t *k; + /** the value */ + apr_json_value_t *v; +} apr_json_kv_t; + +/** + * A structure to hold a JSON object. + * + * Use apr_json_object_create() to allocate. + */ +typedef struct apr_json_object_t { + /** The key value pairs in the object are in this list */ + APR_RING_HEAD(apr_json_object_list_t, apr_json_kv_t) list; + /** JSON object */ + apr_hash_t *hash; +} apr_json_object_t; + +/** + * Allocate and return a apr_json_value_t structure. + * + * @param pool The pool to allocate from. + * @return The apr_json_value_t structure. + */ +APU_DECLARE(apr_json_value_t *) apr_json_value_create(apr_pool_t *pool) + __attribute__((nonnull(1))); + +/** + * Allocate and return a apr_json_object_t structure. + * + * @param pool The pool to allocate from. + * @return The apr_json_object_t structure. + */ +APU_DECLARE(apr_json_object_t *) apr_json_object_create(apr_pool_t *pool) + __attribute__((nonnull(1))); + +/** + * Associate a value with a key in a JSON object. + * @param obj The JSON object. + * @param key Pointer to the key. + * @param val Value to associate with the key. + * @remark If the value is NULL the key value pair is deleted. + */ +APU_DECLARE(void) apr_json_object_set(apr_json_object_t *obj, + apr_json_value_t *key, apr_json_value_t *val, + apr_pool_t *pool) __attribute__((nonnull(1, 2, 4))); + +/** + * Look up the value associated with a key in a JSON object. + * @param ht The hash table + * @param key Pointer to the key + * @return Returns NULL if the key is not present. + */ +APU_DECLARE(apr_json_kv_t *) + apr_json_object_get(apr_json_object_t *obj, const char *key) + __attribute__((nonnull(1, 2))); + +/** + * Decode utf8-encoded JSON string into apr_json_value_t. + * @param retval the result + * @param injson utf8-encoded JSON string. + * @param size length of the input string. + * @param offset number of characters processed. + * @param flags set to APR_JSON_FLAGS_WHITESPACE to preserve whitespace, + * or APR_JSON_FLAGS_NONE to filter whitespace. + * @param level maximum nesting level we are prepared to decode. + * @param pool pool used to allocate the result from. + * @return APR_SUCCESS on success, APR_EOF if the JSON text is truncated. + * APR_BADCH when a decoding error has occurred (the location of the error + * is at offset), APR_EINVAL if the level has been exceeded, or + * APR_ENOTIMPL on platforms where not implemented. + */ +APU_DECLARE(apr_status_t) apr_json_decode(apr_json_value_t ** retval, + const char *injson, apr_ssize_t size, apr_off_t * offset, + int flags, int level, apr_pool_t * pool) + __attribute__((nonnull(1, 2, 7))); + +/** + * Encode data represented as apr_json_value_t to utf8-encoded JSON string + * and append it to the specified brigade. + * + * All JSON strings are checked for invalid UTF-8 character sequences, + * and if found invalid sequences are replaced with the replacement + * character "�" (U+FFFD). + * + * @param brigade brigade the result will be appended to. + * @param flush optional flush function for the brigade. Can be NULL. + * @param ctx optional contaxt for the flush function. Can be NULL. + * @param json the JSON data. + * @param flags set to APR_JSON_FLAGS_WHITESPACE to preserve whitespace, + * or APR_JSON_FLAGS_NONE to filter whitespace. + * @param pool pool used to allocate the buckets from. + * @return APR_SUCCESS on success, or APR_ENOTIMPL on platforms where not + * implemented. + */ +APU_DECLARE(apr_status_t) apr_json_encode(apr_bucket_brigade * brigade, + apr_brigade_flush flush, void *ctx, const apr_json_value_t * json, + int flags, apr_pool_t * pool) __attribute__((nonnull(1, 4, 6))); + +#ifdef __cplusplus +} +#endif +/** @} */ +#endif /* APR_JSON_H */ Added: apr/apr-util/branches/1.7.x/json/apr_json.c URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.7.x/json/apr_json.c?rev=1835350&view=auto ============================================================================== --- apr/apr-util/branches/1.7.x/json/apr_json.c (added) +++ apr/apr-util/branches/1.7.x/json/apr_json.c Sun Jul 8 11:42:12 2018 @@ -0,0 +1,74 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "apr_json.h" + +#define APR_JSON_OBJECT_INSERT_TAIL(o, e) do { \ + apr_json_kv_t *ap__b = (e); \ + APR_RING_INSERT_TAIL(&(o)->list, ap__b, apr_json_kv_t, link); \ + APR_RING_CHECK_CONSISTENCY(&(o)->list, apr_json_kv_t, link); \ + } while (0) + +apr_json_value_t *apr_json_value_create(apr_pool_t *pool) +{ + return apr_pcalloc(pool, sizeof(apr_json_value_t)); +} + +apr_json_object_t *apr_json_object_create(apr_pool_t *pool) +{ + apr_json_object_t *object = apr_pcalloc(pool, + sizeof(apr_json_object_t)); + APR_RING_INIT(&object->list, apr_json_kv_t, link); + object->hash = apr_hash_make(pool); + + return object; +} + +void apr_json_object_set(apr_json_object_t *object, apr_json_value_t *key, + apr_json_value_t *val, apr_pool_t *pool) +{ + apr_json_kv_t *kv; + + kv = apr_hash_get(object->hash, key->value.string.p, key->value.string.len); + + if (!val) { + if (kv) { + apr_hash_set(object->hash, key->value.string.p, key->value.string.len, + NULL); + APR_RING_REMOVE((kv), link); + } + return; + } + + if (!kv) { + kv = apr_palloc(pool, sizeof(apr_json_kv_t)); + APR_RING_ELEM_INIT(kv, link); + APR_JSON_OBJECT_INSERT_TAIL(object, kv); + apr_hash_set(object->hash, key->value.string.p, key->value.string.len, + kv); + } + + kv->k = key; + kv->v = val; +} + +apr_json_kv_t *apr_json_object_get(apr_json_object_t *object, const char *key) +{ + return apr_hash_get(object->hash, key, APR_HASH_KEY_STRING); +} Added: apr/apr-util/branches/1.7.x/json/apr_json_decode.c URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.7.x/json/apr_json_decode.c?rev=1835350&view=auto ============================================================================== --- apr/apr-util/branches/1.7.x/json/apr_json_decode.c (added) +++ apr/apr-util/branches/1.7.x/json/apr_json_decode.c Sun Jul 8 11:42:12 2018 @@ -0,0 +1,819 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "apr_json.h" + +#if !APR_CHARSET_EBCDIC + +typedef struct _json_link_t { + apr_json_value_t *value; + struct _json_link_t *next; +} json_link_t; + +typedef struct apr_json_scanner_t { + apr_pool_t *pool; + const char *p; + const char *e; + int flags; + int level; +} apr_json_scanner_t; + +static apr_status_t apr_json_decode_value(apr_json_scanner_t * self, apr_json_value_t ** retval); + +/* stolen from mod_mime_magic.c :) */ +/* Single hex char to int; -1 if not a hex char. */ +static int hex_to_int(int c) +{ + if (isdigit(c)) + return c - '0'; + if ((c >= 'a') && (c <= 'f')) + return c + 10 - 'a'; + if ((c >= 'A') && (c <= 'F')) + return c + 10 - 'A'; + return -1; +} + +static apr_ssize_t ucs4_to_utf8(char *out, int code) +{ + if (code < 0x00000080) { + out[0] = code; + return 1; + } + else if (code < 0x00000800) { + out[0] = 0xc0 + (code >> 6); + out[1] = 0x80 + (code & 0x3f); + return 2; + } + else if (code < 0x00010000) { + out[0] = 0xe0 + (code >> 12); + out[1] = 0x80 + ((code >> 6) & 0x3f); + out[2] = 0x80 + (code & 0x3f); + return 3; + } + else if (code < 0x00200000) { + out[0] = 0xd0 + (code >> 18); + out[1] = 0x80 + ((code >> 12) & 0x3f); + out[2] = 0x80 + ((code >> 6) & 0x3f); + out[3] = 0x80 + (code & 0x3F); + return 4; + } + return 0; +} + +static apr_status_t apr_json_decode_string(apr_json_scanner_t * self, apr_json_string_t * retval) +{ + apr_status_t status = APR_SUCCESS; + apr_json_string_t string; + const char *p = self->p; + const char *e; + char *q; + + if (self->p >= self->e) { + status = APR_EOF; + goto out; + } + + self->p++; /* eat the leading '"' */ + + /* advance past the \ " */ + string.len = 0; + for (p = self->p, e = self->e; p < e;) { + if (*p == '"') + break; + else if (*p == '\\') { + p++; + if (p >= e) { + status = APR_EOF; + goto out; + } + if (*p == 'u') { + if (p + 4 >= e) { + status = APR_EOF; + goto out; + } + p += 5; + string.len += 4;/* an UTF-8 character spans at most 4 bytes */ + break; + } + else { + string.len++; + p++; + } + } + else { + string.len++; + p++; + } + } + + string.p = q = apr_pcalloc(self->pool, string.len + 1); + e = p; + +#define VALIDATE_UTF8_SUCCEEDING_BYTE(p) \ + if (*(unsigned char *)(p) < 0x80 || *(unsigned char *)(p) >= 0xc0) { \ + status = APR_BADCH; \ + goto out; \ + } + + for (p = self->p; p < e;) { + switch (*(unsigned char *)p) { + case '\\': + p++; + switch (*p) { + case 'u': + /* THIS IS REQUIRED TO BE A 4 DIGIT HEX NUMBER */ + { + int cp = 0; + while (p < e) { + int d = hex_to_int(*p); + if (d < 0) { + status = APR_BADCH; + goto out; + } + cp = (cp << 4) | d; + p++; + } + if (cp >= 0xd800 && cp < 0xdc00) { + /* surrogate pair */ + int sc = 0; + if (p + 6 > e) { + status = APR_EOF; + goto out; + } + if (p[0] != '\\' && p[1] != 'u') { + status = APR_BADCH; + goto out; + } + while (p < e) { + int d = hex_to_int(*p); + if (d < 0) { + status = APR_BADCH; + goto out; + } + sc = (sc << 4) | d; + p++; + } + cp = ((cp & 0x3ff) << 10) | (sc & 0x3ff); + if ((cp >= 0xd800 && cp < 0xe000) || (cp >= 0x110000)) { + status = APR_BADCH; + goto out; + } + } + else if (cp >= 0xdc00 && cp < 0xe000) { + status = APR_BADCH; + goto out; + } + q += ucs4_to_utf8(q, cp); + } + break; + case '\\': + *q++ = '\\'; + p++; + break; + case '/': + *q++ = '/'; + p++; + break; + case 'n': + *q++ = '\n'; + p++; + break; + case 'r': + *q++ = '\r'; + p++; + break; + case 't': + *q++ = '\t'; + p++; + break; + case 'f': + *q++ = '\f'; + p++; + break; + case 'b': + *q++ = '\b'; + p++; + break; + case '"': + *q++ = '"'; + p++; + break; + default: + status = APR_BADCH; + goto out; + } + break; + + case 0xc0: + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc4: + case 0xc5: + case 0xc6: + case 0xc7: + case 0xc8: + case 0xc9: + case 0xca: + case 0xcb: + case 0xcc: + case 0xcd: + case 0xce: + case 0xcf: + case 0xd0: + case 0xd1: + case 0xd2: + case 0xd3: + case 0xd4: + case 0xd5: + case 0xd6: + case 0xd7: + case 0xd8: + case 0xd9: + case 0xda: + case 0xdb: + case 0xdc: + case 0xdd: + case 0xde: + case 0xdf: + if (p + 1 >= e) { + status = APR_EOF; + goto out; + } + *q++ = *p++; + VALIDATE_UTF8_SUCCEEDING_BYTE(p); + *q++ = *p++; + break; + + case 0xe0: + case 0xe1: + case 0xe2: + case 0xe3: + case 0xe4: + case 0xe5: + case 0xe6: + case 0xe7: + case 0xe8: + case 0xe9: + case 0xea: + case 0xeb: + case 0xec: + case 0xed: + case 0xee: + case 0xef: + if (p + 2 >= e) { + status = APR_EOF; + goto out; + } + *q++ = *p++; + VALIDATE_UTF8_SUCCEEDING_BYTE(p); + *q++ = *p++; + VALIDATE_UTF8_SUCCEEDING_BYTE(p); + *q++ = *p++; + break; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + if (p + 3 >= e) { + status = APR_EOF; + goto out; + } + if (((unsigned char *)p)[0] >= 0xf5 || ((unsigned char *)p)[1] >= 0x90) { + status = APR_BADCH; + goto out; + } + *q++ = *p++; + VALIDATE_UTF8_SUCCEEDING_BYTE(p); + *q++ = *p++; + VALIDATE_UTF8_SUCCEEDING_BYTE(p); + *q++ = *p++; + VALIDATE_UTF8_SUCCEEDING_BYTE(p); + *q++ = *p++; + break; + + case 0xf8: + case 0xf9: + case 0xfa: + case 0xfb: + if (p + 4 >= e) { + status = APR_EOF; + goto out; + } + status = APR_BADCH; + goto out; + + case 0xfc: + case 0xfd: + if (p + 5 >= e) { + status = APR_EOF; + goto out; + } + status = APR_BADCH; + goto out; + + default: + *q++ = *p++; + break; + } + } +#undef VALIDATE_UTF8_SUCCEEDING_BYTE + p++; /* eat the trailing '"' */ + *retval = string; +out: + self->p = p; + return status; +} + +static apr_status_t apr_json_decode_array(apr_json_scanner_t * self, + apr_array_header_t ** retval) +{ + apr_status_t status = APR_SUCCESS; + apr_pool_t *link_pool = NULL; + json_link_t *head = NULL, *tail = NULL; + apr_size_t count = 0; + + if ((status = apr_pool_create(&link_pool, self->pool))) + return status; + + if (self->p >= self->e) { + status = APR_EOF; + goto out; + } + + self->level--; + if (self->level < 0) { + return APR_EINVAL; + } + + self->p++; /* toss of the leading [ */ + + for (;;) { + apr_json_value_t *element; + json_link_t *new_node; + + if (self->p == self->e) { + status = APR_EOF; + goto out; + } + + if (*self->p == ']') { + self->p++; + break; + } + + if (APR_SUCCESS != (status = apr_json_decode_value(self, &element))) { + goto out; + } + + new_node = apr_pcalloc(link_pool, sizeof(json_link_t)); + new_node->value = element; + if (tail) { + tail->next = new_node; + } + else { + head = new_node; + } + tail = new_node; + count++; + + if (self->p == self->e) { + status = APR_EOF; + goto out; + } + + if (*self->p == ',') { + self->p++; + } + else if (*self->p != ']') { + status = APR_BADCH; + goto out; + } + } + + { + json_link_t *node; + apr_array_header_t *array = apr_array_make(self->pool, count, sizeof(apr_json_value_t *)); + for (node = head; node; node = node->next) { + *((apr_json_value_t **) (apr_array_push(array))) = node->value; + } + *retval = array; + } + + self->level++; + +out: + if (link_pool) { + apr_pool_destroy(link_pool); + } + return status; +} + +static apr_status_t apr_json_decode_object(apr_json_scanner_t * self, + apr_json_object_t ** retval) +{ + apr_status_t status = APR_SUCCESS; + + apr_json_object_t *object = apr_json_object_create(self->pool); + + if (self->p >= self->e) { + return APR_EOF; + } + + self->level--; + if (self->level < 0) { + return APR_EINVAL; + } + + self->p++; /* toss of the leading { */ + + for (;;) { + apr_json_value_t *key; + apr_json_value_t *value; + + if (self->p == self->e) { + status = APR_EOF; + goto out; + } + + if (*self->p == '}') { + self->p++; + break; + } + + if ((status = apr_json_decode_value(self, &key))) + goto out; + + if (key->type != APR_JSON_STRING) { + status = APR_BADCH; + goto out; + } + + if (self->p == self->e) { + status = APR_EOF; + goto out; + } + + if (*self->p != ':') { + status = APR_BADCH; + goto out; + } + + self->p++; /* eat the ':' */ + + if (self->p == self->e) { + status = APR_EOF; + goto out; + } + + if ((status = apr_json_decode_value(self, &value))) + goto out; + + apr_json_object_set(object, key, value, self->pool); + + if (self->p == self->e) { + status = APR_EOF; + goto out; + } + + if (*self->p == ',') { + self->p++; + } + else if (*self->p != '}') { + status = APR_BADCH; + goto out; + } + } + + self->level++; + + *retval = object; +out: + return status; +} + +static apr_status_t apr_json_decode_boolean(apr_json_scanner_t * self, int *retval) +{ + if (self->p >= self->e) + return APR_EOF; + + if (self->e - self->p >= 4 && strncmp("true", self->p, 4) == 0 && + (self->p == self->e || + (!isalnum(((unsigned char *)self->p)[4]) && + ((unsigned char *)self->p)[4] != '_'))) { + self->p += 4; + *retval = 1; + return APR_SUCCESS; + } + else if (self->e - self->p >= 5 && strncmp("false", self->p, 5) == 0 && + (self->p == self->e || + (!isalnum(((unsigned char *)self->p)[5]) && + ((unsigned char *)self->p)[5] != '_'))) { + self->p += 5; + *retval = 0; + return APR_SUCCESS; + } + return APR_BADCH; +} + +static apr_status_t apr_json_decode_number(apr_json_scanner_t * self, apr_json_value_t * retval) +{ + apr_status_t status = APR_SUCCESS; + int treat_as_float = 0, exp_occurred = 0; + const char *p = self->p, *e = self->e; + + if (p >= e) + return APR_EOF; + + { + unsigned char c = *(unsigned char *)p; + if (c == '-') { + p++; + if (p >= e) + return APR_EOF; + c = *(unsigned char *)p; + } + if (c == '.') { + p++; + if (p >= e) + return APR_EOF; + c = *(unsigned char *)p; + treat_as_float = 1; + } + if (!isdigit(c)) { + status = APR_BADCH; + goto out; + } + p++; + } + + if (!treat_as_float) { + while (p < e) { + unsigned char c = *(unsigned char *)p; + if (c == 'e' || c == 'E') { + p++; + if (p >= e) + return APR_EOF; + c = *(unsigned char *)p; + if (c == '-') { + p++; + if (p >= e) + return APR_EOF; + c = *(unsigned char *)p; + } + if (!isdigit(c)) { + status = APR_BADCH; + goto out; + } + treat_as_float = 1; + exp_occurred = 1; + break; + } + else if (c == '.') { + p++; + treat_as_float = 1; + break; + } + else if (!isdigit(c)) + break; + p++; + } + } + else { + while (p < e) { + unsigned char c = *(unsigned char *)p; + if (c == 'e' || c == 'E') { + p++; + if (p >= e) + return APR_EOF; + c = *(unsigned char *)p; + if (c == '-') { + p++; + if (p >= e) + return APR_EOF; + c = *(unsigned char *)p; + } + if (!isdigit(c)) { + status = APR_BADCH; + goto out; + } + exp_occurred = 1; + break; + } + else if (!isdigit(c)) + break; + p++; + } + } + + if (treat_as_float) { + if (!exp_occurred) { + while (p < e) { + unsigned char c = *(unsigned char *)p; + if (c == 'e' || c == 'E') { + p++; + if (p >= e) + return APR_EOF; + c = *(unsigned char *)p; + if (c == '-') { + p++; + if (p >= e) + return APR_EOF; + c = *(unsigned char *)p; + } + if (!isdigit(c)) { + status = APR_BADCH; + goto out; + } + exp_occurred = 1; + break; + } + else if (!isdigit(c)) + break; + p++; + } + } + if (exp_occurred) { + if (p >= e || !isdigit(*(unsigned char *)p)) + return APR_EOF; + while (++p < e && isdigit(*(unsigned char *)p)); + } + } + + if (treat_as_float) { + retval->type = APR_JSON_DOUBLE; + retval->value.dnumber = strtod(self->p, NULL); + } + else { + retval->type = APR_JSON_LONG; + retval->value.lnumber = strtol(self->p, NULL, 10); + } + +out: + self->p = p; + return status; +} + +static apr_status_t apr_json_decode_null(apr_json_scanner_t * self) +{ + if (self->e - self->p >= 4 && strncmp("null", self->p, 4) == 0 && + (self->p == self->e || + (!isalnum(((unsigned char *)self->p)[4]) && + ((unsigned char *)self->p)[4] != '_'))) { + self->p += 4; + return APR_SUCCESS; + } + return APR_BADCH; +} + +static apr_status_t apr_json_decode_space(apr_json_scanner_t * self, + const char **space) +{ + const char *p = self->p; + char *s; + int len = 0; + + *space = NULL; + + if (self->p >= self->e) { + return APR_SUCCESS; + } + + while (p < self->e && isspace(*(unsigned char *)p)) { + p++; + len++; + } + + if (self->flags & APR_JSON_FLAGS_WHITESPACE) { + if (len) { + *space = s = apr_palloc(self->pool, len + 1); + + while (self->p < self->e && isspace(*(unsigned char *) self->p)) { + *s++ = *self->p++; + } + *s = 0; + + } + } else { + self->p = p; + } + + return APR_SUCCESS; +} + +static apr_status_t apr_json_decode_value(apr_json_scanner_t * self, apr_json_value_t ** retval) +{ + apr_json_value_t value; + apr_status_t status = APR_SUCCESS; + + status = apr_json_decode_space(self, &value.pre); + + if (status == APR_SUCCESS) { + switch (*(unsigned char *) self->p) { + case '"': + value.type = APR_JSON_STRING; + status = apr_json_decode_string(self, &value.value.string); + break; + case '[': + value.type = APR_JSON_ARRAY; + status = apr_json_decode_array(self, &value.value.array); + break; + case '{': + value.type = APR_JSON_OBJECT; + status = apr_json_decode_object(self, &value.value.object); + break; + case 'n': + value.type = APR_JSON_NULL; + status = apr_json_decode_null(self); + break; + case 't': + case 'f': + value.type = APR_JSON_BOOLEAN; + status = apr_json_decode_boolean(self, &value.value.boolean); + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + status = apr_json_decode_number(self, &value); + break; + default: + status = APR_BADCH; + } + } + + if (status == APR_SUCCESS) { + status = apr_json_decode_space(self, &value.post); + } + + if (status == APR_SUCCESS) { + *retval = apr_json_value_create(self->pool); + **retval = value; + } + return status; +} + +apr_status_t apr_json_decode(apr_json_value_t ** retval, const char *injson, + apr_ssize_t injson_size, apr_off_t * offset, int flags, int level, + apr_pool_t * pool) +{ + apr_status_t status; + apr_json_scanner_t scanner; + + scanner.p = injson; + scanner.e = injson + + (injson_size == APR_JSON_VALUE_STRING ? strlen(injson) : injson_size); + scanner.pool = pool; + scanner.flags = flags; + scanner.level = level; + + if (APR_SUCCESS == (status = apr_json_decode_value(&scanner, retval))) { + if (scanner.p != scanner.e) { + /* trailing craft */ + status = APR_BADCH; + } + } + + if (offset) { + *offset = scanner.p - injson; + } + + return status; +} + +#else +/* we do not yet support JSON on EBCDIC platforms, but will do in future */ +apr_status_t apr_json_decode(apr_json_value_t ** retval, const char *injson, + apr_size_t injson_size, apr_pool_t * pool) +{ + return APR_ENOTIMPL; +} +#endif Added: apr/apr-util/branches/1.7.x/json/apr_json_encode.c URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.7.x/json/apr_json_encode.c?rev=1835350&view=auto ============================================================================== --- apr/apr-util/branches/1.7.x/json/apr_json_encode.c (added) +++ apr/apr-util/branches/1.7.x/json/apr_json_encode.c Sun Jul 8 11:42:12 2018 @@ -0,0 +1,300 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr_json.h" + +#if !APR_CHARSET_EBCDIC + +typedef struct apr_json_serializer_t { + apr_pool_t *pool; + apr_bucket_brigade *brigade; + apr_brigade_flush flush; + void *ctx; + int flags; +} apr_json_serializer_t; + +static apr_status_t apr_json_encode_value(apr_json_serializer_t * self, + const apr_json_value_t * value); + +static apr_status_t apr_json_brigade_write(apr_json_serializer_t * self, + const char *chunk, apr_size_t chunk_len, const char *escaped) +{ + apr_status_t status; + + status = apr_brigade_write(self->brigade, self->flush, self->ctx, chunk, chunk_len); + if (APR_SUCCESS == status) { + status = apr_brigade_puts(self->brigade, self->flush, self->ctx, escaped); + } + + return status; +} + +static apr_status_t apr_json_brigade_printf(apr_json_serializer_t * self, + const char *chunk, apr_size_t chunk_len, const char *fmt, ...) +{ + va_list ap; + apr_status_t status; + + status = apr_brigade_write(self->brigade, self->flush, self->ctx, chunk, + chunk_len); + if (APR_SUCCESS == status) { + va_start(ap, fmt); + status = apr_brigade_vprintf(self->brigade, self->flush, self->ctx, fmt, + ap); + va_end(ap); + } + + return status; +} + +static apr_status_t apr_json_encode_string(apr_json_serializer_t * self, + const apr_json_string_t * string) +{ + apr_status_t status; + const char *p, *e, *chunk; + const char invalid[4] = { 0xEF, 0xBF, 0xBD, 0x00 }; + unsigned char c; + + status = apr_brigade_putc(self->brigade, self->flush, self->ctx, '\"'); + if (APR_SUCCESS != status) { + return status; + } + + for (p = chunk = string->p, e = string->p + string->len; p < e; p++) { + switch (*p) { + case '\n': + status = apr_json_brigade_write(self, chunk, p - chunk, "\\n"); + chunk = p + 1; + break; + case '\r': + status = apr_json_brigade_write(self, chunk, p - chunk, "\\r"); + chunk = p + 1; + break; + case '\t': + status = apr_json_brigade_write(self, chunk, p - chunk, "\\t"); + chunk = p + 1; + break; + case '\b': + status = apr_json_brigade_write(self, chunk, p - chunk, "\\b"); + chunk = p + 1; + break; + case '\f': + status = apr_json_brigade_write(self, chunk, p - chunk, "\\f"); + chunk = p + 1; + break; + case '\\': + status = apr_json_brigade_write(self, chunk, p - chunk, "\\\\"); + chunk = p + 1; + break; + case '"': + status = apr_json_brigade_write(self, chunk, p - chunk, "\\\""); + chunk = p + 1; + break; + default: + c = (unsigned char)(*p); + apr_size_t left = e - p; + if (c < 0x20) { + status = apr_json_brigade_printf(self, chunk, p - chunk, + "\\u%04x", c); + chunk = p + 1; + } + else if (((c >> 7) == 0x00)) { + /* 1 byte */ + } + else if (left > 1 && ((c >> 5) == 0x06) && p[1]) { + /* 2 bytes */ + if (left < 2 || (p[1] >> 6) != 0x02) { + status = apr_json_brigade_write(self, chunk, p - chunk, + invalid); + chunk = p + 1; + } + } + else if (((c >> 4) == 0x0E)) { + /* 3 bytes */ + if (left < 3 || (p[1] >> 6) != 0x02 || (p[2] >> 6) != 0x02) { + status = apr_json_brigade_write(self, chunk, p - chunk, + invalid); + chunk = p + 1; + } + } + else if ((c >> 3) == 0x1E) { + /* 4 bytes */ + if (left < 4 || (p[1] >> 6) != 0x02 || (p[2] >> 6) != 0x02 || (p[3] >> 6) != 0x02) { + status = apr_json_brigade_write(self, chunk, p - chunk, + invalid); + chunk = p + 1; + } + } + else { + status = apr_json_brigade_write(self, chunk, p - chunk, + invalid); + chunk = p + 1; + } + break; + } + + if (APR_SUCCESS != status) { + return status; + } + } + + if (chunk < p) { + status = apr_brigade_write(self->brigade, self->flush, self->ctx, chunk, p - chunk); + if (APR_SUCCESS != status) { + return status; + } + } + + return apr_brigade_putc(self->brigade, self->flush, self->ctx, '\"'); +} + + +static apr_status_t apr_json_encode_array(apr_json_serializer_t * self, apr_array_header_t * array) +{ + apr_status_t status; + apr_size_t i; + + status = apr_brigade_putc(self->brigade, self->flush, self->ctx, '['); + if (APR_SUCCESS != status) { + return status; + } + + for (i = 0; i < array->nelts; i++) { + + if (i > 0) { + status = apr_brigade_putc(self->brigade, self->flush, self->ctx, ','); + if (APR_SUCCESS != status) { + return status; + } + } + + status = apr_json_encode_value(self, ((apr_json_value_t **) array->elts)[i]); + if (APR_SUCCESS != status) { + return status; + } + + } + + return apr_brigade_putc(self->brigade, self->flush, self->ctx, ']'); +} + +static apr_status_t apr_json_encode_object(apr_json_serializer_t * self, apr_json_object_t * object) +{ + apr_status_t status; + apr_json_kv_t *kv; + int first = 1; + status = apr_brigade_putc(self->brigade, self->flush, self->ctx, '{'); + if (APR_SUCCESS != status) { + return status; + } + + for (kv = APR_RING_FIRST(&(object)->list); + kv != APR_RING_SENTINEL(&(object)->list, apr_json_kv_t, link); + kv = APR_RING_NEXT((kv), link)) { + + if (!first) { + status = apr_brigade_putc(self->brigade, self->flush, self->ctx, ','); + if (APR_SUCCESS != status) { + return status; + } + } + + { + status = apr_json_encode_value(self, kv->k); + if (APR_SUCCESS != status) { + return status; + } + + status = apr_brigade_putc(self->brigade, self->flush, self->ctx, ':'); + if (APR_SUCCESS != status) { + return status; + } + + status = apr_json_encode_value(self, kv->v); + if (APR_SUCCESS != status) { + return status; + } + } + first = 0; + } + return apr_brigade_putc(self->brigade, self->flush, self->ctx, '}'); +} + +static apr_status_t apr_json_encode_value(apr_json_serializer_t * self, const apr_json_value_t * value) +{ + apr_status_t status = APR_SUCCESS; + + if (value->pre && (self->flags & APR_JSON_FLAGS_WHITESPACE)) { + status = apr_brigade_puts(self->brigade, self->flush, self->ctx, + value->pre); + } + + if (APR_SUCCESS == status) { + switch (value->type) { + case APR_JSON_STRING: + status = apr_json_encode_string(self, &value->value.string); + break; + case APR_JSON_LONG: + status = apr_brigade_printf(self->brigade, self->flush, self->ctx, + "%" APR_INT64_T_FMT, value->value.lnumber); + break; + case APR_JSON_DOUBLE: + status = apr_brigade_printf(self->brigade, self->flush, self->ctx, + "%lf", value->value.dnumber); + break; + case APR_JSON_BOOLEAN: + status = apr_brigade_puts(self->brigade, self->flush, self->ctx, + value->value.boolean ? "true" : "false"); + break; + case APR_JSON_NULL: + status = apr_brigade_puts(self->brigade, self->flush, self->ctx, + "null"); + break; + case APR_JSON_OBJECT: + status = apr_json_encode_object(self, value->value.object); + break; + case APR_JSON_ARRAY: + status = apr_json_encode_array(self, value->value.array); + break; + default: + return APR_EINVAL; + } + } + + if (APR_SUCCESS == status && value->post + && (self->flags & APR_JSON_FLAGS_WHITESPACE)) { + status = apr_brigade_puts(self->brigade, self->flush, self->ctx, + value->post); + } + + return status; +} + +apr_status_t apr_json_encode(apr_bucket_brigade * brigade, apr_brigade_flush flush, + void *ctx, const apr_json_value_t * json, int flags, apr_pool_t * pool) +{ + apr_json_serializer_t serializer = {pool, brigade, flush, ctx, flags}; + return apr_json_encode_value(&serializer, json); +} + +#else + /* we do not yet support JSON on EBCDIC platforms, but will do in future */ +apr_status_t apr_json_encode(apr_bucket_brigade * brigade, apr_brigade_flush flush, + void *ctx, const apr_json_value_t * json, apr_pool_t * pool) +{ + return APR_ENOTIMPL; +} +#endif Modified: apr/apr-util/branches/1.7.x/test/Makefile.in URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.7.x/test/Makefile.in?rev=1835350&r1=1835349&r2=1835350&view=diff ============================================================================== --- apr/apr-util/branches/1.7.x/test/Makefile.in (original) +++ apr/apr-util/branches/1.7.x/test/Makefile.in Sun Jul 8 11:42:12 2018 @@ -17,7 +17,8 @@ STDTEST_PORTABLE = dbd testall TESTS = teststrmatch.lo testuri.lo testuuid.lo testbuckets.lo testpass.lo \ testmd4.lo testmd5.lo testldap.lo testdate.lo testdbm.lo testdbd.lo \ testxml.lo testrmm.lo testreslist.lo testqueue.lo testxlate.lo \ - testmemcache.lo testcrypto.lo testsiphash.lo testredis.lo + testmemcache.lo testcrypto.lo testsiphash.lo testredis.lo \ + testjson.lo PROGRAMS = $(STDTEST_PORTABLE) Modified: apr/apr-util/branches/1.7.x/test/abts_tests.h URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.7.x/test/abts_tests.h?rev=1835350&r1=1835349&r2=1835350&view=diff ============================================================================== --- apr/apr-util/branches/1.7.x/test/abts_tests.h (original) +++ apr/apr-util/branches/1.7.x/test/abts_tests.h Sun Jul 8 11:42:12 2018 @@ -42,7 +42,8 @@ const struct testlist { {testdbm}, {testqueue}, {testreslist}, - {testsiphash} + {testsiphash}, + {testjson} }; #endif /* APR_TEST_INCLUDES */ Added: apr/apr-util/branches/1.7.x/test/testjson.c URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.7.x/test/testjson.c?rev=1835350&view=auto ============================================================================== --- apr/apr-util/branches/1.7.x/test/testjson.c (added) +++ apr/apr-util/branches/1.7.x/test/testjson.c Sun Jul 8 11:42:12 2018 @@ -0,0 +1,139 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "apr_json.h" + +#include "abts.h" +#include "testutil.h" + +static void test_json_identity(abts_case * tc, void *data) +{ + apr_json_value_t *json = NULL; + apr_json_kv_t *image, *width, *ids, *title, + *animated, *thumbnail, *height; + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + const char *src; + char buf[1024]; + apr_size_t len = sizeof(buf); + apr_off_t offset = 0; + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + src = "{" + " \"Image\" : {" + " \"Width\" : 800 ," + " \"IDs\" : [116, 943, 234, 38793]," + " \"Title\" : \"View from 15th Floor\"," + " \"Animated\" : false," + " \"Thumbnail\" : {" + " \"Height\" : 125," + " \"Width\" : 100," + " \"Url\" : \"http://www.example.com/image/481989943\"" + " }," + " \"Height\" : 600 " + " }" + "}"; + + apr_json_decode(&json, src, APR_JSON_VALUE_STRING, &offset, APR_JSON_FLAGS_WHITESPACE, + 10, p); + apr_json_encode(bb, NULL, NULL, json, APR_JSON_FLAGS_WHITESPACE, p); + apr_brigade_flatten(bb, buf, &len); + apr_json_decode(&json, buf, len, &offset, APR_JSON_FLAGS_WHITESPACE, 10, p); + + ABTS_STR_NEQUAL(tc, src, buf, len); + + ABTS_INT_EQUAL(tc, len, offset); + ABTS_INT_EQUAL(tc, APR_JSON_OBJECT, json->type); + image = apr_hash_get(json->value.object->hash, "Image", 5); + ABTS_PTR_NOTNULL(tc, image); + width = apr_hash_get(image->v->value.object->hash, "Width", 5); + ABTS_PTR_NOTNULL(tc, width); + ABTS_INT_EQUAL(tc, APR_JSON_LONG, width->v->type); + ABTS_INT_EQUAL(tc, 800, width->v->value.lnumber); + ids = apr_hash_get(image->v->value.object->hash, "IDs", 3); + ABTS_PTR_NOTNULL(tc, ids); + ABTS_INT_EQUAL(tc, APR_JSON_ARRAY, ids->v->type); + title = apr_hash_get(image->v->value.object->hash, "Title", 5); + ABTS_PTR_NOTNULL(tc, title); + ABTS_INT_EQUAL(tc, APR_JSON_STRING, title->v->type); + animated = apr_hash_get(image->v->value.object->hash, "Animated", 8); + ABTS_PTR_NOTNULL(tc, animated); + ABTS_INT_EQUAL(tc, APR_JSON_BOOLEAN, animated->v->type); + ABTS_TRUE(tc, !animated->v->value.boolean); + thumbnail = apr_hash_get(image->v->value.object->hash, "Thumbnail", 9); + ABTS_PTR_NOTNULL(tc, thumbnail); + ABTS_INT_EQUAL(tc, APR_JSON_OBJECT, thumbnail->v->type); + height = apr_hash_get(image->v->value.object->hash, "Height", 6); + ABTS_PTR_NOTNULL(tc, height); + ABTS_INT_EQUAL(tc, APR_JSON_LONG, height->v->type); + ABTS_INT_EQUAL(tc, 600, height->v->value.lnumber); + +} + +static void test_json_level(abts_case * tc, void *data) +{ + apr_json_value_t *json = NULL; + apr_status_t status; + const char *src; + apr_off_t offset = 0; + + src = "{" + "\"One\":{" + "\"Two\":{" + "\"Three\":{"; + + status = apr_json_decode(&json, src, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 2, p); + + ABTS_INT_EQUAL(tc, APR_EINVAL, status); + +} + +static void test_json_eof(abts_case * tc, void *data) +{ + apr_json_value_t *json = NULL; + apr_status_t status; + const char *src; + apr_off_t offset = 0; + + src = "{" + "\"One\":{" + "\"Two\":{" + "\"Three\":{"; + + status = apr_json_decode(&json, src, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + + ABTS_INT_EQUAL(tc, APR_EOF, status); + +} + +abts_suite *testjson(abts_suite * suite) +{ + suite = ADD_SUITE(suite); + + abts_run_test(suite, test_json_identity, NULL); + abts_run_test(suite, test_json_level, NULL); + abts_run_test(suite, test_json_eof, NULL); + + return suite; +} Modified: apr/apr-util/branches/1.7.x/test/testutil.h URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.7.x/test/testutil.h?rev=1835350&r1=1835349&r2=1835350&view=diff ============================================================================== --- apr/apr-util/branches/1.7.x/test/testutil.h (original) +++ apr/apr-util/branches/1.7.x/test/testutil.h Sun Jul 8 11:42:12 2018 @@ -69,5 +69,6 @@ abts_suite *testxlate(abts_suite *suite) abts_suite *testrmm(abts_suite *suite); abts_suite *testdbm(abts_suite *suite); abts_suite *testsiphash(abts_suite *suite); +abts_suite *testjson(abts_suite *suite); #endif /* APR_TEST_INCLUDES */